Smooth a value in one instruction [code snippet]

Here is the way to smooth a value in one line:

A0smooth = A0smooth + (A0 - A0smooth) / 2;

smoothes A0 to A0smooth.
You should run that with A0 and A0smooth being floats.
If you want a stronger smoothing replace /2 by /3 or more.
The higher the value the stronger the smoothing.

This code does a smoothing in the same way than a RC filter in electronics.

That's one line of code, not one instruction (by my count, it's like 10-12 instructions - load, sub, rightshift, add, store - times 2 because on an 8-bit avr, and 16-bit operands...)

As an aside, dividing by 3 is much worse, while dividing by 4 (or other powers of 2) is not - division on AVRs is implemented in software and slow.

AKA a recursive lowpass filter. In general

weight = .95;
value = value * weight + newValue * (1-weight);

See http://www.dspguide.com/ for a free webbook on this topic.

Here is the way to smooth a value in one line:

A0smooth = A0smooth + (A0 - A0smooth) / 2;

smoothes A0 to A0smooth.

... but remember that on a UNO, A0 always has the value 14, so doesn't really need smoothing. :smiley:

PaulMurrayCbr:

weight = .95;

value = value * weight + newValue * (1-weight);

That will not do: You must add the weighted increment*, not the newValue

RIN67630:
That will not do: You must add the weighted increment*, not the newValue

The weighted increment is (newValue-value)*(1-weight), and - looking at it - I agree that doing it that way is better, as it involves one fewer floating-point multiplications. BUt mathematically it comes out the same.

Of course, subtracting the weight from 1 is just a result of what you decide that "weight" means.

I'm hesitant to contribute in such distinguished company; however I'd suggest it would be better to use integer arithmentic.

Suppose you get a 12 bit integer value "newValue" from the ADC;

using a weight of .75 ..

to smooth it you take int smoothValue = (smoothValue*3 + newValue) /4

as smoothValue = (smoothValue + smoothValue<1 + newValue) >>2

johnerrington:
I'm hesitant to contribute in such distinguished company; however I'd suggest it would be better to use integer arithmentic.

Suppose you get a 12 bit integer value "newValue" from the ADC;

using a weight of .75 ..

to smooth it you take int smoothValue = (smoothValue*3 + newValue) /4

as smoothValue = (smoothValue + smoothValue<1 + newValue) >>2

better still, "smoothValue<<1"

Or just leave it as "(smoothValue*3 + newValue) /4", and let the compiler figure it out.

smoothValue<<1 loses precision.

Or just leave it as "(smoothValue*3 + newValue) /4", and let the compiler figure it out.

my suggestion is based on several factors:

firstly that CHOOSING to use integer arithmetic forces the use of a sensible weighting - i.e. not something like 0.93;

and secondly that it helps to see where precision could be lost.

also I get very fustrated when reading that someone has read the value of a voltage with the adc as 3.4326171875 volts, just because the reading was 703 on a 5V=1024 scale.

Maintaining precision in a digital filter is important.

johnerrington:
as smoothValue = (smoothValue + smoothValue<1 + newValue) >>2

Loses even more precision :wink:

Yes; the precision is of course lost in the final divide (or shift)

the integer values need to be manipulated at hgher precision for the results to be sensible.

My old brain was struggling so I used excel.

This filter is Vn+1 = (3Vn + 256Sn) >>2

the "voltage" is calculated at the END by (integer) dividing by 256.

You can see that by using higher order bits in the integer value precision is maintained in the filter calculation.

A single bit is lost in the conversion to "voltage" - as it would be in converting from a float.

I've attached the spreadsheet as a zip file. Change the yellow value to change the "seed" sample value. Its filtering a step function.

The graph shows the filter response: Its important to realise that at any instant the filtered value is unlikely to match the current value of your reading.

fresponse.gif

filter.zip (17.8 KB)

It wasn't a shift I was highlighting, it was a Boolean expression

My bad - a typo - unacceptable in a program! However it triggered an interesting excursion. Thanks.
I've updated the spreadsheet and graph above.