Running average

Vmax

Member
I'm trying to add a running average on a PICAXE 28X1. I have two options:

1) Keep track of the average (A) and number of data points (N), or

2) Keep track of the number of data points and a running total (T).

With either option, I can end up with an overflow error (i.e $FFFF goes to $0000). I need 32 bit math on a PICAXE :confused:.

With option 1), the running average is:

A = (A*N + x)/(N + 1)

where x is a the new data point. For Option 2) the running average is:

A = (T + x)/(N + 1)

With either option, the number of data points becomes:

N = N + 1

I like option 2 because it avoids a multiplication (i.e. A*N).
 

Aresby

New Member
There are some options for 32-bit maths on PICAXE.

http://home.btconnect.com/PicAxe_Projects/PICAXE%2032Bit%20Maths/PICAXE32BITMaths3.pdf

is from this guy's web site

http://home.btconnect.com/PicAxe_Projects/PICAXE 32Bit Maths/PICAXE32BitMaths.htm

Perhaps this will help you.

I have to admit this is one of the reasons why I have "branched out" away from PICAXE so that I have [native] support for 32-bit maths including negative numbers, PEMDAS order of execution and string handling.

Still, I'm still a fully paid-up member of the PICAXE world for the simpler projects in life (which most of mine are).
 

nick12ab

Senior Member
You haven't specified what your biggest numbers will be - with smaller numbers and/or small amount of points it becomes easy.

Have you seen my implementation here?
 

hippy

Ex-Staff (retired)
I've often been able to get away with a very simple rolling average of ...

A = ( A + x ) / 2

Your running total with average calculated from that would be most accurate and easiest to calculate ...

A = (T + x)/(N + 1)

Depending on what the x values are you can perhaps occasionally reduce T and N to keep the T at 16-bit and still have a reasonably good average.
 

KeithRB

Senior Member
Another way to do it for N rolling averages is to calculate Avg = ((N-1) *Avg/(N) + x/N), where N is the number of samples you have if it is less than the number of averages you want or the averages you want. For example, if you want 8 averages, the sequence of N would be 1,2,3,4,5,6,7,8,8,8,8,8,8,8....

ETA: Oops, corrected Error
 
Last edited:

boriz

Senior Member
Or express each new sample as a deviation from the current average, scale it then add it to the current average. Something like this...

Difference = New sample - Average
Average = Average + (Difference / Scale)

Where Scale is a constant which determines how quickly the average approaches a new stable value. Try 5 to start with, see if it works.
 

Vmax

Member
I tried following the math, but got lost at division. What about:

Code:
;
; If the running total is a double word (i.e. 32 bits), then in regular math the
; total should be:
;
; Total = Tmsw * $10000 + Tlsw

Symbol Tmsw=w0     ; Most significant word of Total
Symbol Tlsw=w1       ; Least significant word of Total

; The average is Av = Total / N

Symbol Av=w2          ; is the average
Symbol N=w3            ; is the number of data points

; In regular math, the average can be calculated by
;
; Av = (Tmsw * $10000 + Tlsw)/N = Tmsw*$10000/N + Tlsw/N
;
; But you can't express $10000 in PICAXE math, so take out a factor of 2:
;
; Av = Tmsw*2*$8000/N + Tlsw/N = Tmsw*$8000/(N/2) + Tlsw/N
;
; This is an equation of the form:
;
; Av = Term1 + Term2
;
; For example, say the total is 180000 then

Tmsw = 2
Tlsw = $BF20             ; The total is $2BF20 which is 180000
N = 1800                     ; Number of data points.  The average should be 100.

; Calculate Term1:

w4 = N/2                      ; Divide number of data points by 2.
w5 = $8000/w4           ; This is the $8000/(N/2) part
Av = w5 * Tmsw        ; This is Term1

; Calculate Term2:

w6 = Tlsw / N	; This is Term2
Av = Av + w6	; Add everything together.

; A problem with doing things this way is that there can be a significant
; remainder from both Term1 and Term2.  Accumulate all remainders in w6:

; The remainder from Term1 can be calculated from Tmsw*2*($8000 - w5*w4)

w5 = w5*w4               ; Don't use // to calculate the remainder for Term1 because doing
w6 = $8000 - w5         ; it this way can correct for cases when N is odd.
w6 = w6*Tmsw*2      ; w6 = remainder from Term1

w5 = Tlsw // N            ; w5 = remainder from Term2
w6 = w6 + w5             ; Add up remainders
w6 = w6 / N                ; Divide remainders by number of points

Av = Av + w6             ; Add result from remainders to average.
One thing I got out of this link is that you can identify when an overflow happens:

Tlsw = Tlsw + X ; Update Total
If Tlsw < X then ; overflow
Tmsw = Tmsw + 1
endif
 
Last edited:

g6ejd

Senior Member
2) Keep track of the number of data points and a running total (T).
If you use your option-2 and you believe your average will always be within the scope of a word e.g. 0 - 65535, then isn't that the best solution here. Something like:

Average = 0
NumberofReadings = 0
start:
get_new_reading
Average = Average + New Reading
NumberofReadings = NumberofReadings + 1
Average = Average / 2
goto start

Takes about 8 readings to normalise, and you could also reduce the average (and therefore effective range) by a greater factor to further prevent overflow, perhaps / 4 or 8 and then to get the final average * 4 or 8 when your done, won't make that much difference to an average as resolution in the low order digits is rarely a requirement, e.g. average of 12100 or 12165 is in both cases really 12100 or 12200, so who cares about lower order resolutions :)
 
Last edited:

Colinpc

New Member
I have used the attached to get a running average. This is a cut from my code.

An area of memory is setup as an array to store the values. Store values and loop around to start of array when you get to the end.
Your running average is then simply adding all values and divide by the number of values.

Your code loops around to get the values to store.

Code:
Symbol x = w0					'x is value that is to be averaged
Symbol x.lsb = b0
Symbol x.msb = b1

#rem
symbol str1 = w9				'reserved memory for storage
symbol str2 = w10
symbol str3 = w11
symbol str4 = w12
symbol str5 = w13
#endrem

bptr = 18					'point to start of storage area
>
>
'Get a value for x and save it in storage array as follows

@bptrinc = b0					'save it in the store and increment ptr
@bptrinc = b1

if bptr = 28 then 
	bptr =18				'end of store, set pointer to start
endif

runaverage = w9+w10+w11+w12+w13/5			'add all values and get average

'Get another value for x and store in array
Fred Dagg
 

BeanieBots

Moderator
Type "Kalman Filter" into Google and see if it comes up with anything useful.
It is essentially a running average with no need to store or accumulate the raw data.
 

g6ejd

Senior Member
Unless you need the reading values (unlikely), then I agree this technique is far easier to implement.
 

Paix

Senior Member
I thought that I would take the easy option and selected the Kalman FIlter tutorial on YouTube.

My conclusion is that it was pretty soporific stuff and I missed a goodly part of it as I waited for an aha moment that would eventually simplify things. that moment didn't arrive, but the snooze was very welcome, none-the-less.

I would have probably opted for:
current reading = (previous reading + current reading) / 2

Checking back in the previously unread first page of the thread I see that I am in good company with Hippy's rolling average. That wouldn't send me to sleep either. :)
 

BeanieBots

Moderator
You can cut the Kalman Filter down to absolute basics as follows:-

NewAverage = (Y*OldAverage + NewReading)/N

If you make N=2, then you essentially get What Paix has proposed above.
If you make N larger, you are giving the new reading less "weight" in the equation which boils down to an average based over more samples. You can also play with Y to give more "weight" to the 'rolling average' but this is more often than not left as 1.
 

KeithRB

Senior Member
Something wrong there BeanieBots. If you make N say 4, (and for simplicity make NewReading = oldAverage), you will divide by 2 each iteration!

I always use NewAverage = (N-1)*OldAverage/N + NewReading/N
 

boriz

Senior Member
"You can cut the Kalman Filter down to absolute basics as follows..."

Bloody hell BB. I thought you'd pulled off some magic there. I have read some Kalman filter stuff and got lost every time, quickly. ( http://en.wikipedia.org/wiki/Kalman_filter )

A Picaxe Kalman filter, using 16bit integer maths would be quite a feat.
 

BeanieBots

Moderator
@KethRB, yes, you are correct. I must improve my mental algebra!

The equation I was after is intended to avoid the issue of (NewReading/N) which tends to result is large quantising error for 8-bit values particularly when they are small. It CAN be done by summing the new reading first and then doing the divide later (because I've done it) but I must have missed something. It's probably very close to your equation if you make N large so that you can ignore the (-1) then the N's cancel and you end up with a result that only a little scaling (my Y term).

@Boriz, Most fancy named actions etc can be boiled down to very simple things when they are made specific.
PID algorithms are good examples. My favourite this month came from a customer who described his circuit as "a very complex signal processor which uses a trans-impedance pulse shaper". The circuit was nothing more than a classic high gain inverting op-amp configuration with a slugging cap stuck across the feedback resistor. Don't ever get put off by fancy names. Just have a look at what the general principal is and work it from there.
 

techElder

Well-known member
I agree with the "fancy names" category. My ignorance caused me to invent several "classic" circuits. You see, it wasn't my fault ...
 

premelec

Senior Member
Well my bimorphic stressed cadiddlehopper with variable polarity is working fine - or it was a minute ago... :)
 

campo

New Member
This component allows accurate formula of a operating regular of Term principles. Developed for 28X1 but could be designed.
FEATURES:
1.Does not need to keep a history of all examples.
2.The EXACT regular is organised as a Whole and Rest aspect.
 

Anobium

Senior Member
My average calculation routine from my Damp Checker

Code:
symbol res = b2
symbol totalnumberofsamples = b3
symbol runningtotal = w3
symbol lftcalc = w4
symbol rgtcalc = w5



totalnumberofsamples = 1
s:
	readadc 0, b0
	if totalnumberofsamples = 0 then
		runningtotal = 0
		totalnumberofsamples = 1
	end if
	runningtotal = runningtotal + b0
	lftcalc = runningtotal * 2 + totalnumberofsamples
	rgtcalc = totalnumberofsamples * 2
	res = lftcalc / rgtcalc
	sertxd (#res,13,10)
	inc totalnumberofsamples
goto s
See http://stackoverflow.com/questions/2124283/whats-the-best-way-to-create-a-percentage-value-from-two-integers-in-c for the logic.
 
Top