Skip to content

Commit

Permalink
0.3.0 version
Browse files Browse the repository at this point in the history
  • Loading branch information
RobTillaart committed Apr 16, 2020
1 parent 8757b1e commit 6f33ee4
Show file tree
Hide file tree
Showing 16 changed files with 991 additions and 1 deletion.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 Rob Tillaart
Copyright (c) 2012-2020 Rob Tillaart

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
# RunningAverage

Arduino library to calculate the running average by means of a circular buffer.

## Description
The RunningAverage object gives a running average of the last N numbers, giving them
all equal weight. This is doen by adding new data to an internal circular buffer,
removing the oldest and replace it by the newest. The size of the internal buffer

By keeping track of the **_sum** the runningAverage can be calculated fast (only 1 division).
This is done with **getFastAverage()**.
However the constant adding/subtracting when adding new elements introduces an accumulating error.
In tests adding up to 1500000 numbers this error was always small. But that is no proof.
In version 0.2.16 there is a fix added that uses the calculation of the sum in **getAverage()** to
update the internal **_sum**.

## Operation

See examples
200 changes: 200 additions & 0 deletions RunningAverage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
//
// FILE: RunningAverage.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.16
// DATE: 2015-July-10
// PURPOSE: RunningAverage library for Arduino
//
// The library stores N individual values in a circular buffer,
// to calculate the running average.
//
// HISTORY:
// 0.1.00 - 2011-01-30 initial version
// 0.1.01 - 2011-02-28 fixed missing destructor in .h
// 0.2.00 - 2012-??-?? Yuval Naveh added trimValue (found on web)
// http://stromputer.googlecode.com/svn-history/r74/trunk/Arduino/Libraries/RunningAverage/RunningAverage.cpp
// 0.2.01 - 2012-11-21 refactored
// 0.2.02 - 2012-12-30 refactored trimValue -> fillValue
// 0.2.03 - 2013-11-31 getElement
// 0.2.04 - 2014-07-03 added memory protection
// 0.2.05 - 2014-12-16 changed float -> double
// 0.2.06 - 2015-03-07 all size uint8_t
// 0.2.07 - 2015-03-16 added getMin() and getMax() functions (Eric Mulder)
// 0.2.08 - 2015-04-10 refactored getMin() and getMax() implementation
// 0.2.09 - 2015-07-12 refactor const + constructor
// 0.2.10 - 2015-09-01 added getFastAverage() and refactored getAverage()
// http://forum.arduino.cc/index.php?topic=50473
// 0.2.11 - 2015-09-04 added getMaxInBuffer() getMinInBuffer() request (Antoon)
// 0.2.12 - 2016-12-01 added GetStandardDeviation() GetStandardError() BufferIsFull() (V0v1kkk)
// 0.2.13 - 2017-07-26 revert double to float - issue #33;
// refactored a bit; marked some TODO's; all function names to camelCase
// 0.2.14 - 2020-01-15 added getValue(n) to retrieve elements in order of addition - see issue #132
// 0.2.15 - 2020-01-17 fix overflow in getValue - see issue #139
// 0.2.16 2020-04-16 improve _sum - see issue #149 (bourkemcrobbo)
//
// Released to the public domain
//

#include "RunningAverage.h"

RunningAverage::RunningAverage(const uint8_t size)
{
_size = size;
_ar = (float*) malloc(_size * sizeof(float));
if (_ar == NULL) _size = 0;
clear();
}

RunningAverage::~RunningAverage()
{
if (_ar != NULL) free(_ar);
}

// resets all counters
void RunningAverage::clear()
{
_cnt = 0;
_idx = 0;
_sum = 0.0;
_min = NAN;
_max = NAN;
for (uint8_t i = _size; i > 0; )
{
_ar[--i] = 0.0; // keeps addValue simpler
}
}

// adds a new value to the data-set
void RunningAverage::addValue(const float value)
{
if (_ar == NULL) return; // allocation error

_sum -= _ar[_idx];
_ar[_idx] = value;
_sum += _ar[_idx];
_idx++;

if (_idx == _size) _idx = 0; // faster than %

// handle min max
if (_cnt == 0) _min = _max = value;
else if (value < _min) _min = value;
else if (value > _max) _max = value;

// update count as last otherwise if ( _cnt == 0) above will fail
if (_cnt < _size) _cnt++;
}

// returns the average of the data-set added sofar
float RunningAverage::getAverage()
{
if (_cnt == 0) return NAN;

_sum = 0;
for (uint8_t i = 0; i < _cnt; i++)
{
_sum += _ar[i];
}
return _sum / _cnt; // multiplication is faster ==> extra admin
}

// the larger the size of the internal buffer the greater the gain wrt getAverage()
float RunningAverage::getFastAverage() const
{
if (_cnt == 0) return NAN;

return _sum / _cnt; // multiplication is faster ==> extra admin
}

// returns the minimum value in the buffer
float RunningAverage::getMinInBuffer() const
{
if (_cnt == 0) return NAN;

float min = _ar[0];
for (uint8_t i = 1; i < _cnt; i++)
{
if (_ar[i] < min) min = _ar[i];
}
return min;
}

// returns the maximum value in the buffer
float RunningAverage::getMaxInBuffer() const
{
if (_cnt == 0) return NAN;

float max = _ar[0];
for (uint8_t i = 1; i < _cnt; i++)
{
if (_ar[i] > max) max = _ar[i];
}
return max;
}


// returns the value of an element if exist, NAN otherwise
float RunningAverage::getElement(uint8_t idx) const
{
if (idx >=_cnt ) return NAN;

return _ar[idx];
}

// Return standard deviation of running average. If buffer is empty, return NAN.
float RunningAverage::getStandardDeviation() const
{
if (_cnt <= 1) return NAN;

float temp = 0;
float average = getFastAverage();
for (uint8_t i = 0; i < _cnt; i++)
{
temp += pow((_ar[i] - average), 2);
}
temp = sqrt(temp/(_cnt - 1)); // TODO possible divide by zero ....

return temp;
}

// Return standard error of running average. If buffer is empty, return NAN.
float RunningAverage::getStandardError() const //++
{
float temp = getStandardDeviation();

if (temp == NAN) return NAN;
if (_cnt <= 1) return NAN;

float n;
if (_cnt >= 30) n = _cnt;
else n = _cnt - 1;
temp = temp/sqrt(n);

return temp;
}

// fill the average with the same value number times. (weight)
// This is maximized to size times. no need to fill the internal buffer over 100%
void RunningAverage::fillValue(const float value, const uint8_t number)
{
clear();
uint8_t s = number;
if (s > _size) s = _size;
for (uint8_t i = s; i > 0; i--)
{
addValue(value);
}
// NOTE: the clear iterates over the buffer,
// so merging the clear loop could gain some performance.
}

float RunningAverage::getValue(const uint8_t idx)
{
if (_cnt == 0) return NAN;
if (idx >= _cnt) return NAN; // cannot ask more than is added
uint16_t pos = idx + _idx;
if (pos >= _cnt) pos -= _cnt;
return _ar[pos];
}

// -- END OF FILE --
64 changes: 64 additions & 0 deletions RunningAverage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once
//
// FILE: RunningAverage.h
// AUTHOR: [email protected]
// VERSION: 0.3.0
// DATE: 2016-dec-01
// PURPOSE: RunningAverage library for Arduino
// URL: https://github.com/RobTillaart/RunningAverage
// HISTORY: See RunningAverage.cpp
//

#define RUNNINGAVERAGE_LIB_VERSION "0.3.0"

// #include <stdlib.h>
// #include <math.h>
#include "Arduino.h"

class RunningAverage
{
public:
RunningAverage(void);
explicit RunningAverage(const uint8_t size);
~RunningAverage();

void clear();
void addValue(const float);
void fillValue(const float, const uint8_t);
float getValue(const uint8_t);

float getAverage(); // iterates over all elements.
float getFastAverage() const; // reuses previous calculated values.

// return statistical characteristics of the running average
float getStandardDeviation() const;
float getStandardError() const;

// returns min/max added to the data-set since last clear
float getMin() const { return _min; };
float getMax() const { return _max; };

// returns min/max from the values in the internal buffer
float getMinInBuffer() const;
float getMaxInBuffer() const;

// return true if buffer is full
bool bufferIsFull() const { return _cnt == _size; };

float getElement(uint8_t idx) const;

uint8_t getSize() const { return _size; }
uint8_t getCount() const { return _cnt; }


protected:
uint8_t _size;
uint8_t _cnt;
uint8_t _idx;
float _sum;
float* _ar;
float _min;
float _max;
};

// -- END OF FILE --
61 changes: 61 additions & 0 deletions examples/fillValue/fillValue.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// FILE: fillValue.ino
// AUTHOR: Rob Tillaart
// DATE: 2012-12-30
//
// PUPROSE: demo + timing of fillValue
//

#include "RunningAverage.h"

RunningAverage myRA(10);
int samples = 0;

uint32_t start, stop;


void setup(void)
{
Serial.begin(115200);
Serial.print("Demo RunningAverage lib - fillValue ");
Serial.print("Version: ");
Serial.println(RUNNINGAVERAGE_LIB_VERSION);
delay(10);

for (int i = 0; i < 15; i++)
{
measure_duration(i);
}

Serial.println();
}

void loop(void)
{
long rn = random(0, 100);
myRA.addValue(rn / 100.0);
samples++;
Serial.print("Running Average: ");
Serial.println(myRA.getAverage(), 4);

if (samples == 300)
{
samples = 0;
myRA.fillValue(100, 10);
}
delay(100);
}

void measure_duration(int n)
{
start = micros();
myRA.fillValue(100, n);
stop = micros();
Serial.print("fillValue(100, ");
Serial.print(n);
Serial.print("): ");
Serial.println(stop - start);
delay(10);
}

// -- END OF FILE --
Loading

0 comments on commit 6f33ee4

Please sign in to comment.