From 272cd7e583794b7d32aa9fa083a521686174a382 Mon Sep 17 00:00:00 2001 From: Rob Tillaart Date: Sun, 30 Jun 2024 15:59:33 +0200 Subject: [PATCH] 0.1.0 RunAvgWeight --- libraries/RunAvgWeight/.arduino-ci.yml | 29 ++ libraries/RunAvgWeight/.github/FUNDING.yml | 4 + .../.github/workflows/arduino-lint.yml | 13 + .../.github/workflows/arduino_test_runner.yml | 17 ++ .../.github/workflows/jsoncheck.yml | 18 ++ libraries/RunAvgWeight/CHANGELOG.md | 11 + libraries/RunAvgWeight/LICENSE | 21 ++ libraries/RunAvgWeight/README.md | 121 +++++++++ libraries/RunAvgWeight/RunAvgWeight.cpp | 253 ++++++++++++++++++ libraries/RunAvgWeight/RunAvgWeight.h | 80 ++++++ .../raw_demo_equal_weight.ino | 59 ++++ .../examples/raw_test/raw_test.ino | 95 +++++++ libraries/RunAvgWeight/keywords.txt | 39 +++ libraries/RunAvgWeight/library.json | 23 ++ libraries/RunAvgWeight/library.properties | 11 + libraries/RunAvgWeight/test/unit_test_001.cpp | 125 +++++++++ 16 files changed, 919 insertions(+) create mode 100644 libraries/RunAvgWeight/.arduino-ci.yml create mode 100644 libraries/RunAvgWeight/.github/FUNDING.yml create mode 100644 libraries/RunAvgWeight/.github/workflows/arduino-lint.yml create mode 100644 libraries/RunAvgWeight/.github/workflows/arduino_test_runner.yml create mode 100644 libraries/RunAvgWeight/.github/workflows/jsoncheck.yml create mode 100644 libraries/RunAvgWeight/CHANGELOG.md create mode 100644 libraries/RunAvgWeight/LICENSE create mode 100644 libraries/RunAvgWeight/README.md create mode 100644 libraries/RunAvgWeight/RunAvgWeight.cpp create mode 100644 libraries/RunAvgWeight/RunAvgWeight.h create mode 100644 libraries/RunAvgWeight/examples/raw_demo_equal_weight/raw_demo_equal_weight.ino create mode 100644 libraries/RunAvgWeight/examples/raw_test/raw_test.ino create mode 100644 libraries/RunAvgWeight/keywords.txt create mode 100644 libraries/RunAvgWeight/library.json create mode 100644 libraries/RunAvgWeight/library.properties create mode 100644 libraries/RunAvgWeight/test/unit_test_001.cpp diff --git a/libraries/RunAvgWeight/.arduino-ci.yml b/libraries/RunAvgWeight/.arduino-ci.yml new file mode 100644 index 00000000..1f989667 --- /dev/null +++ b/libraries/RunAvgWeight/.arduino-ci.yml @@ -0,0 +1,29 @@ +platforms: + rpipico: + board: rp2040:rp2040:rpipico + package: rp2040:rp2040 + gcc: + features: + defines: + - ARDUINO_ARCH_RP2040 + warnings: + flags: + +packages: + rp2040:rp2040: + url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json + +compile: + # Choosing to run compilation tests on 2 different Arduino platforms + # selected only those that work + platforms: + - uno + # - due + # - zero + # - leonardo + - m4 + - esp32 + - esp8266 + # - mega2560 + - rpipico + diff --git a/libraries/RunAvgWeight/.github/FUNDING.yml b/libraries/RunAvgWeight/.github/FUNDING.yml new file mode 100644 index 00000000..554358c3 --- /dev/null +++ b/libraries/RunAvgWeight/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: RobTillaart +custom: "https://www.paypal.me/robtillaart" diff --git a/libraries/RunAvgWeight/.github/workflows/arduino-lint.yml b/libraries/RunAvgWeight/.github/workflows/arduino-lint.yml new file mode 100644 index 00000000..7f8f4ef4 --- /dev/null +++ b/libraries/RunAvgWeight/.github/workflows/arduino-lint.yml @@ -0,0 +1,13 @@ +name: Arduino-lint + +on: [push, pull_request] +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: arduino/arduino-lint-action@v1 + with: + library-manager: update + compliance: strict \ No newline at end of file diff --git a/libraries/RunAvgWeight/.github/workflows/arduino_test_runner.yml b/libraries/RunAvgWeight/.github/workflows/arduino_test_runner.yml new file mode 100644 index 00000000..dbd0ce79 --- /dev/null +++ b/libraries/RunAvgWeight/.github/workflows/arduino_test_runner.yml @@ -0,0 +1,17 @@ +name: Arduino CI + +on: [push, pull_request] + +jobs: + runTest: + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - run: | + gem install arduino_ci + arduino_ci.rb diff --git a/libraries/RunAvgWeight/.github/workflows/jsoncheck.yml b/libraries/RunAvgWeight/.github/workflows/jsoncheck.yml new file mode 100644 index 00000000..1cbb5e2c --- /dev/null +++ b/libraries/RunAvgWeight/.github/workflows/jsoncheck.yml @@ -0,0 +1,18 @@ +name: JSON check + +on: + push: + paths: + - '**.json' + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - name: json-syntax-check + uses: limitusus/json-syntax-check@v2 + with: + pattern: "\\.json$" \ No newline at end of file diff --git a/libraries/RunAvgWeight/CHANGELOG.md b/libraries/RunAvgWeight/CHANGELOG.md new file mode 100644 index 00000000..531f6d3c --- /dev/null +++ b/libraries/RunAvgWeight/CHANGELOG.md @@ -0,0 +1,11 @@ +# Change Log RunAvgWeight + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + + +## [0.1.0] - 2024-06-30 +- initial version, based upon **RunningAverage** + diff --git a/libraries/RunAvgWeight/LICENSE b/libraries/RunAvgWeight/LICENSE new file mode 100644 index 00000000..37fe70e5 --- /dev/null +++ b/libraries/RunAvgWeight/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024-2024 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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libraries/RunAvgWeight/README.md b/libraries/RunAvgWeight/README.md new file mode 100644 index 00000000..7a07a955 --- /dev/null +++ b/libraries/RunAvgWeight/README.md @@ -0,0 +1,121 @@ + +[![Arduino CI](https://github.com/RobTillaart/RunAvgWeight/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) +[![Arduino-lint](https://github.com/RobTillaart/RunAvgWeight/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/RunAvgWeight/actions/workflows/arduino-lint.yml) +[![JSON check](https://github.com/RobTillaart/RunAvgWeight/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/RunAvgWeight/actions/workflows/jsoncheck.yml) +[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/RunAvgWeight.svg)](https://github.com/RobTillaart/RunAvgWeight/issues) + +[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/RunAvgWeight/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/RobTillaart/RunAvgWeight.svg?maxAge=3600)](https://github.com/RobTillaart/RunAvgWeight/releases) +[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/RunAvgWeight.svg)](https://registry.platformio.org/libraries/robtillaart/RunAvgWeight) + + +# RunAvgWeight + +Arduino library to calculate the running average with weights by means of a circular buffer. + + +## Description + +**Experimental** + +The RunAvgWeight (RAW) object gives a running average of the last N floating point numbers, +while giving them a different weight. + +This is done by adding new data to internal circular buffers, removing the oldest and +replace it by the newest. +The size of the internal buffer can be set in the constructor. + +The interface of the RunAvgWeight object is strongly based upon **RunningAverage**. +However not all functions are implemented. + +**Warning** + +However the constant adding and subtracting when adding new elements to the RAW object +possibly introduces an ever increasing error. +In tests with **RunningAverage** class with equal weights, adding up to 1500000 numbers +this error was always small. Be aware of this. + + +#### Related + +- https://github.com/RobTillaart/Correlation +- https://github.com/RobTillaart/GST - Golden standard test metrics +- https://github.com/RobTillaart/Histogram +- https://github.com/RobTillaart/RunningAngle +- https://github.com/RobTillaart/RunAvgWeight +- https://github.com/RobTillaart/RunningAverage +- https://github.com/RobTillaart/RunningMedian +- https://github.com/RobTillaart/statHelpers - combinations & permutations +- https://github.com/RobTillaart/printHelpers - print scientific format +- https://github.com/RobTillaart/Statistic + + +## Interface + +```cpp +#include "RunAvgWeight.h" +``` + +### Constructor + +- **RunAvgWeight(uint16_t size)** allocates dynamic memory, one float (4 bytes) per element. +No default size (yet). +- **~RunAvgWeight()** destructor to free the memory allocated. + + +### Basic + +- **void clear()** empties the internal buffers. +- **void addValue(float value, float weight)** adds a new value to the object, +if the internal buffer is full, the oldest element is removed. +- **float getValue(uint16_t position)** returns the value at **position** from the additions. +Position 0 is the first one to disappear. +- **float getAverage()** iterates over all elements to get the average, slower but accurate. +Updates the variables used by **getFastAverage()** to improve its accuracy again. +- **float getFastAverage()** reuses previous calculated values, therefore faster. Accuracy can drift. + + +### Extended functions + +- **float getStandardDeviation()** returns the standard deviation of the current content. +Needs more than one element to be calculable. +- **float getStandardError()** returns the standard error of the current content. +- **float getMin()** returns minimum 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 getMaxInBuffer()** returns maximum in the internal buffer. + + +### Admin functions + +- **bool bufferIsFull()** returns true if buffer is full. +- **float getElement(uint16_t index)** get element directly from internal buffer at index. (debug) +- **uint16_t getSize()** returns the size of the internal array. +- **uint16_t getCount()** returns the number of slots used of the internal array. + + + +## Future + +#### Must + +- update documentation +- keep in sync with RunningAverage + +#### Should + +- elaborate unit test + +#### Could + +#### Wont + +## Support + +If you appreciate my libraries, you can support the development and maintenance. +Improve the quality of the libraries by providing issues and Pull Requests, or +donate through PayPal or GitHub sponsors. + +Thank you, + + diff --git a/libraries/RunAvgWeight/RunAvgWeight.cpp b/libraries/RunAvgWeight/RunAvgWeight.cpp new file mode 100644 index 00000000..fcda4447 --- /dev/null +++ b/libraries/RunAvgWeight/RunAvgWeight.cpp @@ -0,0 +1,253 @@ +// +// FILE: RunAvgWeight.cpp +// AUTHOR: Rob Tillaart +// VERSION: 0.1.0 +// DATE: 2024-06-30 +// PURPOSE: Arduino library to calculate the running average with weights by means of a circular buffer +// URL: https://github.com/RobTillaart/RunAvgWeight +// +// based upon: https://github.com/RobTillaart/RunningAverage + + +#include "RunAvgWeight.h" + + +RunAvgWeight::RunAvgWeight(const uint16_t size) +{ + _size = size; + _values = (float*) malloc(_size * sizeof(float)); + _weights = (float *) malloc(_size * sizeof(float)); + if (_values == NULL) _size = 0; + if (_weights == NULL) _size = 0; + clear(); +} + + +RunAvgWeight::~RunAvgWeight() +{ + if (_values != NULL) free(_values); + if (_weights != NULL) free(_weights); +} + + +// resets all counters +void RunAvgWeight::clear() +{ + _count = 0; + _index = 0; + _sumValues = 0.0; + _sumWeights = 0.0; + _min = NAN; + _max = NAN; + for (uint16_t i = _size; i > 0; ) + { + _values[--i] = 0.0; // keeps addValue simpler + _weights[--i] = 0.0; // keeps addValue simpler + } +} + + +// adds a new value to the data-set +void RunAvgWeight::addValue(const float value, const float weight) +{ + if ((_values == NULL) || (_weights == NULL)) + { + return; + } + + _sumValues -= _values[_index]; + _values[_index] = value; + _sumValues += _values[_index]; + + _sumWeights -= _weights[_index]; + _weights[_index] = weight; + _sumWeights += _weights[_index]; + _index++; + + if (_index == _size) _index = 0; // faster than % + + // handle min max + if (_count == 0) _min = _max = value; + else if (value < _min) _min = value; + else if (value > _max) _max = value; + + // update count as last otherwise if ( _count == 0) above will fail + if (_count < _size) _count++; +} + + +float RunAvgWeight::getValue(const uint16_t position) +{ + if (_count == 0) + { + return NAN; + } + if (position >= _count) + { + return NAN; // cannot ask more than is added + } + uint16_t pos = position + _index; + if (pos >= _count) pos -= _count; + return _values[pos]; +} + + +// returns the weight of an element if exist, NAN otherwise +// "partner" of getValue() +float RunAvgWeight::getWeight(const uint16_t position) +{ + if (_count == 0) + { + return NAN; + } + if (position >= _count) + { + return NAN; // cannot ask more than is added + } + uint16_t pos = position + _index; + if (pos >= _count) pos -= _count; + return _weights[pos]; +} + + +// returns the average of the data-set added so far, NAN if no elements. +float RunAvgWeight::getAverage() +{ + if (_count == 0) + { + return NAN; + } + // OPTIMIZE local variable for sums. + _sumValues = 0; + _sumWeights = 0; + for (uint16_t i = 0; i < _count; i++) + { + _sumValues += _values[i] * _weights[i]; + _sumWeights += _weights[i]; + } + return _sumValues / _sumWeights; // multiplication is faster ==> extra admin +} + + +// the larger the size of the internal buffer +// the greater the gain with respect to getAverage() +float RunAvgWeight::getFastAverage() +{ + if (_count == 0) + { + return NAN; + } + + return _sumValues / _sumWeights; +} + + +// returns the minimum value in the buffer +float RunAvgWeight::getMinInBuffer() +{ + if (_count == 0) + { + return NAN; + } + + float _min = _values[0]; + for (uint16_t i = 1; i < _count; i++) + { + if (_values[i] < _min) _min = _values[i]; + } + return _min; +} + + +// returns the maximum value in the buffer +float RunAvgWeight::getMaxInBuffer() +{ + if (_count == 0) + { + return NAN; + } + + float _max = _values[0]; + for (uint16_t i = 1; i < _count; i++) + { + if (_values[i] > _max) _max = _values[i]; + } + return _max; +} + + +// returns the value of an element if exist, NAN otherwise +float RunAvgWeight::getElementValue(uint16_t index) +{ + if (_count == 0) + { + return NAN; + } + return _values[index]; +} + + +// returns the value of an element if exist, NAN otherwise +float RunAvgWeight::getElementWeight(uint16_t index) +{ + if (_count == 0) + { + return NAN; + } + return _weights[index]; +} + + + +// What is standard deviation in weighted average context? +// https://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_variance +// +// https://stackoverflow.com/questions/61831353/how-can-i-calculate-weighted-standard-errors-and-plot-them-in-a-bar-plot +// pop_v = sum( w * (x-mean)^2 ) / sum( w ) +// if the weights are frequency pop_v = sum( w * (x-mean)^2 ) / (sum( w ) - 1) + +/* +weighted.var = function(x,w,type="reliability") { + m=weighted.mean(x,w) + if(type=="frequency"){ return( sum(w*(x-m)^2)/(sum(w)-1) ) } + else { return( sum(w*(x-m)^2)*sum(w)/(sum(w)^2-sum(w^2)) ) } +} +weighted.sem = function(x,w,...) { return( sqrt(weighted.var(x,w,...)*sum(w^2)/sum(w)^2) ) } +*/ + + +// If buffer is empty or has only one element, return NAN. +float RunAvgWeight::getStandardDeviation() +{ + if (_count <= 1) return NAN; + + float average = getFastAverage(); + float variance = 0; + for (uint16_t i = 0; i < _count; i++) + { + float t = _values[i] - average; + variance += t * t * _weights[i]; + } + variance /= _sumWeights; + return sqrt(variance); +} + +// TODO: What is standard error in weighted average context? +// see above. +// If buffer is empty or has only one element, return NAN. +float RunAvgWeight::getStandardError() +{ + float temp = getStandardDeviation(); + if (temp == NAN) return NAN; + + float n; + if (_count >= 30) n = _count; + else n = _count - 1; + temp = temp/sqrt(n); + + return temp; +} + + +// -- END OF FILE -- + diff --git a/libraries/RunAvgWeight/RunAvgWeight.h b/libraries/RunAvgWeight/RunAvgWeight.h new file mode 100644 index 00000000..1a67d017 --- /dev/null +++ b/libraries/RunAvgWeight/RunAvgWeight.h @@ -0,0 +1,80 @@ +#pragma once +// +// FILE: RunAvgWeight.h +// AUTHOR: Rob Tillaart +// VERSION: 0.1.0 +// DATE: 2024-06-30 +// PURPOSE: Arduino library to calculate the running average with weights by means of a circular buffer +// URL: https://github.com/RobTillaart/RunAvgWeight +// +// based upon: https://github.com/RobTillaart/RunningAverage + + +#include "Arduino.h" + + +#define RUNAVGWEIGHT_LIB_VERSION (F("0.1.0")) + + +class RunAvgWeight +{ +public: + explicit RunAvgWeight(const uint16_t size); + ~RunAvgWeight(); + + void clear(); + void addValue(const float value, const float weight = 1.0); + float getValue(const uint16_t position); + float getWeight(const uint16_t position); + + // getAverage() iterates over all elements. + float getAverage(); + // getFastAverage() reuses previous calculated values. + float getFastAverage(); + + // return statistical characteristics of the running average. + // TODO: investigate correctness of these two functions. + // weight == 1 + // weight <> 1 + float getStandardDeviation(); + float getStandardError(); + + // returns min/max added to the data-set since last clear. + // values without weight! + float getMin() { return _min; }; + float getMax() { return _max; }; + + // returns min/max from the values in the internal buffer. + // values without weight! + float getMinInBuffer(); + float getMaxInBuffer(); + + // return true if buffer is full + bool bufferIsFull() { return _count == _size; }; + + // helper functions. + float getElementValue(uint16_t index); + float getElementWeight(uint16_t index); + float getSumValues() { return _sumValues; }; + float getSumWeights() { return _sumWeights; }; + + uint16_t getSize() { return _size; } + uint16_t getCount() { return _count; } + + +protected: + uint16_t _size; + uint16_t _count; + uint16_t _index; + + float * _values; + float * _weights; + float _sumValues; + float _sumWeights; + float _min; + float _max; +}; + + +// -- END OF FILE -- + diff --git a/libraries/RunAvgWeight/examples/raw_demo_equal_weight/raw_demo_equal_weight.ino b/libraries/RunAvgWeight/examples/raw_demo_equal_weight/raw_demo_equal_weight.ino new file mode 100644 index 00000000..e9c092f5 --- /dev/null +++ b/libraries/RunAvgWeight/examples/raw_demo_equal_weight/raw_demo_equal_weight.ino @@ -0,0 +1,59 @@ +// +// FILE: raw_demo_equal_weight.ino +// AUTHOR: Rob Tillaart +// DATE: 2024-06-30 +// PURPOSE: show working of RunAvgWeight with equal weights +// URL: https://github.com/RobTillaart/RunAvgWeight + + +#include "RunAvgWeight.h" + + +RunAvgWeight myRA(10); +int samples = 0; + + +void setup(void) +{ + Serial.begin(115200); + Serial.println(); + Serial.println(__FILE__); + Serial.print("RUNAVGWEIGHT_LIB_VERSION: "); + Serial.println(RUNAVGWEIGHT_LIB_VERSION); + + myRA.clear(); // explicitly start clean + + for (int i = 0; i < 10; i++) + { + myRA.addValue(i * 0.01 + 1 ); + // Serial.print(myRA.getCount()); + // Serial.print("\t"); + // Serial.print(myRA.getAverage(), 3); + // Serial.print("\t"); + Serial.print(myRA.getStandardDeviation(), 3); + Serial.print("\t"); + Serial.println(myRA.getMaxInBuffer(), 3); + } +} + + +void loop(void) +{ + long rn = random(0, 1000); + myRA.addValue(rn * 0.001); + samples++; + Serial.print(samples); + Serial.print("\t Running Average: "); + Serial.println(myRA.getAverage(), 3); + + if (samples == 300) + { + samples = 0; + myRA.clear(); + Serial.println(); + } + delay(10); +} + + +// -- END OF FILE -- diff --git a/libraries/RunAvgWeight/examples/raw_test/raw_test.ino b/libraries/RunAvgWeight/examples/raw_test/raw_test.ino new file mode 100644 index 00000000..79507db9 --- /dev/null +++ b/libraries/RunAvgWeight/examples/raw_test/raw_test.ino @@ -0,0 +1,95 @@ +// +// FILE: raw_test.ino +// AUTHOR: Rob Tillaart +// DATE: 2024-06-30 +// PURPOSE: show working of RunAvgWeight +// URL: https://github.com/RobTillaart/RunAvgWeight + + +#include "RunAvgWeight.h" + + +RunAvgWeight myRA(10); +int samples = 0; + + +void setup(void) +{ + Serial.begin(115200); + Serial.println(); + Serial.println(__FILE__); + Serial.print("RUNAVGWEIGHT_LIB_VERSION: "); + Serial.println(RUNAVGWEIGHT_LIB_VERSION); + Serial.println(); + + myRA.clear(); // explicitly start clean + + for (int i = 0; i < 20; i++) + { + float value = i * 0.01 + 1; + float weight = sqrt(i); + myRA.addValue(value, weight); + Serial.print(myRA.bufferIsFull()); + } + Serial.println(); + Serial.println(); + + for (int i = 0; i < 10; i++) + { + Serial.print(i); + Serial.print("\t"); + Serial.print(myRA.getValue(i), 3); + Serial.print("\t"); + Serial.print(myRA.getWeight(i), 3); + Serial.println(); + } + Serial.println(); + + Serial.print("SIZE:\t"); + Serial.println(myRA.getSize()); + Serial.print("COUNT:\t"); + Serial.println(myRA.getCount()); + Serial.println(); + + Serial.print("AVG:\t"); + Serial.println(myRA.getAverage(), 5); + Serial.print("AVG(f):\t"); + Serial.println(myRA.getFastAverage(), 5); + Serial.println(); + + Serial.print("STDDEV:\t"); + Serial.println(myRA.getStandardDeviation(), 5); + Serial.print("ERROR:\t"); + Serial.println(myRA.getStandardError(), 5); + Serial.println(); + + Serial.print("MIN:\t"); + Serial.println(myRA.getMin(), 3); + Serial.print("MININB:\t"); + Serial.println(myRA.getMinInBuffer(), 3); + Serial.println(); + + Serial.print("MAX:\t"); + Serial.println(myRA.getMax(), 3); + Serial.print("MAXINB:\t"); + Serial.println(myRA.getMaxInBuffer(), 3); + Serial.println(); + + Serial.print("SUMVAL:\t"); + Serial.println(myRA.getSumValues(), 3); + Serial.print("SUMWGT:\t"); + Serial.println(myRA.getSumWeights(), 3); + Serial.print("RATIO:\t"); + Serial.println(myRA.getSumValues() / myRA.getSumWeights(), 3); + Serial.println(); + +} + + +void loop(void) +{ + +} + + +// -- END OF FILE -- diff --git a/libraries/RunAvgWeight/keywords.txt b/libraries/RunAvgWeight/keywords.txt new file mode 100644 index 00000000..5bc337eb --- /dev/null +++ b/libraries/RunAvgWeight/keywords.txt @@ -0,0 +1,39 @@ +# Syntax Colouring Map For RunningAverage + + +# Data types (KEYWORD1) +RunAvgWeight KEYWORD1 + + +# Methods and Functions (KEYWORD2) +clear KEYWORD2 +addValue KEYWORD2 +getValue KEYWORD2 +getWeight KEYWORD2 + +getAverage KEYWORD2 +getFastAverage KEYWORD2 +getStandardDeviation KEYWORD2 +getStandardError KEYWORD2 + +getMin KEYWORD2 +getMax KEYWORD2 +getMinInBuffer KEYWORD2 +getMaxInBuffer KEYWORD2 + +bufferIsFull KEYWORD2 + +getElementValue KEYWORD2 +getElementWeight KEYWORD2 +getSumValues KEYWORD2 +getSumWeights KEYWORD2 + +getSize KEYWORD2 +getCount KEYWORD2 + + +# Instances (KEYWORD2) + + +# Constants (LITERAL1) +RUNAVGWEIGHT_LIB_VERSION LITERAL1 \ No newline at end of file diff --git a/libraries/RunAvgWeight/library.json b/libraries/RunAvgWeight/library.json new file mode 100644 index 00000000..2b21d7e2 --- /dev/null +++ b/libraries/RunAvgWeight/library.json @@ -0,0 +1,23 @@ +{ + "name": "RunAvgWeight", + "keywords": "running average, moving average, average", + "description": "Running Average with weight of N elements. Supports min max average.", + "authors": + [ + { + "name": "Rob Tillaart", + "email": "Rob.Tillaart@gmail.com", + "maintainer": true + } + ], + "repository": + { + "type": "git", + "url": "https://github.com/RobTillaart/RunAvgWeight.git" + }, + "version": "0.1.0", + "license": "MIT", + "frameworks": "*", + "platforms": "*", + "headers": "RunAvgWeight.h" +} diff --git a/libraries/RunAvgWeight/library.properties b/libraries/RunAvgWeight/library.properties new file mode 100644 index 00000000..9599e2eb --- /dev/null +++ b/libraries/RunAvgWeight/library.properties @@ -0,0 +1,11 @@ +name=RunAvgWeight +version=0.1.0 +author=Rob Tillaart +maintainer=Rob Tillaart +sentence=Running Average with weight of N elements. +paragraph=Supports min max average. +category=Data Processing +url=https://github.com/RobTillaart/RunAvgWeight +architectures=* +includes=RunAvgWeight.h +depends= diff --git a/libraries/RunAvgWeight/test/unit_test_001.cpp b/libraries/RunAvgWeight/test/unit_test_001.cpp new file mode 100644 index 00000000..735effbf --- /dev/null +++ b/libraries/RunAvgWeight/test/unit_test_001.cpp @@ -0,0 +1,125 @@ +// +// FILE: unit_test_001.cpp +// AUTHOR: Rob Tillaart +// VERSION: 0.1.0 +// DATE: 2024-06-30 +// PURPOSE: unit tests for RunAvgWeight +// https://github.com/RobTillaart/RunAvgWeight +// + + +// supported assertions +// ---------------------------- +// assertEqual(expected, actual); // a == b +// assertNotEqual(unwanted, actual); // a != b +// assertComparativeEquivalent(expected, actual); // abs(a - b) == 0 or (!(a > b) && !(a < b)) +// assertComparativeNotEquivalent(unwanted, actual); // abs(a - b) > 0 or ((a > b) || (a < b)) +// assertLess(upperBound, actual); // a < b +// assertMore(lowerBound, actual); // a > b +// assertLessOrEqual(upperBound, actual); // a <= b +// assertMoreOrEqual(lowerBound, actual); // a >= b +// assertTrue(actual); +// assertFalse(actual); +// assertNull(actual); + +// // special cases for floats +// assertEqualFloat(expected, actual, epsilon); // fabs(a - b) <= epsilon +// assertNotEqualFloat(unwanted, actual, epsilon); // fabs(a - b) >= epsilon +// assertInfinity(actual); // isinf(a) +// assertNotInfinity(actual); // !isinf(a) +// assertNAN(arg); // isnan(a) +// assertNotNAN(arg); // !isnan(a) + +#include + +#include "Arduino.h" +#include "RunAvgWeight.h" + + +unittest_setup() +{ + fprintf(stderr, "RUNAVGWEIGHT_LIB_VERSION: %s\n", (char *) RUNAVGWEIGHT_LIB_VERSION); +} + + +unittest_teardown() +{ +} + + +unittest(test_zero_elements) +{ + RunAvgWeight myRA(10); + myRA.clear(); + + int size = myRA.getSize(); + assertEqual(10, size); + + int cnt = myRA.getCount(); + assertEqual(0, cnt); + + float x = myRA.getAverage(); + assertNAN(x); +} + + +unittest(test_min_max) +{ + RunAvgWeight myRA(10); + myRA.clear(); + + for (int i = -5; i < 6; i++) + { + myRA.addValue(i); + } + + float mi = myRA.getMin(); + assertEqual(-5, mi); + float ma = myRA.getMax(); + assertEqual(5, ma); + + mi = myRA.getMinInBuffer(); + assertEqual(-4, mi); + ma = myRA.getMaxInBuffer(); + assertEqual(5, ma); +} + + +unittest(test_buffer_full) +{ + RunAvgWeight myRA(10); + myRA.clear(); + assertFalse(myRA.bufferIsFull()); + + for (int i = 0; i < 9; i++) + { + myRA.addValue(i); + assertFalse(myRA.bufferIsFull()); + } + + myRA.addValue(42); + assertTrue(myRA.bufferIsFull()); +} + + +unittest(test_large) +{ + RunAvgWeight myRA(300); + myRA.clear(); + assertFalse(myRA.bufferIsFull()); + + for (int i = 0; i < 299; i++) + { + myRA.addValue(i); + assertFalse(myRA.bufferIsFull()); + } + + myRA.addValue(42); + assertTrue(myRA.bufferIsFull()); +} + + +unittest_main() + + +// --------