HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

stability of an exponential moving average

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
stabilitymovingaverageexponential

Problem

I used the following code:

#include
class UltrasonicRecorder
{
public:
    UltrasonicRecorder(int referenceValue, Input referenceInput) :
        _referenceValue(referenceValue),
        _referenceInput(referenceInput)
    {}

    std::vector GetData();

    void RecordData(Input);
    int CompensateTemperatur(int);

private:
    double _compensation;
    int _referenceValue;
    Input _referenceInput;
    std::vector _data;
};

std::vector UltrasonicRecorder::GetData()
{
    std::vector ret;
    std::swap(ret, _data);

    return ret;
}

void UltrasonicRecorder::RecordData(Input input)
{
    int value = (ANALOG_VALUE_RANGE - 1) - GetAnalogValue(input);
    value = CompensateTemperature(value);
    _data.push_back(value);
}

int UltrasonicRecorder::CompensateTemperature(int value)
{
    int measuredValue = GetAnalogValue(_referenceInput);
    if(measuredValue > 0)
    {
        static const double delta = 1.0e-6;
        _compensation = (1.0 - delta) * _compensation + 
            delta * _referenceValue / (double)measuredValue;
    }
    return static_cast(value * _compensation);
}


int GetAnalogValue(Input) returns a 12 bit value from a ultrasonic sensor measuring distance (0-4095, ANALOG_VALUE_RANGE = 4096). double _compensation; is a member of the encapsulating class. _referenceValue is the initial reference value from _referenceInput (normally it has a low fluctuation, with a value somewhere in the middle of the 12 bit range, but it changes with temperature).

RecordData() is called every 2 milliseconds (more or less, running on Windows). So a high update rate for the reference value, but I wanted a gradual compensation over time. Therefore I used an exponential moving average, the delta is empirically determined and may need to be changed later.

GetData() is called approximately every second to flush _data and use the record for further evaluation.

We started testing this code on customer facilities, but got the feedback that val

Solution

How about initializing the compensation factor so that it doesn't have to do the convergence from an undefined state?

UltrasonicRecorder(int referenceValue, Input referenceInput) :
    _referenceValue(referenceValue),
    _referenceInput(referenceInput)
{
    _compensation = 1.0; // This!
}


Edit: Your EWMA is correct. Your woes of compounding errors are unfounded. As you always "move towards" the measurement value, any compounding errors will be limited. In fact the compound error will at any point in time be less than:

(sum{i=0,infty} (1-delta)^i)*ULP(ANALOG_VALUE_RANGE) ~ 2^-(52-12) / delta ~ 10^-6

Code Snippets

UltrasonicRecorder(int referenceValue, Input referenceInput) :
    _referenceValue(referenceValue),
    _referenceInput(referenceInput)
{
    _compensation = 1.0; // This!
}
(sum{i=0,infty} (1-delta)^i)*ULP(ANALOG_VALUE_RANGE) ~ 2^-(52-12) / delta ~ 10^-6

Context

StackExchange Code Review Q#39694, answer score: 3

Revisions (0)

No revisions yet.