-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathqssmoother.dox
More file actions
439 lines (439 loc) · 16.1 KB
/
qssmoother.dox
File metadata and controls
439 lines (439 loc) · 16.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
/*! @page qssmoother_desc Filters for signal smoothing
* @tableofcontents
* In many embedded systems applications, signals are captured and may be
* subject to "noise." To reconstruct the original signal, a "smoothing filter"
* is applied to "clear" the signal of this noise. The filter attenuates high
* frequency components and emphasizes slow changes in values, making trends
* easier to discern. The qLibs library offers the \ref qlibs::smoother class, which
* includes a collection of commonly used and efficient smoothing filters for
* easy signal processing.
*
* Below is the list of filters supported by @ref qssmoother :
*
* @section qssmoother_lpf1 First Order Low Pass Filter
*
* \ref qlibs::smootherLPF1 is first-order low-pass filter that passes signals
* with a frequency lower than a chosen cutoff frequency and reduces signals
* with frequencies higher than the cutoff. This results in a smoother signal
* by removing short-term fluctuations and highlighting longer-term trends.
* The discrete-time equation of this filter is defined as follows, where \f$w\f$
* is the cut-off frequency given in \f$rad/seg\f$
*
* The difference-equation of the output of this filter is given by:
*
* <center> \f$y(t)=x(t)+\alpha[y(t-1)+x(t)]\f$ </center>
*
* where,
*
* <center> \f$\alpha=e^{-T_{m}w}\f$ </center>
*
* <center> \f$ 0 < \alpha < 1\f$ </center>
*
*
* @subsection qssmoother_ex1 Example: setting up a 1st order low-pass filter:
* @code{.c}
* smootherLPF1 filter;
* real_t alpha = 0.85f;
* bool ret;
*
* ret = filter.setup( alpha );
* if ( !ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
* @section qssmoother_lpf2 Second Order Low Pass Filter
*
* \ref qlibs::smootherLPF2 extends the first-order filter with steeper attenuation.
* Its stop-band roll-off is twice as sharp (≈40 dB/decade or 12 dB/octave).
*
* The difference equation is:
*
* <center> \f$y(t)=kx(t)+b_{1}x(t-1)+kx(t-2)-a_{1}y(t-1)-a_{2}y(t-2)\f$ </center>
*
* where,
*
* <center>
* \f$ 0 < \alpha < 1\f$
*
* \f$p=\sqrt{2\alpha}\f$, \f$r=1+p+\alpha^{2}\f$, \f$k=\frac{\alpha^{2}}{r}\f$
*
* \f$a_1=\frac{ 2(\alpha^{2}-1)}{r}\f$, \f$a_2=\frac{ 1-p+\alpha^{2} }{r}\f$, \f$b_1=2k\f$
* </center>
*
* @subsection qssmoother_ex2 Example: setting up a 2nd order low-pass filter:
* @code{.c}
* smootherLPF2 filter;
* real_t alpha = 0.85f;
* int ret;
*
* ret = filter.setup( alpha );
* if ( !ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
*
* @section qssmoother_mwm1 Moving Window Median Filter O(n)
*
* \ref qlibs::smootherMWM1 is a low-pass filter that calculates the moving median of the input signal over
* time using the sliding window method. A window of a specified length is moved
* sample by sample and the median of the data in the window is computed.
*
* For each input sample, the output is the median of the current sample and the
* previous \f$N - 1\f$ samples. \f$N\f$ represents the length of the window in
* samples
* To compute the first \f$N - 1\f$ outputs, he algorithm fills the window with
* the first current sample when there is not enough data yet.
*
* The difference-equation of the output of this filter is given by:
*
* <center> \f$ \frac{1}{N}\sum_{i=0}^{N-1}y(t-i)=\frac{1}{N}[ y(t) + y(t-1) + ... + y(t-N-1) ] \f$ </center>
*
* This filter has a time complexity of \f$O(n)\f$
*
* @subsection qssmoother_ex3 Example: setting up a moving window median filter:
* @code{.c}
* smootherMWM1 filter;
* real_t m_window[ 20 ] = { 0.0f };
* bool ret;
*
* ret = filter.setup( m_window );
* if ( !ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
*
* @section qssmoother_mwm2 Moving Window Median Filter O(1)
*
* \ref qlibs::smootherMWM2 is a filter that operates on the same principle as the previous filter. The main
* difference is the treatment of the sliding window, which is implemented as a
* TDL (Tapped Delay Line) using a circular queue.
* This structure has \f$N - 1\f$ taps in a line of delays, each tap extracts
* the signal at a fixed integer delay relative to the input.
*
* The circular queue approach prevents the shift of all the \f$N - 1\f$ samples
* stored in the sliding window.
*
* @remark
* This implementation offers a significant performance benefit as it has a
* constant time complexity of \f$O(1)\f$ , compared to the previous filter
* where time complexity grows proportionally to the size of the window. Use
* this filter when dealing with large sliding windows.
*
* @subsection qssmoother_ex4 Example: Setting up a moving window median filter:
* @code{.c}
* smootherMWM2 filter;
* real_t m_window[ 150 ] = { 0.0f };
* bool ret;
*
* ret = filter.setup( m_window );
* if ( !ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
*
* @section qssmoother_mor1 Moving Outlier Removal Filter O(n)
*
* The \ref qlibs::smootherMOR1 filter detects and removes outliers in the input signal by computing the
* median of a moving window for each sample. If a new incoming sample deviates
* from the median by a specified margin \f$\alpha\f$, it is replaced by the
* moving median \f$\overline{m(k)} \f$.
*
* This filter uses the following criteria for outlier removal:
*
* <center>
* \f$ y(k) = \overline{m(k)} \rightarrow \text{if} \rightarrow | \overline{m(k)} - x(k) | > \alpha|\overline{m(k)}| \f$
*
* \f$ \text{else}=x(k) \f$
* </center>
*
* This filter has a time complexity of \f$O(n)\f$
*
* @subsection qssmoother_ex5 Example: setting up an outlier removal filter:
* @code{.c}
* smootherMOR1 smoother;
* real_t alpha = 0.8f;
* real_t m_window[ 10 ] = { 0.0f };
* bool ret;
*
* ret = filter.setup( m_window, alpha );
* if ( !ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
*
* @section qssmoother_mor2 Moving Outlier Removal Filter O(1)
*
* The \ref qlibs::smootherMOR2 is similar to the previous filter, but with a constant time complexity of \f$O(1)\f$
* by using a \ref qlibs::tdl data structure.
*
* @subsection qssmoother_ex6 Example: setting up an outlier removal filter:
* @code{.c}
* smootherMOR2 smoother;
* real_t alpha = 0.8f;
* real_t m_window[ 100 ] = { 0.0f };
* bool ret;
*
* ret = filter.setup( m_window, alpha );
* if ( !ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
*
* @section qssmoother_gmwf Gaussian Filter
*
* \ref qlibs::smootherGMWF is a filter that uses a kernel for smoothing, which defines the shape of the function
* that is used to take the average of the neighboring points. A Gaussian kernel
* has the shape of a Gaussian curve with a normal distribution.
*
* The coefficients of the kernel are computed from the following equation:
*
* <center> \f$ w(n)=e^{\frac{1}{2}( \alpha\frac{ n }{ (L-1)/2} )^2} = e^{-n^{2}/2\sigma^2 }\f$ </center>
*
* were \f$ -(L-1)/2 \leq n \leq (L-1)/2 \f$ and \f$\alpha\f$ is inversely proportional
* to the standard deviation, \f$\sigma\f$, of a Gaussian random variable. The exact
* correspondence with the standard deviation of a Gaussian probability density
* function is \f$ \sigma = (L – 1)/(2\alpha)\f$ .
*
* This filter has a time complexity of \f$O(n)\f$
*
* @subsection qssmoother_ex7 Example: setting up a gaussian filter:
* @code{.c}
* constexpr size_t SMOOTHER_WINDOW_SIZE = 10;
* const real_t sigma = 0.5f;
* const real_t centerOffset = SMOOTHER_WINDOW_SIZE/2.0f;
*
* smootherGMWF filter;
* real_t window[ SMOOTHER_WINDOW_SIZE ] = { 0.0f };
* real_t kernel[ SMOOTHER_WINDOW_SIZE ] = { 0.0f };
* bool ret;
*
* ret = filter.setup( sigma, centerOffset, window, kernel );
* if ( 0 == ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
*
* @section qssmoother_expw Exponential Weighting filter
*
* The \ref qlibs::smootherEXPW filter uses a weighting factor that is computed and applied to the input
* signal in a recursive manner. The weighting factor decreases exponentially as
* the age of the data increases, but never reaches zero, meaning more recent
* data has more influence on the current sample than older data
*
* The value of the forgetting factor \f$\lambda\f$ determines the rate of change
* of the weighting factor. A smaller value (below 0.5) gives more weight to
* recent data, while a value of 1.0 indicates infinite memory. The optimal
* value for the forgetting factor depends on the data stream.
*
* The moving average algorithm updates the weight and computes the moving
* average recursively for each data sample that comes in by using the following
* recursive equations:
*
* <center> \f$ w(t) = \lambda w(t-1) + 1 \f$ </center>
*
* <center> \f$ y(t) = ( 1-\frac{1}{w(t)} )y(t-1) + \frac{1}{w(t)}x(t)\f$ </center>
*
* @subsection qssmoother_ex8 Example: setting up an exponential weighting filter:
* @code{.c}
* smootherEXPW filter;
* real_t lambda = 0.8f;
* bool ret;
*
* ret = filter.setup( lambda );
* if ( 0 == ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
*
* @section qssmoother_klmn Scalar Kalman filter
*
* The \ref qlibs::smootherKLMN (Kalman Filter) is an efficient optimal estimator that provides a recursive
* computational methodology for getting the state of a signal from measurements
* that are typically noisy, while providing an estimate of the uncertainty of
* the estimate. Here, the scalar or one-dimensional version is provided
* and only three design parameters are required, the initial covariance \f$P(0)\f$,
* the signal noise covariance \f$Q\f$ and the measurement uncertainty \f$r\f$.
*
* The recursive equations used by this scalar version are:
*
* predict
*
* <center> \f$ x(k) = Ax(k-1)\f$ </center>
*
* <center> \f$ P(k) = A^{2}P(k-1) + Q\f$ </center>
*
* compute the Kalman gain
*
* <center> \f$ K(k) = \frac{P(k)H}{r + H P(k) H} \f$ </center>
*
* update the state and the state uncertainty
*
* <center> \f$ x(k) = x(k-1) + K(k)( u(k) - H x(k) ) \f$ </center>
*
* <center> \f$ P(k) = ( 1 - K(k)H )P(k-1) \f$ </center>
*
* Where
*
* \f$A\f$ is the state transition model, \f$H\f$ the observation model, \f$x(k)\f$
* the filter output (state estimation), \f$P(k)\f$ the covariance of the predicted
* state, \f$u(k)\f$ the signal measurement and \f$K(k)\f$ the kalman gain.
*
* @subsection qssmoother_ex9 Example: setting up a scalar Kalman filter:
* @code{.c}
* smootherKLMN filter;
* bool ret;
*
* ret = filter.setup( 100.0f, 0.9f, 100.0f );
* if ( !ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
*
* @section qssmoother_desf Double Exponential Smoothing
*
* The \ref qlibs::smootherDESF is a time series forecasting filter used to analyze and predict trends in
* data. It is an extension of simple exponential smoothing that incorporates
* trend information into the forecast.
*
* The double exponential smoothing filter calculates two smoothing coefficients,
* one for the level of the series and one for the trend. These coefficients are
* used to give more weight to recent observations and dampen the effect of
* older observations. The level and trend are then updated using the following
* equations:
*
* Level:
* <center> \f$ L(t) = \alpha x(t) + ( 1 - \alpha ) [ L(t-1) + T(t-1) ] \f$ </center>
*
* Trend:
* <center> \f$ T(t) = \beta [ L(t-1) + T(t-1) ] + ( 1 - \beta ) T(t-1) \f$ </center>
*
* where \f$\alpha\f$ and \f$\beta\f$ are the smoothing coefficients for the
* level and trend, respectively.
*
* The double exponential smoothing filter can be used to generate forecasts by
* extrapolating the level and trend components into the future. The forecast for
* time \f$t+k\f$ is given by:
*
* <center> \f$ F(t+k) = L(t) + kT(t) \f$ </center>
*
* Where \f$k\f$ is the number of time periods into the future.
*
* Overall, double exponential smoothing is a simple but effective technique
* for forecasting time series data with trends. It can be easily implemented 3
* and provides accurate forecasts for short-term predictions. However, it may
* not perform well for longer-term forecasts or for data with complex seasonal
* patterns.
*
* @subsection qssmoother_ex10 Example: setting up the Double Exponential Smoothing:
* @code{.c}
* smootherDESF filter;
* bool ret;
*
* ret = filter.setup( 0.08f, 0.085f, 10.0f );
* if ( !ret ) {
* // error, smooth filter cant be configured
* }
* @endcode
*
* @section qssmoother_alnf Adaptive Linear Filter
*
* The \ref qlibs::smootherALNF is a digital filter that adjusts its parameters based on the
* characteristics of the input signal and the desired output signal.
*
* The adaptive linear filter works by using an algorithm to iteratively adjust
* the filter coefficients in response to changes in the input signal \f$x(t)\f$.
* The algorithm seeks to minimize the difference between the filter output and
* the desired output, known as the error signal \f$ e(t)\f$. This is done by
* adjusting the filter coefficients \f$w_i(t)\f$ in the direction that reduces
* the error signal. For this, the Least Mean Squares (LMS) algorithm is being
* used. The LMS algorithm updates the filter coefficients by multiplying the
* error signal by the input signal and a small step size parameter, and then
* adding this product to the existing filter coefficients.
*
* It is particularly useful in situations where the characteristics of the
* input signal are unknown or change over time, as it can adapt to these changes
* and continue to provide accurate filtering or modeling.
*
* One potential drawback of the adaptive linear filter is that it can be
* computationally intensive, especially if the input signal is large or the
* filter has many coefficients. Additionally, the performance of the filter
* can be sensitive to the choice of step size parameter, which must be
* carefully chosen to balance convergence speed with stability.
*
* The adaptation rule for every filter coefficient is given by
* <center> \f$ \Delta w(t) = \alpha e(t) x(t) + \mu \Delta w(t-1) \f$ </center>
*
* <center> \f$ w(t) = w(t-1) x(t) + \Delta w(t) \f$ </center>
*
* The equation also includes a momentum term that adds a fraction of the
* previous coefficient update to the current coefficient update. This can help
* to reduce oscillations in the filter output and improve convergence speed.
*
* @section qssmoother_ex11 Example of signal smoothing
*
* The following is an example of how a smoothing filter can be implemented. In
* this scenario, a Gaussian filter is used to smooth a speed sensor signal,
* reducing noise and improving overall signal quality. The filter is set up by
* defining the necessary parameters and instantiating the filter. The filter is
* then executed at a sampling rate of 100 milliseconds. This process involves
* sampling the signal and running it through the filter, resulting in a cleaned
* and enhanced signal that can be further processed without the need to account
* for noise and disturbances.
*
* @code{.c}
* #include <iostream>
* #include "freertos/FreeRTOS.h"
* #include "freertos/task.h"
* #include "bsp.h"
* #include "AppSignalProcessing.h"
* #include <qlibs.h>
*
* using namespace qlibs;
*
* constexpr size_t SMOOTHER_WINDOW_SIZE = 10;
* constexpr uint32_t SMOOTHER_SAMPLE_TIME = 100;
* constexpr real_t GAUSS_SIGMA = 0.5f;
* constexpr real_t GAUSS_CENTER = SMOOTHER_WINDOW_SIZE/2.0f;
*
* void xTaskSignalProcessing( void *arg )
* {
* smootherGMWF *filter = static_cast<smootherGMWF *>( arg );
* real_t noisySignal;
* real_t smoothedSignal;
* for ( ;; ) {
* noisySignal = BSP_ScaletoSomething( BSP_AnalogRead( BSP_AI_SPEED_CHANNEL ) );
* smoothedSignal = filter->smooth( noisySignal );
* AppSignalProcess( smoothedSignal );
* vTaskDelay( SMOOTHER_SAMPLE_TIME / portTICK_RATE_MS ) ;
* }
* }
*
* int main( int argc, char *argv[] )
* {
* smootherGMWF filter;
* real_t window[ SMOOTHER_WINDOW_SIZE ] = { 0.0f };
* real_t kernel[ SMOOTHER_WINDOW_SIZE ] = { 0.0f };
* bool ret;
*
* BSP_SystemInit( );
* ret = filter.setup( GAUSS_SIGMA, GAUSS_CENTER, window, kernel );
* if ( !ret ) {
* puts( "ERROR: Cant configure the smoother filter" );
* }
* xTaskCreate( xTaskSignalProcessing, "signalProcessing", 512, &filter, 2, nullptr );
* vTaskStartScheduler();
* for( ;; );
* return EXIT_SUCCESS;
* }
* @endcode
*
*/