2

I currently use the following function to calculate moving average for temperature readings that are happening each 200ms.

uint16_t ntc_average(uint16_t adcdata)
{ 

    static uint8_t      first = 1;
    static uint16_t t1,t2,t3,t4,t5;

    if(first == 1)
    {
        first = 0;
        t1 = t2 = t3 = t4 = t5 = adcdata;
    }

    t5 = t4;
    t4 = t3;
    t3 = t2;
    t2 = t1;
    t1 = adcdata;
    adcdata = (t1+t2+t3+t4+t5)/5;

    return(adcdata);
}

However, 5 points are not enough and I might need much much longer buffer to smooth more. For example once or twice per 10-20 readings the value may drop one point up or down and I need to smooth that. Increasing tn variables seems ugly... I guess that I might need t1-t50.

Can anyone suggest another function in C which I can use for smoothing temperature readings? Note that the values are unsigned integers and not float.

Pablo
  • 28,133
  • 34
  • 125
  • 215
  • 5
    What about a circular buffer? – imreal Dec 21 '12 at 20:59
  • I was hopping for some algorithm which is not using so much buffers. For circular buffer I need to use memory. Since I am working on tiny MCU with tiny memory, it would be great to cut memory usage. – Pablo Dec 21 '12 at 21:04
  • see http://stackoverflow.com/q/3760506/10396 for ideas. – AShelly Dec 21 '12 at 21:22

2 Answers2

4

You can smooth without taking an arithmetic average. For example, rather than dumping the specific sample that drops out of your moving window, you can just drop the average itself on every iteration.

newAverage = (oldAverage * (windowSize - 1) + newSample) / windowSize;

It may or may not be good enough for your system, but it's worth a try.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • Looks like it works. So if I put windowSize to 5, why I don't get the same fluctuations as from my function? – Pablo Dec 21 '12 at 21:39
  • 1
    It's not quite the same function. Yours is a true average. This is an exponential decay filter. Both provide smoothing, but yours weighs each sample equally, while this one puts more weight on more recent samples. – AShelly Dec 21 '12 at 21:46
1

A simple filter is often used for this:

ave = ave * (1-a) + samp * a;

where a is a small constant between 0.0 and 1.0


in s0.15 fixed point:

int32 bigAverage; 
int16 ave;
int16 sample;
int16 alpha = 32768L*(0.1) //your constant here
int16 oneLessAlpha = 32768L-alpha;

//... in loop ...
bigAverage = (bigAverage>>15)*oneLessAlpha;
bigAverage += sample*alpha;
ave = bigAverage>>15;

You do the accumulation in a long to keep better precision.

AShelly
  • 34,686
  • 15
  • 91
  • 152
  • I've seen this, but wasn't sure how to convert it to integer math, since want to avoid floating point math. – Pablo Dec 21 '12 at 21:21
  • @Pablo, I think it's the same as my answer. The `windowSize` in my answer is just `1/a` from this answer. – Carl Norum Dec 21 '12 at 21:24
  • @Carl Norum: sorry, let me try that – Pablo Dec 21 '12 at 21:26
  • I believe this code has an error. An easy way to see it is that if you assume the purpose of bigAverage is to contain extra precision, then it wouldn't make sense to throw away the extra precision (bigAverage >>15) on each iteration. Instead, it would make sense to shift sample left (sample << 15) when adding it to bigAverage, and then shift the whole result right before updating bigAverage. – Dan Sandberg Dec 28 '20 at 14:12