Here's the code. It's a code 'module' that can run on any picaxe. It includes a short demo of how to use it. Basically it will maintain a precise moving average and you can have up to 65535 samples.
It also includes a conversion routine to express the remainder in 4 decimal places so you can use for display purposes. This little routine could be extracted and used for other display routines if necessary too.
<code><pre><font size=2 face='Courier'>
#picaxe 18x
#rem
#############################################################################
# #
# RUNNING AVERAGE DEMO #
# #
#############################################################################
#endrem
Symbol NewX = w6
Demo:
'Run the simulator, and look at the word values. Put simulator breaks after each
'run of UpdateAverage, to see the values returned.
Gosub InitialiseAverage 'sets it to zero
NewX = 12345
Gosub UpdateAverage 'Giving result Av = 12345 (Whole = 12345, Remainder = 0)
NewX = 20342
Gosub UpdateAverage 'Giving result Av = 16343.5 (Whole = 16343, Remainder = 1)
NewX = 12
Gosub UpdateAverage 'Giving result Av = 10899.6666... (Whole = 10899, Remainder = 2)
NewX = 54322
Gosub UpdateAverage 'Giving result Av = 21755.25 (Whole = 21755, Remainder = 1)
'Express the Remainder of the last result in 4 decimal places.
Gosub FourDP
End
#rem
#############################################################################
# #
# RUNNING AVERAGE MODULE #
# #
# Code Version :1A #
# Date :May 2007 #
# PICAXE Type :18x #
# Editor Software :5.07 #
# #
# Author :Jeremy Leach #
# #
#############################################################################
INTRODUCTION:
-------------
This code enables a precise running average (arithmetic mean) of Word values to be kept.
Features are:
- Does not need to keep all the values read.
- The average is held as a Whole and Remainder part and there is no loss of
accuracy as time progresses.
- The code is relatively fast and compact.
METHOD:
-------
Av0 = The previous average = SumX / N
Where:
SumX is the sum of all the previous readings.
N is the number of samples.
This division can be expressed as a whole and remainder part:
Av0 = Whole0 + (Remainder0/N)
Therefore SumI = (Whole0 * N) + Remainder0 ....Equ1
The new average is:
Av1 = (SumX + X1)/(N+1)
Using Eqn1...
Av1 = [(Whole0 * N) + Remainder0 + X1]/(N+1)
Av1 = [(Whole0 * N)/(N+1)] + [Remainder0 / (N+1)] + [X1/(N+1)]
Av1 = Term1 + Term2 + Term3
The method used evaluates Av1 from this expression.
#endrem
'############################################################################
'# VARIABLES #
'############################################################################
'Word0 (b0 and b1)
Symbol TotalWholeW = w0
Symbol WholeW = w0
'Word1 (b2 and b3)
Symbol Re_mainderW = w1
'Word2 (b4 and b5)
Symbol Word1 = w2
'Word3 (b6 and b7)
Symbol Word2 = w3
'Word4 (b8 and b9)
Symbol Word3 = w4
'Word5 (b10 and b11)
Symbol TempW = w5
Symbol DecimalPlaces = w5
'Word6 (b12 and b13)
'Symbol NewX = w6 '!!!Put at start of code !!!
'############################################################################
'# CONSTANTS #
'############################################################################
'RAM constants
Symbol RAM_AvWholeW = 80 '& 81
Symbol RAM_AvRemainderW = 82 '& 83
Symbol RAM_SamplesW = 84 '& 85
Symbol RAM_TotalWholeW = 86 '& 87
Symbol RAM_TotalRemainderW = 88 '& 89
Symbol RAM_LastTotalWholeW = 90 '& 91
'############################################################################
'# ROUTINES #
'############################################################################
InitialiseAverage:
'Sets RAM_SamplesW, RAM_AvWholeW and RAM_AvRemainderW to zero.
Poke RAM_SamplesW, 0,0
Poke RAM_AvWholeW, 0,0
Poke RAM_AvRemainderW, 0,0
Return
UpdateAverage:
'Updates the average value, based on the NewX word value, the new SamplesW value and the
'previous average value.
'ON ENTRY: NewX is the new sampled Word value.
'ON EXIT : The RAM_AvWholeW and RAM_AvRemainderW contain the new average.
' So does WholeW and Re_mainderW.
'Increment Samples, leaving the updated samples value in Word3.
Peek RAM_SamplesW,Word Word3
Inc Word3
Poke RAM_SamplesW,Word Word3
'Set the Whole and Remainder totals to zero.
Poke RAM_TotalWholeW, 0,0
Poke RAM_TotalRemainderW, 0,0
'Evaluate Term1 = [(Whole0 * N)/(N+1)] and set the Whole and Remainder totals.
Peek RAM_AvWholeW, Word Word1 'Load Whole0 into Word1
Word2 = Word3 - 1 'Word2 = N
Gosub Word1Word2DivWord3
Gosub UpdateTotals
'Evaluate Term2 = [Remainder0 / (N+1)] and update the Whole and Remainder totals.
Peek RAM_AvRemainderW, Word Word1
WholeW = Word1 / Word3
Re_mainderW = Word1 // Word3
Gosub UpdateTotals
'Evaluate Term3 = [X1/(N+1)] and update the Whole and Remainder totals.
WholeW = NewX / Word3
Re_mainderW = NewX // Word3
Gosub UpdateTotals
'Set the Whole and Remainder values for the final average result.
Peek RAM_TotalWholeW, Word WholeW
Peek RAM_TotalRemainderW, Word Re_mainderW
'Save this result
Poke RAM_AvWholeW, Word WholeW
Poke RAM_AvRemainderW, Word Re_mainderW
Return
UpdateTotals:
'Update the RAM_TotalWholeW and RAM_TotalRemainderW values.
Peek RAM_TotalRemainderW, Word TempW
TempW = TempW + Re_mainderW
'Adjust values if new remainder exceeds N+1.
WholeW = TempW/Word3 + WholeW
TempW = TempW // Word3
Poke RAM_TotalRemainderW, Word TempW
Peek RAM_TotalWholeW, Word TempW
TempW = TempW + WholeW
Poke RAM_TotalWholeW, Word TempW
Return
Word1Word2DivWord3:
#rem
Calculate (Word1 * Word2) / Word3.
ON ENTRY: Word1,2 and 3 are loaded appropriately.
ON EXIT: WholeW holds the Whole part of the result.
Re_mainderW holds the remainder of the result.
NOTE: This routine assumes that the result is a Word value.
Code is based on ...
(Word1 * Word2) / Word3
= (Word1/Word3) * Word2
= (Whole + Remainder/Word3) * Word2
= (Whole * Word2) + [(Remainder * Word2)/Word3]
#endrem
'Initialise
TotalWholeW = 0
Poke RAM_LastTotalWholeW, 255,255
'Iterate
Do
TempW = Word1/Word3 'Set TempW to be the whole part of Word1/Word3
TotalWholeW = TempW * Word2 + TotalWholeW
Re_mainderW = Word1//Word3 'Calc the remainder part of Word1/Word3
'Check if the last TotalWhole value is the same as this one.
Peek RAM_LastTotalWholeW, Word TempW
If TempW = TotalWholeW Then
'Adjust the Whole and Remainder values if necessary
WholeW = Re_mainderW * Word2 / Word3 + WholeW
Re_mainderW = Re_mainderW * Word2 // Word3
Exit
Endif
'Save this TotalWhole value as the Last TotalWhole Value.
Poke RAM_LastTotalWholeW, Word TotalWholeW
'Set up a new iteration.
Word1 = Re_mainderW
Loop
Return
FourDP:
'This converts a remainder value into a 4 decimal place value.
'ON ENTRY: Re_mainderW holds the remainder word
' Word3 holds the denominator word
'ON EXIT : DecimalPlaces holds a Word where the last 4 digits are the
' decimal place values. E.g if the decimal places were 0.1234
' then DecimalPlaces would hold 1234.
'NOTE: This uses the calculation DP = Remainder/Denominator * 10000
Word1 = Re_mainderW
Word2 = 10000
Gosub Word1Word2DivWord3
DecimalPlaces = WholeW
Return
</font></pre></code>
Edited by - jeremy leach on 30/04/2007 14:13:56