Averaging
| Averaging 16 bit values without using 32 bit
math? |
The typical way to average numbers is to add them all together and then
divide by the number of samples.
However, adding several 16 bit numbers isn't easy to do in PBP because the
largest variable size is 16 bits (Word).
While 32 bit or larger math is possible with PBP, sometimes it's just not
worth writing the extra code.
In this case the solution is simple... Do the division first.
This simple idea yields several benefits:
- It eliminates the 32 bit math.
- It allows you to keep a running average. After each sample, the new
average is available.
Otherwise you may have to wait for many samples
to be taken before getting a new average value.
- Response times can be improved for rapid changes in the data.
The calculation goes as follows:
- Simply choose the number of samples that you want to average and assign
it to AvgCount
-
Divide to current average (ADavg) by AvgCount and subtract that number
from ADavg
-
Divide the new Value by AvgCount and add that number to the
average.
So if your AvgCount is 16 then it Reduces the average by 1/16 then
adds 1/16th of the new number.
One effect of averaging can be a long delay before it "Catches up"
to fast changing data. For instance, when you turn on power, it may be
several seconds before the average shows the correct value of the initial data.
It seems to slowly count up until finally reaching the target.
By assigning a value to FAspread you can allow it to "Catch
Up" if the difference between the average and the new Value is greater that
the specified amount. This will improve the appearance of the displayed numbers
considerably.
( for 10 bit A/D a FAspread of 200 may be suitable, but it really
depends on your application)
One drawback of "Dividing first" is that when the difference
between the average and the new Value is less than the number of samples (AvgCount),
the numbers being added (Value/AvgCount) are too small to
make a difference due to the truncating of integer math. To overcome this,
the RealClose section was added to reduce the number of samples being averaged when
the two numbers a very close to each other. If your averaging less than 8
samples, change it to (AvgCount/2) instead of (AvgCount/4)
The Lean version. (Keeps a running average of only 1
value)
' This routine will keep a "Running Average"
' Instead of adding a bunch of values together and then dividing by the number of samples,
' it averages each sample into the final result immediately.
' This eliminates the need for 32 bit math.
' To allow it to "Catch up" to large changes, set the FAspread to an acceptable range.
' Simply place the new number in VALUE and GoSub AVERAGE
' The Average will be returned into the same variable VALUE
AvgCount CON 16 ' = Number of samples to average
FAspread CON 1000 ' = Fast Average threshold +/-
ADavg VAR WORD
Value VAR WORD
' -=-=-=-=-=-= Average Analog values -=-=-=-=-=-=-=-=-=-=
Average:
IF Value = ADavg Then NoChange
IF ABS (Value - ADavg) > FAspread OR Value < AvgCount Then FastAvg
IF ABS (Value - ADavg) < AvgCount Then RealClose
ADavg = ADavg - (ADavg/AvgCount)
ADavg = ADavg + (Value/AvgCount)
GoTo AVGok
FastAvg:
ADavg = Value
GoTo AVGok
RealClose:
ADavg = ADavg - (ADavg/(AvgCount/4))
ADavg = ADavg + (Value/(AvgCount/4))
AVGok:
Value = ADavg ' Put Average back into Value
NoChange:
Return
|
Of course, just about any "useful" program will have to deal with
more than 1 averaged value, so consider the following example which allows you
to average as many values as you may need.
' This routine will keep a "Running Average" of several inputs
' Instead of adding a bunch of values together and then dividing by the
' number of samples, it averages each sample into the final
' result immediately.
' This illiminates the need for 32 bit math.
' To allow it to "Catch up" to large changes, set the FAspread to an
' acceptable range.
' First, Select the sensor. ie. Sensor = Temperature
' then place the new number in VALUE and GoSub AVERAGE
' The Average will be returned into the same variable VALUE
AvgCount CON 16 ' = Number of samples to average
FAspread CON 1000 ' = Fast Average threshold +/-
ADavg VAR WORD[3] ' Change this array size to match the number of inputs
Value VAR WORD
Sensor VAR BYTE
Temperature CON 0 ' Change these to represent your inputs
Pressure CON 1
Flow CON 2
' -=-=-=-=-=-= Average Analog values -=-=-=-=-=-=-=-=-=-=
Average:
IF Value = ADavg[Sensor] Then NoChange
IF ABS (Value - ADavg[Sensor]) > FAspread OR Value < AvgCount Then FastAvg
IF ABS (Value - ADavg[Sensor]) < AvgCount Then RealClose
ADavg[Sensor] = ADavg[Sensor] - (ADavg[Sensor]/AvgCount)
ADavg[Sensor] = ADavg[Sensor] + (Value/AvgCount)
GoTo AVGok
FastAvg:
ADavg[Sensor] = Value
GoTo AVGok
RealClose:
ADavg[Sensor] = ADavg[Sensor] - (ADavg[Sensor]/(AvgCount/4))
ADavg[Sensor] = ADavg[Sensor] + (Value/(AvgCount/4))
AVGok:
Value = ADavg[Sensor] ' Put Average back into Value
NoChange:
Return
|
I hope this Helps your program, feel free to use it in any way you wish.
|