Engee documentation
Notebook

Signal smoothing by sliding window

Let's consider 4 ways of smoothing a signal with a sliding window, from low-level to the highest level.

Different ways of signal smoothing

In different applications, an engineer may need a wide variety of signal filtering methods, and we will break down four methods (that work identically) and discuss their advantages and disadvantages.

image.png

Delay chain (explicit FIR)

Firstly, the lowest level method of implementing a sliding window would be to simply memorise a certain number of signal points, say 100, as they arrive. The result would be the sum of all these "delayed measurements" divided by their number (100).

image.png

Inside the filter there are 10 identical blocks whose outputs are summed and divided by 100.

image.png

Inside each such block are 10 delay blocks Unit Delay, which memorise the next value before passing to the next block the value they received a step earlier.

Why did we create such a hierarchy of blocks? By arranging the delay blocks in this way we avoided the need to create a line of 100 delay blocks.

This method is not very flexible because we still have to create delay blocks manually. It would be difficult to parameterise such a filter with a mask.

Using an off-the-shelf FIR filter block

It is much easier to place the block on the diagram Discrete FIR filter

image.png

We will specify the same coefficient weights to the block so that each measurement has the same contribution to the final signal.

Smoothing through CIC filter

A CIC filter with equal weights can be created using the much simpler and more elegant design shown below.

image.png

Here, with a delay length of 100 and a few simple blocks, we have created a cascaded integral comb filter of BICH type (with infinite impulse response). There are no multiplication operations here at all, so the filter is very fast.

Block "Sliding averaging"

Another block from the standard library, called Moving Average, if the "sliding window" method is specified in its settings, will implement the same operation as the previous ones.

The block needs to receive a vector of values as input, so we feed a buffered signal into the input port (a block Buffer of length 100 with overlap 99). At the output it will return us a single value packed into a matrix of size 1х1. We convert it into a scalar using the block Demux.

Running the model

Let's run the model for 2500 samples. The input signal is generated by the following subsystem:

image.png

This is the result of summing the constant 1 with a vector of uniformly distributed noise with amplitude 0.01. After 500 samples of the model, a step with amplitude 1 is added to the signal.

In [ ]:
# Если модель еще не открыта, загрузим из файла
if "moving_average"  getfield.(engee.get_all_models(), :name)
    engee.load( "$(@__DIR__)/moving_average.engee");
end;

data = engee.run( "moving_average" )
Out[0]:
SimulationResult(
    "Сглаженный блоком FIR" => WorkspaceArray("moving_average/Сглаженный блоком FIR"),
    "Исходный" => WorkspaceArray("moving_average/Исходный"),
    "Сглаженный через блок MA" => WorkspaceArray("moving_average/Сглаженный через блок MA"),
    "Сглаженный явным FIR" => WorkspaceArray("moving_average/Сглаженный явным FIR"),
    "Сглаженный CIC фильтром" => WorkspaceArray("moving_average/Сглаженный CIC фильтром")
)

Let's compare the results with each other. They should be identical, but the latter method will introduce a delay of one sample because of the way the signal buffering is implemented.

In [ ]:
plot( data["Сглаженный явным FIR"].time, data["Сглаженный явным FIR"].value, label="Сглаженный явным FIR" )
plot!( data["Сглаженный блоком FIR"].time, data["Сглаженный блоком FIR"].value, label="Сглаженный блоком FIR" )
plot!( data["Сглаженный CIC фильтром"].time, data["Сглаженный CIC фильтром"].value, label="Сглаженный CIC фильтром" )
plot!( data["Сглаженный через блок MA"].time, data["Сглаженный через блок MA"].value, label="Сглаженный через блок MA" )
plot!( legend=:bottomright )
Out[0]:

Conclusion

If you often have to filter signals in your work, you will find it useful to work with the models presented in this example.

The choice of filter depends on whether you need to assign weights (for example, to implement an exponential filter), as well as on what kind of signal is input (scalars or vectors) and whether you need to translate the model into code or not.

Blocks used in example