Storing multiple values and then take average

Here's my question. Is there a function or such to take average of 10 values, where you discard possible error values (= value is n% higher or lower than the average) and then take the average of the values left?

I know you could use variables and possibly a database, but are there any simpler ways to do this?

Until you have the average you won't know which readings to discard and then it's too late since you already used them to calculate the average which tells you to discard them.....

I use 100 for the number of readings on an LM35- even 1000 doesn't hold things up too long, and that minimises the effect of a few funnies like if there's an AC spike nearby or the dog farts, or whatever.

You don't to store all the values read individually. Take the first reading and store it. Add all subsequent readings to the stored value. Finally, divide the stored value by the number of times the reading was taken. - Scotty

scottyjr:
You don't to store all the values read individually. Take the first reading and store it. Add all subsequent readings to the stored value. Finally, divide the stored value by the number of times the reading was taken. - Scotty

Yep that's what I do here:

// read and calc the temp
          temptotal=0;
          for(int x = 0; x < 100; x++) {
            rawVal=analogRead(5);    //Connect LM35 on Analog 5
            temp=(500 * rawVal) /1024;
            temptotal = temptotal + temp;
          }
          temp=temptotal/100;

I came here to look for an answer to a question I had about potentiometers giving values that vary too much. I've found that the answer is to smooth the data by taking an average of the last 10 samples or so. I only post here because I think that for continuous input data, the methods here are a bit simple, and don't quite achieve an efficient calculation of a moving average. The following code hasn't been tested on an arduino (or anywhere), and hence may have errors. But, I think it gives an idea on how (e.g.) an average can be taken from the last 10 input values without having to add up all the numbers each time.

BTW: I used round() to round the average, rather than truncate. I haven't looked in the arduino header files to check that I need to cast the result of round() or not.

int values[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Array with 10 zeroes. 10 pseduo-samples before the first one read.
int total = 0; // The total of the last 10 samples read
int next = 0;  // The index of the oldest sample in the array

/*
 * Function that accepts a new sample, and returns the average of the last 10 samples.
 */

int addValue( int newValue )
{
  total = total - values[next] + newValue; // Update totla
  values[next] = newValue; // Update array
  next = ( next + 1 ) % 10; // move to new 'oldest sample in the array'

  return (int) round( total / 10.0 ); // return average
}

Oh dear, now that I reread the OP, that doesn't solve the problem as it doesn't throw away values too far from the average. The following code might do, but it hasn't been debugged, or even compiled on an arduino. Note that the code watches out for situations where none of the values are in the range around the average, and takes the raw average in these situations.

#include <stdio.h>
#include <math.h>

int values[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 
int total = 0; // The total of the last 10 samples read
int next = 0;  // The index of the oldest sample in the array

/*
 * Function that accepts a new sample, and returns the average of the last 10 samples.
 *
 * A threshold of 0.5 means that the value must be between 1/2x - 2x the 
 * average. A threshold of 0.2 means that the value must be between 1/5x -
 * 5x the average.
 */

int addValue( int newValue, double threshold )
{
  int newTotal;
  double newNum;
  double average;
  int index;

  total = total - values[next] + newValue; // Update totla
  values[next] = newValue; // Update array
  next = ( next + 1 ) % 10; // move to new 'oldest sample in the array'

  average = total / 10.0;
  newTotal = 0;
  newNum = 0;

  for ( index = 0; index < 10; index++ )
  {
    if ( values[index] >= average * threshold &&
	 values[index] <= average / threshold )
    {
      newTotal += values[index];
      newNum++;
    }
  }

  if ( newNum > 0 )
  {
    return (int) round( newTotal / newNum );
  }
  else
  {
    return (int) round( total / 10.0 ); // return average
  }
}

Wiper:
Here's my question. Is there a function or such to take average of 10 values, where you discard possible error values (= value is n% higher or lower than the average) and then take the average of the values left?

I know you could use variables and possibly a database, but are there any simpler ways to do this?

You should not take the average but the (weighted) median.
take your measurements, sort them and take the middle value. optional you can take the (weighted) average of the middle n where n is smaller than your sample set.

example { 51, 53, 51, 52, 1023, 50, 0 }
sort them { 0, 50, 51, 51, 52, 53, 1024 }
middle value = 51
average of middle 3 = 51.333

Check - Arduino Playground - HomePage -

There are many variations in which you take a weighted median, the idea is to take the middle value for 32%, its neighbours for 16% and so on and the weights add up to 100%, The top and bottom item only add for a few percent so the outliers add very little to the weighted median. In fact taking the middle one for 100% is the algorithm above, and taking equal weights it becomes the average

Or you could use a median filter

A median filter is a bit of a challenge without sorting, which is an O( n log n ) operation. Not too bad for an array of length 10, but an arduino doesn't exactly have a lot of grunt. But, I suppose that an incremental sorting technique would work well in the context of receiving a stream of samples that are to be moving average filtered.

This is quite an interesting problem. You need to be able to shuffle an incoming sample into the correct place in the array. But, you need to be able to find the oldest value and delete it.

As I have to smooth some values myself, I wrote the following which is a stab at taking the median. This will smooth data, but I wonder whether if I'm getting a pot that is alternating between two values whether the median itself could alternate a bit more than a sample average.

int values[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int sorted[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int oldest = 0;

int mediansmooth( int newValue )
{
  int index;
  int replace = values[oldest];
  double median;
  double newTotal;
  double newNum;

	// Replace oldest value
  values[oldest] = newValue;

	// Find the value to be replaced in the array. Note it doesn't
	// have to be the one that came in at the same time as the oldest
	// sample as long as it's the same value

  for ( index = 0; sorted[index] != replace; index++ );

  sorted[index] = newValue; // Put the new value in

	// Shuffle the new value down in the array if it's currently
	// too high up in the array

  while ( index > 0 && sorted[index-1] > sorted[index] )
  {
    temp = sorted[index-1];
    sorted[index-1] = sorted[index];
    sorted[index] = temp;
    index--;
  }

	// Shuffle the new value up in the array if it's currently 
	// too far down given its value.

  while ( index + 1 < 10 && sorted[index+1] < sorted[index] )
  {
    temp = sorted[index+1];
    sorted[index+1] = sorted[index];
    sorted[index] = temp;
    index++;
  }

	// Trivially take the median value

  median = ( sorted[4] + sorted[5] ) / 2.0;
  return median;
}