0.4.6 RunningAverage

This commit is contained in:
Rob Tillaart 2024-06-29 09:22:03 +02:00
parent 650a3c45f0
commit 40a0f8123f
12 changed files with 218 additions and 51 deletions

View File

@ -6,11 +6,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.4.6] - 2024-06-15
- Fix #30, add **float getSum()** (thanks to heidnerd)
- Fix #31, add **float getStandardDeviationLast(uint16_t count)** (thanks to alvaro-oliver)
- added code to detect inner array == NULL in more functions (return NAN).
- changed return type **bool clear()**
- changed return type **bool addValue(..)**
- changed return type **bool fillValue(..)**
- changed return type **bool setPartial(..)**
- moved performance.txt to performance sketch
- update unit test
- update keywords.txt
- update readme.md
- minor edits.
## [0.4.5] - 2024-01-05 ## [0.4.5] - 2024-01-05
- fix URL in examples - fix URL in examples
- minor edits - minor edits
## [0.4.4] - 2023-10-18 ## [0.4.4] - 2023-10-18
- update readme.md badges - update readme.md badges
- update examples - update examples
@ -29,7 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- minor edits - minor edits
## [0.4.1] - 2021-11-22 ## [0.4.1] - 2021-11-22
- updated buil-CI, readme, badges - updated build-CI, readme, badges
- add getAverageLast() functions. - add getAverageLast() functions.
## [0.4.0] - 2021-05-18 ## [0.4.0] - 2021-05-18
@ -57,7 +70,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.15] - 2020-01-17 ## [0.2.15] - 2020-01-17
- fix overflow in getValue - see issue #139 - fix overflow in getValue - see issue #139
## [0.2.14] - 2020-01-15 ## [0.2.14] - 2020-01-15
- added getValue(n) to retrieve elements in order of addition - see issue #132 - added getValue(n) to retrieve elements in order of addition - see issue #132

View File

@ -58,12 +58,19 @@ No default size (yet).
### Basic ### Basic
- **void clear()** empties the internal buffer. The following functions return **false** if the internal buffer is not allocated.
- **void add(float value)** wrapper for **addValue()**
- **void addValue(float value)** adds a new value to the object, if the internal buffer is full, - **bool clear()** empties the internal buffer.
- **bool add(float value)** wrapper for **addValue()**.
- **bool addValue(float value)** adds a new value to the object, if the internal buffer is full,
the oldest element is removed. the oldest element is removed.
- **void fillValue(float value, uint16_t number)** adds number elements of value. - **bool fillValue(float value, uint16_t number)** adds number elements of value.
Good for initializing the system to a certain starting average. Good for initializing the system to a certain starting average.
The following functions returns NAN if there are no values present (count == 0) or
of internal array is not allocated.
- **float getValue(uint16_t position)** returns the value at **position** from the additions. - **float getValue(uint16_t position)** returns the value at **position** from the additions.
Position 0 is the first one to disappear. Position 0 is the first one to disappear.
- **float getAverage()** iterates over all elements to get the average, slower but accurate. - **float getAverage()** iterates over all elements to get the average, slower but accurate.
@ -80,6 +87,7 @@ Needs more than one element to be calculable.
- **float getMax()** returns maximum since last clear, does not need to be in the buffer any more. - **float getMax()** returns maximum since last clear, does not need to be in the buffer any more.
- **float getMinInBuffer()** returns minimum in the internal buffer. - **float getMinInBuffer()** returns minimum in the internal buffer.
- **float getMaxInBuffer()** returns maximum in the internal buffer. - **float getMaxInBuffer()** returns maximum in the internal buffer.
- **float getSum()** returns sum of values in the internal buffer.
### Admin functions ### Admin functions
@ -92,9 +100,10 @@ Needs more than one element to be calculable.
## Partial functions ## Partial functions
- **void setPartial(uint16_t partial = 0)** use only a part of the internal array. - **bool setPartial(uint16_t partial = 0)** use only a part of the internal array.
Allows to change the weight and history factor. Allows to change the weight and history factor.
0 ==> use all == default. 0 ==> use all == default.
Returns false if internal buffer is not allocated.
- **uint16_t getPartial()** returns the set value for partial. - **uint16_t getPartial()** returns the set value for partial.
@ -122,6 +131,33 @@ parameter, the functions will return the statistics of the whole buffer.
- **float getAverageSubset(uint16_t start, uint16_t count)** - **float getAverageSubset(uint16_t start, uint16_t count)**
Get the average of subset - count elements from start. Get the average of subset - count elements from start.
Returns NAN if no elements or internal array not allocated.
## Performance
Indicative performance on an UNO, see examples.
| Function | 0.4.5 us | 0.4.6 us | Notes |
|:----------------------:|:----------:|:----------:|:-------:|
| clear | 60 | 60 |
| addValue | 24 | 24 |
| fillValue | 1512 | 1520 |
| getValue | 4 | 8 |
| getAverage | 520 | 552 |
| getFastAverage | 36 | 40 |
| getStandardDeviation | 1856 | 1856 |
| getStandardError | 1920 | 1920 |
| getMin | 8 | 4 |
| getMax | 4 | 4 |
| getMinInBuffer | 216 | 212 |
| getMaxInBuffer | 208 | 208 |
| getSum | - | 8 |
| bufferIsFull | 8 | 8 |
| getElement | 4 | 4 |
| getSize | 8 | 8 |
| getCount | 8 | 8 |
| last functions | - | - | not tested
## Operation ## Operation
@ -131,7 +167,6 @@ See examples
## Future ## Future
#### Must #### Must
- update documentation, explain better - update documentation, explain better
@ -139,16 +174,22 @@ See examples
#### Should #### Should
- check for optimizations. - check for optimizations.
- divide by count happens often ... - divide by count happens often
- clear(bool zero = true) to suppress setting all to 0. ? - fillValue can be optimized (See #13)
- ```temp = sqrt(temp/(_count - 1));``` is this correct STDDEV?
- divide by count or count - 1?
- https://www.zaner.com/3.0/education/technicalstudies/MSD.asp
#### Could #### Could
- create a double based derived class? Template class? - create a double based derived class?
- Template class?
- add error handling (important?). - add error handling (important?).
- investigate **modus()** most frequently occurring value. - investigate **modus()** most frequently occurring value.
- difficult with floats ? - difficult with floats ?
- what to do when on two or more values are on par? - what to do when on two or more values are on par? (no modus?)
- **int getUniqueValuesInBuffer()** O(n^2)
#### Wont #### Wont

View File

@ -1,7 +1,7 @@
// //
// FILE: RunningAverage.cpp // FILE: RunningAverage.cpp
// AUTHOR: Rob Tillaart // AUTHOR: Rob Tillaart
// VERSION: 0.4.5 // VERSION: 0.4.6
// DATE: 2011-01-30 // DATE: 2011-01-30
// PURPOSE: Arduino library to calculate the running average by means of a circular buffer // PURPOSE: Arduino library to calculate the running average by means of a circular buffer
// URL: https://github.com/RobTillaart/RunningAverage // URL: https://github.com/RobTillaart/RunningAverage
@ -30,26 +30,31 @@ RunningAverage::~RunningAverage()
// resets all counters // resets all counters
void RunningAverage::clear() bool RunningAverage::clear()
{ {
_count = 0; _count = 0;
_index = 0; _index = 0;
_sum = 0.0; _sum = 0.0;
_min = NAN; _min = NAN;
_max = NAN; _max = NAN;
if (_array == NULL)
{
return false;
}
for (uint16_t i = _size; i > 0; ) for (uint16_t i = _size; i > 0; )
{ {
_array[--i] = 0.0; // keeps addValue simpler _array[--i] = 0.0; // keeps addValue simpler
} }
return true;
} }
// adds a new value to the data-set // adds a new value to the data-set
void RunningAverage::addValue(const float value) bool RunningAverage::addValue(const float value)
{ {
if (_array == NULL) if (_array == NULL)
{ {
return; return false;
} }
_sum -= _array[_index]; _sum -= _array[_index];
@ -66,13 +71,15 @@ void RunningAverage::addValue(const float value)
// update count as last otherwise if ( _count == 0) above will fail // update count as last otherwise if ( _count == 0) above will fail
if (_count < _partial) _count++; if (_count < _partial) _count++;
return true;
} }
// returns the average of the data-set added so far, NAN if no elements. // returns the average of the data-set added so far,
// returns NAN if no elements or missing array
float RunningAverage::getAverage() float RunningAverage::getAverage()
{ {
if (_count == 0) if ((_count == 0) || (_array == NULL))
{ {
return NAN; return NAN;
} }
@ -94,7 +101,6 @@ float RunningAverage::getFastAverage() const
{ {
return NAN; return NAN;
} }
return _sum / _count; // multiplication is faster ==> extra admin return _sum / _count; // multiplication is faster ==> extra admin
} }
@ -102,7 +108,7 @@ float RunningAverage::getFastAverage() const
// returns the minimum value in the buffer // returns the minimum value in the buffer
float RunningAverage::getMinInBuffer() const float RunningAverage::getMinInBuffer() const
{ {
if (_count == 0) if ((_count == 0) || (_array == NULL))
{ {
return NAN; return NAN;
} }
@ -119,7 +125,7 @@ float RunningAverage::getMinInBuffer() const
// returns the maximum value in the buffer // returns the maximum value in the buffer
float RunningAverage::getMaxInBuffer() const float RunningAverage::getMaxInBuffer() const
{ {
if (_count == 0) if ((_count == 0) || (_array == NULL))
{ {
return NAN; return NAN;
} }
@ -136,11 +142,10 @@ float RunningAverage::getMaxInBuffer() const
// returns the value of an element if exist, NAN otherwise // returns the value of an element if exist, NAN otherwise
float RunningAverage::getElement(uint16_t index) const float RunningAverage::getElement(uint16_t index) const
{ {
if (_count == 0) if ((_count == 0) || (_array == NULL))
{ {
return NAN; return NAN;
} }
return _array[index]; return _array[index];
} }
@ -161,6 +166,7 @@ float RunningAverage::getStandardDeviation() const
{ {
temp += pow((_array[i] - average), 2); temp += pow((_array[i] - average), 2);
} }
// TODO: when to divide by count || count-1?
temp = sqrt(temp/(_count - 1)); temp = sqrt(temp/(_count - 1));
return temp; return temp;
// see issue #13 // see issue #13
@ -188,9 +194,12 @@ float RunningAverage::getStandardError() const
// fill the average with the same value number times. (weight) // fill the average with the same value number times. (weight)
// This is maximized to size times. // This is maximized to size times.
// no need to fill the internal buffer over 100% // no need to fill the internal buffer over 100%
void RunningAverage::fillValue(const float value, const uint16_t number) bool RunningAverage::fillValue(const float value, const uint16_t number)
{ {
clear(); if (!clear())
{
return false;
}
uint16_t s = number; uint16_t s = number;
if (s > _partial) s = _partial; if (s > _partial) s = _partial;
@ -198,6 +207,7 @@ void RunningAverage::fillValue(const float value, const uint16_t number)
{ {
addValue(value); addValue(value);
} }
return true;
} }
@ -223,7 +233,7 @@ void RunningAverage::fillValue(const float value, const uint16_t number)
float RunningAverage::getValue(const uint16_t position) float RunningAverage::getValue(const uint16_t position)
{ {
if (_count == 0) if ((_count == 0) || (_array == NULL))
{ {
return NAN; return NAN;
} }
@ -238,11 +248,11 @@ float RunningAverage::getValue(const uint16_t position)
} }
void RunningAverage::setPartial(const uint16_t partial) bool RunningAverage::setPartial(const uint16_t partial)
{ {
_partial = partial; _partial = partial;
if ((_partial == 0) || (_partial > _size)) _partial = _size; if ((_partial == 0) || (_partial > _size)) _partial = _size;
clear(); return clear();
} }
@ -264,6 +274,28 @@ float RunningAverage::getAverageLast(uint16_t count)
} }
float RunningAverage::getStandardDeviationLast(uint16_t count)
{
uint16_t cnt = count;
if (cnt > _count) cnt = _count;
if (cnt <= 1) return NAN;
float temp = 0;
float average = getAverageLast(count);
uint16_t idx = _index;
for (uint16_t i = 0; i < cnt; i++)
{
if (idx == 0) idx = _size;
idx--;
temp += pow((_array[idx] - average), 2);
}
temp = sqrt(temp/(cnt - 1));
return temp;
}
float RunningAverage::getMinInBufferLast(uint16_t count) float RunningAverage::getMinInBufferLast(uint16_t count)
{ {
uint16_t cnt = count; uint16_t cnt = count;
@ -306,7 +338,7 @@ float RunningAverage::getMaxInBufferLast(uint16_t count)
float RunningAverage::getAverageSubset(uint16_t start, uint16_t count) float RunningAverage::getAverageSubset(uint16_t start, uint16_t count)
{ {
if (_count == 0) if ((_count == 0) || (_array == NULL))
{ {
return NAN; return NAN;
} }

View File

@ -2,7 +2,7 @@
// //
// FILE: RunningAverage.h // FILE: RunningAverage.h
// AUTHOR: Rob Tillaart // AUTHOR: Rob Tillaart
// VERSION: 0.4.5 // VERSION: 0.4.6
// DATE: 2011-01-30 // DATE: 2011-01-30
// PURPOSE: Arduino library to calculate the running average by means of a circular buffer // PURPOSE: Arduino library to calculate the running average by means of a circular buffer
// URL: https://github.com/RobTillaart/RunningAverage // URL: https://github.com/RobTillaart/RunningAverage
@ -14,7 +14,7 @@
#include "Arduino.h" #include "Arduino.h"
#define RUNNINGAVERAGE_LIB_VERSION (F("0.4.5")) #define RUNNINGAVERAGE_LIB_VERSION (F("0.4.6"))
class RunningAverage class RunningAverage
@ -23,10 +23,11 @@ public:
explicit RunningAverage(const uint16_t size); explicit RunningAverage(const uint16_t size);
~RunningAverage(); ~RunningAverage();
void clear(); // return false if internal buffer not allocated.
void add(const float value) { addValue(value); }; bool clear();
void addValue(const float value); bool add(const float value) { return addValue(value); };
void fillValue(const float value, const uint16_t number); bool addValue(const float value);
bool fillValue(const float value, const uint16_t number);
float getValue(const uint16_t position); float getValue(const uint16_t position);
float getAverage(); // iterates over all elements. float getAverage(); // iterates over all elements.
@ -40,9 +41,10 @@ public:
float getMin() const { return _min; }; float getMin() const { return _min; };
float getMax() const { return _max; }; float getMax() const { return _max; };
// returns min/max from the values in the internal buffer // returns min/max/sum from the values in the internal buffer
float getMinInBuffer() const; float getMinInBuffer() const;
float getMaxInBuffer() const; float getMaxInBuffer() const;
float getSum() const { return _sum; };
// return true if buffer is full // return true if buffer is full
bool bufferIsFull() const { return _count == _size; }; bool bufferIsFull() const { return _count == _size; };
@ -54,12 +56,13 @@ public:
// use not all elements just a part from 0..partial-1 // use not all elements just a part from 0..partial-1
// (re)setting partial will clear the internal buffer. // (re)setting partial will clear the internal buffer.
void setPartial(const uint16_t partial = 0); // 0 ==> use all bool setPartial(const uint16_t partial = 0); // 0 ==> use all
uint16_t getPartial() { return _partial; }; uint16_t getPartial() { return _partial; };
// get some stats from the last count additions. // get some stats from the last count additions.
float getAverageLast(uint16_t count); float getAverageLast(uint16_t count);
float getStandardDeviationLast(uint16_t count);
float getMinInBufferLast(uint16_t count); float getMinInBufferLast(uint16_t count);
float getMaxInBufferLast(uint16_t count); float getMaxInBufferLast(uint16_t count);

View File

@ -0,0 +1,25 @@
UNO IDE 1.8.19
ra_performance.ino
RUNNINGAVERAGE_LIB_VERSION: 0.4.5
clear : 60
addValue : 24
fillValue : 1512
getValue : 4
getAverage : 520
getFastAverage : 36
getStandardDeviation : 1856
getStandardError : 1920
getMin : 8
getMax : 4
getMinInBuffer : 216
getMaxInBuffer : 208
bufferIsFull : 8
getElement : 4
getSize : 8
getCount : 8
done...

View File

@ -0,0 +1,26 @@
UNO IDE 1.8.19
ra_performance.ino
RUNNINGAVERAGE_LIB_VERSION: 0.4.6
clear : 60
addValue : 24
fillValue : 1520
getValue : 8
getAverage : 552
getFastAverage : 40
getStandardDeviation : 1856
getStandardError : 1920
getMin : 4
getMax : 4
getMinInBuffer : 216
getMaxInBuffer : 212
getSum : 4
bufferIsFull : 12
getElement : 4
getSize : 8
getCount : 8
done...

View File

@ -48,6 +48,7 @@ void setup(void)
test_getMax(); test_getMax();
test_getMinInBuffer(); test_getMinInBuffer();
test_getMaxInBuffer(); test_getMaxInBuffer();
test_getSum();
test_bufferIsFull(); test_bufferIsFull();
test_getElement(); test_getElement();
@ -191,6 +192,17 @@ void test_getMaxInBuffer()
} }
void test_getSum()
{
start = micros();
x = myRA.getSum();
stop = micros();
Serial.print("\tgetSum \t\t: ");
Serial.println(stop - start);
delay(10);
}
void test_bufferIsFull() void test_bufferIsFull()
{ {
start = micros(); start = micros();
@ -241,4 +253,3 @@ void loop()
// -- END OF FILE -- // -- END OF FILE --

View File

@ -21,6 +21,7 @@ getMin KEYWORD2
getMax KEYWORD2 getMax KEYWORD2
getMinInBuffer KEYWORD2 getMinInBuffer KEYWORD2
getMaxInBuffer KEYWORD2 getMaxInBuffer KEYWORD2
getSum KEYWORD2
bufferIsFull() KEYWORD2 bufferIsFull() KEYWORD2
getElement KEYWORD2 getElement KEYWORD2
@ -31,6 +32,7 @@ setPartial KEYWORD2
getPartial KEYWORD2 getPartial KEYWORD2
getAverageLast KEYWORD2 getAverageLast KEYWORD2
getStandardDeviationLast KEYWORD2
getMinInBufferLast KEYWORD2 getMinInBufferLast KEYWORD2
getMaxInBufferLast KEYWORD2 getMaxInBufferLast KEYWORD2

View File

@ -15,7 +15,7 @@
"type": "git", "type": "git",
"url": "https://github.com/RobTillaart/RunningAverage.git" "url": "https://github.com/RobTillaart/RunningAverage.git"
}, },
"version": "0.4.5", "version": "0.4.6",
"license": "MIT", "license": "MIT",
"frameworks": "*", "frameworks": "*",
"platforms": "*", "platforms": "*",

View File

@ -1,5 +1,5 @@
name=RunningAverage name=RunningAverage
version=0.4.5 version=0.4.6
author=Rob Tillaart <rob.tillaart@gmail.com> author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com> maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=The library stores the last N individual values in a circular buffer to calculate the running average. sentence=The library stores the last N individual values in a circular buffer to calculate the running average.

View File

@ -63,7 +63,7 @@ unittest(test_zero_elements)
} }
unittest(test_min_max) unittest(test_min_max_sum)
{ {
RunningAverage myRA(10); RunningAverage myRA(10);
myRA.clear(); myRA.clear();
@ -74,14 +74,17 @@ unittest(test_min_max)
} }
float mi = myRA.getMin(); float mi = myRA.getMin();
assertEqual(-5, mi); assertEqualFloat(-5, mi, 0.001);
float ma = myRA.getMax(); float ma = myRA.getMax();
assertEqual(5, ma); assertEqualFloat(5, ma, 0.001);
mi = myRA.getMinInBuffer(); mi = myRA.getMinInBuffer();
assertEqual(-4, mi); assertEqualFloat(-4, mi, 0.001);
ma = myRA.getMaxInBuffer(); ma = myRA.getMaxInBuffer();
assertEqual(5, ma); assertEqualFloat(5, ma, 0.001);
float sum = myRA.getSum();
assertEqualFloat(5, sum, 0.001);
} }
@ -94,8 +97,8 @@ unittest(test_buffer_full)
for (int i = 0; i < 9; i++) for (int i = 0; i < 9; i++)
{ {
myRA.addValue(i); myRA.addValue(i);
assertFalse(myRA.bufferIsFull());
} }
assertFalse(myRA.bufferIsFull());
myRA.addValue(42); myRA.addValue(42);
assertTrue(myRA.bufferIsFull()); assertTrue(myRA.bufferIsFull());
@ -111,8 +114,8 @@ unittest(test_large)
for (int i = 0; i < 299; i++) for (int i = 0; i < 299; i++)
{ {
myRA.addValue(i); myRA.addValue(i);
assertFalse(myRA.bufferIsFull());
} }
assertFalse(myRA.bufferIsFull());
myRA.addValue(42); myRA.addValue(42);
assertTrue(myRA.bufferIsFull()); assertTrue(myRA.bufferIsFull());
@ -151,25 +154,37 @@ unittest(test_last)
{ {
myRA.addValue(i); myRA.addValue(i);
} }
fprintf(stderr, "\nPARTIAL: 0\n");
assertNAN(myRA.getMinInBufferLast(0)); assertNAN(myRA.getMinInBufferLast(0));
assertNAN(myRA.getAverageLast(0)); assertNAN(myRA.getAverageLast(0));
assertNAN(myRA.getMaxInBufferLast(0)); assertNAN(myRA.getMaxInBufferLast(0));
assertNAN(myRA.getStandardDeviationLast(0));
fprintf(stderr, "\nPARTIAL: 1\n");
assertEqualFloat(999.0, myRA.getMinInBufferLast(1), 0.001); assertEqualFloat(999.0, myRA.getMinInBufferLast(1), 0.001);
assertEqualFloat(999.0, myRA.getAverageLast(1), 0.001); assertEqualFloat(999.0, myRA.getAverageLast(1), 0.001);
assertEqualFloat(999.0, myRA.getMaxInBufferLast(1), 0.001); assertEqualFloat(999.0, myRA.getMaxInBufferLast(1), 0.001);
assertEqualFloat(0, myRA.getStandardDeviationLast(1), 0.001);
fprintf(stderr, "\nPARTIAL: 10\n");
assertEqualFloat(990.0, myRA.getMinInBufferLast(10), 0.001); assertEqualFloat(990.0, myRA.getMinInBufferLast(10), 0.001);
assertEqualFloat(994.5, myRA.getAverageLast(10), 0.001); assertEqualFloat(994.5, myRA.getAverageLast(10), 0.001);
assertEqualFloat(999.0, myRA.getMaxInBufferLast(10), 0.001); assertEqualFloat(999.0, myRA.getMaxInBufferLast(10), 0.001);
assertEqualFloat(3.02765, myRA.getStandardDeviationLast(10), 0.001);
fprintf(stderr, "\nPARTIAL: 100\n");
assertEqualFloat(900.0, myRA.getMinInBufferLast(100), 0.001); assertEqualFloat(900.0, myRA.getMinInBufferLast(100), 0.001);
assertEqualFloat(949.5, myRA.getAverageLast(100), 0.001); assertEqualFloat(949.5, myRA.getAverageLast(100), 0.001);
assertEqualFloat(999.0, myRA.getMaxInBufferLast(100), 0.001); assertEqualFloat(999.0, myRA.getMaxInBufferLast(100), 0.001);
assertEqualFloat(29.0115, myRA.getStandardDeviationLast(100), 0.001);
fprintf(stderr, "\nPARTIAL: 1000\n");
assertEqualFloat(700.0, myRA.getMinInBufferLast(1000), 0.001); assertEqualFloat(700.0, myRA.getMinInBufferLast(1000), 0.001);
assertEqualFloat(849.5, myRA.getAverageLast(1000), 0.001); assertEqualFloat(849.5, myRA.getAverageLast(1000), 0.001);
assertEqualFloat(999.0, myRA.getMaxInBufferLast(1000), 0.001); assertEqualFloat(999.0, myRA.getMaxInBufferLast(1000), 0.001);
assertEqualFloat(86.7468, myRA.getStandardDeviationLast(1000), 0.001);
} }