Moving average algorithm for low memory embedded systems

Viewed 55

I am currently working with moving average and use an algorithm to calculate it quickly on the fly:

public void AddPoint(double input)
{
    Value += (input - Value) / (dataLength * divergenceCorrection);
}

I noticed that the average can lag behind quite a bit as opposed to calculating it truely for each value.

Example:

double[] values = new double[] { 10, 10, 10, 10, 10, 2, 2, 2, 2, 2 };
double value = 0;
foreach(double num in values)
{
    value += (num - value) / (5 * 1);
}

-> result: 3.54.. as opposed to 2
this becomes more prevalent if the array was
{ 100000, 100000, 100000, 100000, 100000, 2, 2, 2, 2, 2 }
-> 22031, which is waaaay off the chart vs (2+2+2+2+2)/5

For this reason, I started with experimenting with divergenceCorrection in an attempt to bring down the decay closer to the targeted length:

double[] values = new double[] { 100000, 100000, 100000, 100000, 100000, 2, 2, 2, 2, 2 };
double value = 0;
foreach(double num in values)
{
    value += (num - value) / (5 * 0.25);
}

-> 33

I believe this might have different implications on the outcome so I wonder what the apropriate way to do this.

A solution could be like this but I will use this function on embedded systems as well. The low memory would restrict me a lot for either data legth or resolution (when pooling values)

double[] values = new double[] { 100000, 100000, 100000, 100000, 2, 2, 2, 2, 2 };
Queue<double> queue = new Queue<double>();
double value = 100000;
// prime queue with first value
for(int i = 0; i < 5; i++)
{
    queue.Enqueue(value / 5);
}
// formula
double tempVal = double.NaN;
foreach(double num in values)
{
    tempVal = num/5;
    value += tempVal;
    queue.Enqueue(tempVal);
    value -= queue.Dequeue();
}

Note: the input is live fed sensor data, which is why I have to store the values in memory.

2 Answers

To calculate the moving average you don't need to store all of the values in memory. You just need to store a moving sum value and add a new value an remove an old value as you need to calculate the next result in the sequence. Additionally division is a more expensive operation so you should do that less.

A simple moving average calculation might look like this:

    double[] values = new double[] { 10, 10, 10, 10, 10, 2, 2, 2, 2, 2 };
    double value = 0;
    int movingCount = 5;
    
    for(int i = 0; i < values.Length; i++) 
    {
        value += values[i];
        if(i > movingCount - 1) {
            value -= values[i - movingCount];
        }
        
        int demoninator = movingCount;
        if(i < movingCount - 1) {
            demoninator = i + 1;
        }
        
        Console.WriteLine($"Moving average: {value / demoninator}");
    }   
Related