From 7a10a4e925bd5723597b2b591ad3ab302fe4a94c Mon Sep 17 00:00:00 2001 From: Rob Tillaart Date: Sun, 6 Nov 2022 10:08:49 +0100 Subject: [PATCH] improve internal sorting (#21) * improve internal sorting * add setSearchMode() * update readme --- .arduino-ci.yml | 16 ++ CHANGELOG.md | 110 ++++++++++ README.md | 65 ++++-- RunningMedian.cpp | 204 ++++++++++++++---- RunningMedian.h | 62 +++--- .../RunningMedian_performance.ino | 113 ++++++++++ .../performance_0.3.6.txt | 12 ++ .../performance_0.3.7.txt | 26 +++ keywords.txt | 10 +- library.json | 2 +- library.properties | 2 +- test/unit_test_001.cpp | 14 +- 12 files changed, 538 insertions(+), 98 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 examples/RunningMedian_performance/RunningMedian_performance.ino create mode 100644 examples/RunningMedian_performance/performance_0.3.6.txt create mode 100644 examples/RunningMedian_performance/performance_0.3.7.txt diff --git a/.arduino-ci.yml b/.arduino-ci.yml index 3e49662..fd05073 100644 --- a/.arduino-ci.yml +++ b/.arduino-ci.yml @@ -1,3 +1,18 @@ +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 platforms: @@ -9,3 +24,4 @@ compile: - esp32 - esp8266 - mega2560 + - rpipico diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7dfd9e7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,110 @@ +# Change Log RunningMedian + +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.3.7] - 2022-10-28 +- Add RP2040 support to build-CI. +- Add CHANGELOG.md +- optimized binary insertion sort - see issue #20 (idea thanks to acicuc) +- add performance test sketch (to test sort efficiency) +- add setSearchMode() for selecting fastest median search mode. + EXPERIMENTAL, select between LINEAR or BINARY search. + + +## [0.3.6} - 2022-06-06 +- bump version for platformio + +## [0.3.5} - 2022-06-05 +- configuration options, +- fixed static version not working + +## [0.3.4} - 2021-12-28 +- update library.json, +- update readme.md, +- license, minor edits + +## [0.3.3} - 2021-01-22 +- better insertionSort (+ clean up test code) + +## [0.3.2} - 2021-01-21 +- replaced bubbleSort by insertionSort + better performance for large arrays. + +## [0.3.1} - 2021-01-16 +- Changed size parameter to 255 max + +## [0.3.0} - 2021-01-04 +- malloc() memory as default storage + +---- + +## [0.2.2] - 2021-01-03 +- add Arduino-CI + unit tests + +## [0.2.1] - 2020-06-19 +- fix library.json + +## [0.2.0] - 2020-04-16 +- refactor. + +---- + +## [0.1.15] - 2018-08-24 +- make runningMedian Configurable #110 + +## [0.1.14] - 2017-07-26 +- revert double to float - issue #33 + +## [0.1.13] - 2015-10-30 +- fix getElement(n) - kudos to Gdunge + +## [0.1.12] - 2015-07-12 +- refactor constructor + const + +## [0.1.11] - 2015-03-29 +- undo 0.1.10 fix clear + +## [0.1.10] - 2015-03-07 +- fix clear + +## [0.1.09] - 2014-11-25 +- float to double (support ARM) + +## [0.1.08] - 2013-10-20 +- add getElement(), +- add getSottedElement() +- add predict() + +## [0.1.07] - 2013-10-19 +- add correct median if \_count is even. + +## [0.1.06] - 2013-10-19 +- faster sort, +- dynamic arrays, +- replaced sorted float array with indirection array + +## [0.1.05] - 2013-10-18 +- fixed bug in sort; +- removes default constructor; +- dynamic memory + +## [0.1.04] - 2013-10-17 +- added getAverage(uint8_t) - kudo's to Sembazuru + +## [0.1.03] - 2013-09-30 +- added \_sorted flag, +- minor refactor + +## [0.1.02] - 2012-03-15 +- added ?? + +## [0.1.01] - 2011-02-22 +- added remarks from CodingBadly + +## [0.1.00] - 2011-02-16 +- initial version + diff --git a/README.md b/README.md index f2ca1a0..38d87f5 100644 --- a/README.md +++ b/README.md @@ -46,14 +46,15 @@ is performance wise O(100x) faster in sorting than 255 elements. ### Note: Configurable Options There are several options that can be configured via defines at compile time, those being: -- RUNNING_MEDIAN_USE_MALLOC: bool - - true (default): Dynamic memory allocation is used for the buffer. - - false: Static buffers of size MEDIAN_MAX_SIZE are used. -- MEDIAN_MIN_SIZE: uint - - Dynamic / Static: The buffer stores at least this many items. -- MEDIAN_MAX_SIZE: uint - - Dynamic: Not used. - - Static: The buffer stores at most this many items. +- **RUNNING_MEDIAN_USE_MALLOC**: bool + - true (default): Dynamic memory allocation is used for the buffer. + - false: Static buffers of size MEDIAN_MAX_SIZE are used. +- **MEDIAN_MIN_SIZE**: uint8_t + - Dynamic / Static: The buffer stores at least this many items. + - should be minimal 3. +- **MEDIAN_MAX_SIZE**: uint8_t + - Dynamic: Not used. + - Static: The buffer stores at most this many items. ## Interface @@ -62,9 +63,9 @@ There are several options that can be configured via defines at compile time, th ### Constructor - **RunningMedian(const uint8_t size)** Constructor, dynamically allocates memory. -- **~RunningMedian()** Destructor -- **uint8_t getSize()** returns size of internal array -- **uint8_t getCount()** returns current used elements, getCount() <= getSize() +- **~RunningMedian()** Destructor. +- **uint8_t getSize()** returns size of internal array. +- **uint8_t getCount()** returns current used elements, getCount() <= getSize(). - **bool isFull()** returns true if the internal buffer is 100% filled. @@ -72,9 +73,9 @@ There are several options that can be configured via defines at compile time, th - **clear()** resets internal buffer and variables, effectively empty the buffer. - **add(const float value)** adds a new value to internal buffer, -optionally replacing the oldest element if the buffer is full -- **float getMedian()** returns the median == middle element -- **float getAverage()** returns average of **all** the values in the internal buffer +optionally replacing the oldest element if the buffer is full. +- **float getMedian()** returns the median == middle element. +- **float getAverage()** returns average of **all** the values in the internal buffer. - **float getAverage(uint8_t nMedian)** returns average of **the middle n** values. This effectively removes noise from the outliers in the samples. - **float getHighest()** get the largest values in the buffer. @@ -86,19 +87,41 @@ This value is often interpolated. ### Less used functions - **float getElement(const uint8_t n)** returns the n'th element from the values in time order. -- **float getSortedElement(const uint8_t n)** returns the n'th element from the values in size order (sorted ascending) +- **float getSortedElement(const uint8_t n)** returns the n'th element from the values in size order (sorted ascending). - **float predict(const uint8_t n)** predict the maximum change of median after n additions, -n must be smaller than **getSize()/2** +n must be smaller than **getSize()/2**. + + +### SearchMode optimization + +Since 0.3.7 the internal sort has been optimized. +It is now possible to select between LINEAR (=0) and BINARY (=1) insertion sort. +Pre-0.3.7 used linear insertion sort, and the new linear version is slightly optimized. +For larger internal arrays the performance gain of BINARY mode is substantial. + +- **void setSearchMode(uint8_t searchMode = 0)** 0 = linear, 1 = binary - see table below. +Other values will set the searchMode to linear. +- **uint8_t getSearchMode()** returns the set mode + +| searchMode | value | notes | +|:------------:|:-------:|:-------| +| LINEAR | 0 | fastest for smaller internal buffers (default) +| BINARY | 1 | faster for larger internal buffers + +Depends on the board / clock used where the methods are equally fast. + +Give it a try, and let me know your. ## Operation -See examples +See examples. ## Future -- improve documentation -- check for optimizations -- separate releaseNotes.md -- +- improve documentation. +- check for optimizations. + - get the median without (full) sorting. QuickSelect() +- move all code to .cpp file + diff --git a/RunningMedian.cpp b/RunningMedian.cpp index da8d803..dfcdd21 100644 --- a/RunningMedian.cpp +++ b/RunningMedian.cpp @@ -1,38 +1,10 @@ // // FILE: RunningMedian.cpp // AUTHOR: Rob Tillaart -// VERSION: 0.3.6 +// VERSION: 0.3.7 // PURPOSE: RunningMedian library for Arduino // -// HISTORY: -// 0.1.00 2011-02-16 initial version -// 0.1.01 2011-02-22 added remarks from CodingBadly -// 0.1.02 2012-03-15 added -// 0.1.03 2013-09-30 added _sorted flag, minor refactor -// 0.1.04 2013-10-17 added getAverage(uint8_t) - kudo's to Sembazuru -// 0.1.05 2013-10-18 fixed bug in sort; removes default constructor; dynamic memory -// 0.1.06 2013-10-19 faster sort, dynamic arrays, replaced sorted float array with indirection array -// 0.1.07 2013-10-19 add correct median if _count is even. -// 0.1.08 2013-10-20 add getElement(), add getSottedElement() add predict() -// 0.1.09 2014-11-25 float to double (support ARM) -// 0.1.10 2015-03-07 fix clear -// 0.1.11 2015-03-29 undo 0.1.10 fix clear -// 0.1.12 2015-07-12 refactor constructor + const -// 0.1.13 2015-10-30 fix getElement(n) - kudos to Gdunge -// 0.1.14 2017-07-26 revert double to float - issue #33 -// 0.1.15 2018-08-24 make runningMedian Configurable #110 -// 0.2.0 2020-04-16 refactor. -// 0.2.1 2020-06-19 fix library.json -// 0.2.2 2021-01-03 add Arduino-CI + unit tests -// 0.3.0 2021-01-04 malloc memory as default storage -// 0.3.1 2021-01-16 Changed size parameter to 255 max -// 0.3.2 2021-01-21 replaced bubbleSort by insertionSort -// --> better performance for large arrays. -// 0.3.3 2021-01-22 better insertionSort (+ clean up test code) -// 0.3.4 2021-12-28 update library.json, readme, license, minor edits -// 0.3.5 2022-06-05 configuration options, fixed static version not working -// 0.3.6 2022-06-06 bump version for platformio - +// HISTORY: see changelog.md #include "RunningMedian.h" @@ -63,7 +35,7 @@ RunningMedian::~RunningMedian() } -// resets all internal counters +// resets all internal counters void RunningMedian::clear() { _count = 0; @@ -76,12 +48,12 @@ void RunningMedian::clear() } -// adds a new value to the data-set -// or overwrites the oldest if full. +// adds a new value to the data-set +// or overwrites the oldest if full. void RunningMedian::add(float value) { _values[_index++] = value; - if (_index >= _size) _index = 0; // wrap around + if (_index >= _size) _index = 0; // wrap around if (_count < _size) _count++; _sorted = false; } @@ -93,7 +65,7 @@ float RunningMedian::getMedian() if (_sorted == false) sort(); - if (_count & 0x01) // is it odd sized? + if (_count & 0x01) // is it odd sized? { return _values[_sortIdx[_count / 2]]; } @@ -136,7 +108,9 @@ float RunningMedian::getAverage(uint8_t nMedians) { if ((_count == 0) || (nMedians == 0)) return NAN; - if (_count < nMedians) nMedians = _count; // when filling the array for first time +// when filling the array for first time + if (_count < nMedians) nMedians = _count; + uint8_t start = ((_count - nMedians) / 2); uint8_t stop = start + nMedians; @@ -156,7 +130,7 @@ float RunningMedian::getElement(const uint8_t n) if ((_count == 0) || (n >= _count)) return NAN; uint8_t pos = _index + n; - if (pos >= _count) // faster than % + if (pos >= _count) // faster than % { pos -= _count; } @@ -173,27 +147,164 @@ float RunningMedian::getSortedElement(const uint8_t n) } -// n can be max <= half the (filled) size +// n can be max <= half the (filled) size float RunningMedian::predict(const uint8_t n) { uint8_t mid = _count / 2; if ((_count == 0) || (n >= mid)) return NAN; - float med = getMedian(); // takes care of sorting ! - if (_count & 0x01) // odd # elements + float med = getMedian(); // takes care of sorting ! + if (_count & 0x01) // odd # elements { return max(med - _values[_sortIdx[mid - n]], _values[_sortIdx[mid + n]] - med); } - // even # elements + // even # elements float f1 = (_values[_sortIdx[mid - n]] + _values[_sortIdx[mid - n - 1]]) / 2; float f2 = (_values[_sortIdx[mid + n]] + _values[_sortIdx[mid + n - 1]]) / 2; return max(med - f1, f2 - med) / 2; } +void RunningMedian::setSearchMode(uint8_t searchMode) +{ + if (searchMode == 1) _searchMode = 1; + else _searchMode = 0; +} + + +uint8_t RunningMedian::getSearchMode() +{ + return _searchMode; +} + + +//////////////////////////////////////////////////////////// +// +// PRIVATE +// + +// insertion sort - _searchMode = linear or binary. + +void RunningMedian::sort() +{ + uint16_t lo = 0; + uint16_t hi = 0; + uint16_t mi = 0; + uint16_t temp = 0; + + for (uint16_t i = 1; i < _count; i++) + { + temp = _sortIdx[i]; + float f = _values[temp]; + + // handle special case f is smaller than all elements first. + // only one compare needed, improves linear search too. + if (f <= _values[_sortIdx[0]]) + { + hi = 0; + } + else + { + if (_searchMode == 0) + { + hi = i; + // find insertion point with linear search + while ((hi > 0) && (f < _values[_sortIdx[hi - 1]])) + { + hi--; + } + } + else if (_searchMode == 1) + { + // find insertion point with binary search + lo = 0; + hi = i; + // be aware there might be duplicates + while (hi - lo > 1) + { + mi = (lo + hi) / 2; + if (f < _values[_sortIdx[mi]]) + { + hi = mi; + } + else + { + lo = mi; + } + } + } + } + + // move elements to make space + uint16_t k = i; + while (k > hi) + { + _sortIdx[k] = _sortIdx[k - 1]; + k--; + } + + // insert at right spot. + _sortIdx[k] = temp; + + } + _sorted = true; + + // // verify sorted + // for (int i = 0; i < _count; i++) + // { + // if (i%5 == 0) Serial.println(); + // Serial.print("\t"); + // Serial.print(_values[_sortIdx[i]]); + // } + // Serial.println("\n"); +} + + + +/* + split version of pre-0.3.7 sort - bit faster + void RunningMedian::sort() { // insertSort + for (uint16_t i = 1; i < _count; i++) + { + uint16_t hi = i; + uint16_t temp = _sortIdx[hi]; + float f = _values[temp]; + while ((hi > 0) && (f < _values[_sortIdx[hi - 1]])) + { + hi--; + } + + // move elements to make space + uint16_t k = i; + while (k > hi) + { + _sortIdx[k] = _sortIdx[k - 1]; + k--; + } + + // insert at right spot. + _sortIdx[k] = temp; + } + _sorted = true; + // // verify sorted + // for (int i = 0; i < _count; i++) + // { + // if (i%5 == 0) Serial.println(); + // Serial.print("\t"); + // Serial.print(_values[_sortIdx[i]]); + // } + // Serial.println("\n"); +} +*/ + + +/* +// straightforward insertion sort - PRE-0.3.7 +void RunningMedian::sort() +{ for (uint16_t i = 1; i < _count; i++) { uint16_t z = i; @@ -206,8 +317,17 @@ void RunningMedian::sort() _sortIdx[z] = temp; } _sorted = true; -} + // // verify sorted + // for (int i = 0; i < _count; i++) + // { + // if (i%5 == 0) Serial.println(); + // Serial.print("\t"); + // Serial.print(_values[_sortIdx[i]]); + // } + // Serial.println("\n"); +} +*/ // -- END OF FILE -- diff --git a/RunningMedian.h b/RunningMedian.h index f30fbb0..9a2a23c 100644 --- a/RunningMedian.h +++ b/RunningMedian.h @@ -3,7 +3,7 @@ // FILE: RunningMedian.h // AUTHOR: Rob Tillaart // PURPOSE: RunningMedian library for Arduino -// VERSION: 0.3.6 +// VERSION: 0.3.7 // URL: https://github.com/RobTillaart/RunningMedian // URL: http://arduino.cc/playground/Main/RunningMedian // HISTORY: See RunningMedian.cpp @@ -12,16 +12,16 @@ #include "Arduino.h" -#define RUNNING_MEDIAN_VERSION (F("0.3.6")) +#define RUNNING_MEDIAN_VERSION (F("0.3.7")) -// fall back to fixed storage for dynamic version => remove true +// fall back to fixed storage for dynamic version => remove true #ifndef RUNNING_MEDIAN_USE_MALLOC #define RUNNING_MEDIAN_USE_MALLOC true #endif -// MEDIAN_MIN_SIZE should at least be 3 to be practical, +// MEDIAN_MIN_SIZE should at least be 3 to be practical, #ifndef MEDIAN_MIN_SIZE #define MEDIAN_MIN_SIZE 3 #endif @@ -29,10 +29,10 @@ #ifndef MEDIAN_MAX_SIZE #ifdef RUNNING_MEDIAN_USE_MALLOC -// max 250 to not overflow uint8_t internal variables +// max 250 to not overflow uint8_t internal variables #define MEDIAN_MAX_SIZE 255 #else -// using fixed memory will be limited to 19 elements. +// using fixed memory will be limited to 19 elements. #define MEDIAN_MAX_SIZE 19 #endif #endif @@ -41,52 +41,59 @@ class RunningMedian { public: - // # elements in the internal buffer - // odd sizes results in a 'real' middle element and will be a bit faster. - // even sizes takes the average of the two middle elements as median + // # elements in the internal buffer + // odd sizes results in a 'real' middle element and will be a bit faster. + // even sizes takes the average of the two middle elements as median explicit RunningMedian(const uint8_t size); ~RunningMedian(); - // resets internal buffer and variables + // resets internal buffer and variables void clear(); - // adds a new value to internal buffer, optionally replacing the oldest element. + // adds a new value to internal buffer, optionally replacing the oldest element. void add(const float value); - // returns the median == middle element + // returns the median == middle element float getMedian(); - - // returns the Quantile + + // returns the Quantile float getQuantile(const float quantile); - // returns average of the values in the internal buffer + // returns average of the values in the internal buffer float getAverage(); - // returns average of the middle nMedian values, removes noise from outliers + // returns average of the middle nMedian values, removes noise from outliers float getAverage(uint8_t nMedian); float getHighest() { return getSortedElement(_count - 1); }; float getLowest() { return getSortedElement(0); }; - // get n-th element from the values in time order + // get n-th element from the values in time order float getElement(const uint8_t n); - // get n-th element from the values in size order + // get n-th element from the values in size order float getSortedElement(const uint8_t n); - // predict the max change of median after n additions + // predict the max change of median after n additions float predict(const uint8_t n); uint8_t getSize() { return _size; }; - // returns current used elements, getCount() <= getSize() + // returns current used elements, getCount() <= getSize() uint8_t getCount() { return _count; }; bool isFull() { return (_count == _size); } + // EXPERIMENTAL (might change in the future) + // searchMode defines how the internal insertionSort works + // can be used to optimize performance. + // 0 = LINEAR_SEARCH 1 = BINARY_SEARCH + void setSearchMode(uint8_t searchMode = 0); + uint8_t getSearchMode(); + protected: - boolean _sorted; // _sortIdx{} is up to date - uint8_t _size; // max number of values - uint8_t _count; // current number of values - uint8_t _index; // next index to add. + boolean _sorted; // _sortIdx{} is up to date + uint8_t _size; // max number of values + uint8_t _count; // current number of values <= size + uint8_t _index; // next index to add - // _values holds the elements themself - // _p holds the index for sorted + // _values holds the elements themself + // _sortIdx holds the index for sorted #if RUNNING_MEDIAN_USE_MALLOC float * _values; uint8_t * _sortIdx; @@ -94,7 +101,8 @@ class RunningMedian float _values[MEDIAN_MAX_SIZE]; uint8_t _sortIdx[MEDIAN_MAX_SIZE]; #endif - void sort(); + void sort(); + uint8_t _searchMode = 0; }; diff --git a/examples/RunningMedian_performance/RunningMedian_performance.ino b/examples/RunningMedian_performance/RunningMedian_performance.ino new file mode 100644 index 0000000..2bbfc8f --- /dev/null +++ b/examples/RunningMedian_performance/RunningMedian_performance.ino @@ -0,0 +1,113 @@ +// +// FILE: runningMedian_performance.ino +// AUTHOR: Rob Tillaart +// PURPOSE: test performance +// URL: https://github.com/RobTillaart/RunningMedian + +#include + +// 50 consecutive samples from Sharp distance sensor model GP2Y0A710K0F while stationary. +const int sourceData[] = +{ + // test array + // 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + // 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + // 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + // 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + + 300, 299, 296, 343, 307, 304, 303, 305, 300, 340, + 308, 305, 300, 304, 311, 304, 300, 300, 304, 304, + 284, 319, 306, 304, 300, 302, 305, 310, 306, 304, + 308, 300, 299, 304, 300, 305, 307, 303, 326, 311, + 306, 304, 305, 300, 300, 307, 302, 305, 296, 300 +}; + +const int sourceSize = (sizeof(sourceData) / sizeof(sourceData[0])); + +// RunningMedian samples = RunningMedian(sourceSize); +RunningMedian samples = RunningMedian(sourceSize / 2); + +void setup() +{ + Serial.begin(115200); + while (!Serial); // Wait for serial port to connect. Needed for Leonardo + MKR1010. + delay(1000); + Serial.print(F("Running Median Version: ")); + Serial.println(RUNNING_MEDIAN_VERSION); + +#ifdef RUNNING_MEDIAN_USE_MALLOC + Serial.println(F("Dynamic version using malloc() enabled")); +#else + Serial.print(F("Static version, will always allocate an array of ")); + Serial.print(MEDIAN_MAX_SIZE, DEC); + Serial.println(F(" floats.")); +#endif + + samples.setSearchMode(0); + test1(); + + samples.setSearchMode(1); + test1(); + + Serial.println("\ndone..\n"); +} + +void loop() +{ +} + +void test1() +{ + uint32_t start = 0; + uint32_t stop = 0; + uint32_t total = 0; + + samples.clear(); + Serial.println(); + Serial.print(F("Allocated size = ")); + Serial.println(samples.getSize()); + Serial.print(F("nr of elements = ")); + Serial.println(samples.getCount()); + Serial.print(F(" searchMode = ")); + Serial.println(samples.getSearchMode()); + Serial.println(); + delay(50); + + for (uint8_t i = 0; i < sourceSize; i++) + { + start = micros(); + samples.add(sourceData[i]); + total += (micros() - start); + } + Serial.print(F("50 x add: ")); + Serial.println(total); + Serial.print(F(" avg: ")); + Serial.println(total / 50.0); + delay(100); + + // time to access the data + start = micros(); + float result = samples.getMedian(); + stop = micros(); + Serial.print(F(" 1 x get: ")); + Serial.print(stop - start); + Serial.println(F(" == sorting")); + Serial.print( " median: "); + Serial.println(result); + delay(100); + + // time to access the data + start = micros(); + result = samples.getMedian(); + stop = micros(); + Serial.print(F(" 1 x get: ")); + Serial.print(stop - start); + Serial.println(F(" == no sorting")); + Serial.print( " median: "); + Serial.println(result); + delay(100); + +} + +// -- END OF FILE -- diff --git a/examples/RunningMedian_performance/performance_0.3.6.txt b/examples/RunningMedian_performance/performance_0.3.6.txt new file mode 100644 index 0000000..8b785ce --- /dev/null +++ b/examples/RunningMedian_performance/performance_0.3.6.txt @@ -0,0 +1,12 @@ +Running Median Version: 0.3.6 +Dynamic version using malloc() enabled +Allocated size = 25 + +50 x add: 568 + avg: 11.36 + 1 x get: 1304 == sorting + median: 304.00 + 1 x get: 8 == no sorting + median: 304.00 + +done.. diff --git a/examples/RunningMedian_performance/performance_0.3.7.txt b/examples/RunningMedian_performance/performance_0.3.7.txt new file mode 100644 index 0000000..7287764 --- /dev/null +++ b/examples/RunningMedian_performance/performance_0.3.7.txt @@ -0,0 +1,26 @@ +Running Median Version: 0.3.7 +Dynamic version using malloc() enabled + +Allocated size = 25 +nr of elements = 0 + searchMode = 0 + +50 x add: 532 + avg: 10.64 + 1 x get: 1144 == sorting + median: 304.00 + 1 x get: 12 == no sorting + median: 304.00 + +Allocated size = 25 +nr of elements = 0 + searchMode = 1 + +50 x add: 524 + avg: 10.48 + 1 x get: 792 == sorting + median: 304.00 + 1 x get: 8 == no sorting + median: 304.00 + +done.. \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index 24f6d73..7bd6768 100644 --- a/keywords.txt +++ b/keywords.txt @@ -15,14 +15,18 @@ getAverage KEYWORD2 getHighest KEYWORD2 getLowest KEYWORD2 -getSize KEYWORD2 -getCount KEYWORD2 -isFull KEYWORD2 getElement KEYWORD2 getSortedElement KEYWORD2 predict KEYWORD2 +getSize KEYWORD2 +getCount KEYWORD2 +isFull KEYWORD2 + +setSearchMode KEYWORD2 +getSearchMode KEYWORD2 + # Constants (LITERAL1) RUNNING_MEDIAN_VERSION LITERAL1 diff --git a/library.json b/library.json index e2f26ab..2b817c5 100644 --- a/library.json +++ b/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/RunningMedian.git" }, - "version": "0.3.6", + "version": "0.3.7", "license": "MIT", "frameworks": "arduino", "platforms": "*", diff --git a/library.properties b/library.properties index 713a4f2..ee73bff 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=RunningMedian -version=0.3.6 +version=0.3.7 author=Rob Tillaart maintainer=Rob Tillaart sentence=The library stores the last N individual values in a buffer to select the median. diff --git a/test/unit_test_001.cpp b/test/unit_test_001.cpp index 05095e5..3b35b1a 100644 --- a/test/unit_test_001.cpp +++ b/test/unit_test_001.cpp @@ -37,24 +37,32 @@ #include "RunningMedian.h" - unittest_setup() { fprintf(stderr, "RUNNING_MEDIAN_VERSION: %s\n", (char *) RUNNING_MEDIAN_VERSION); } + unittest_teardown() { } +unittest(test_constants) +{ + assertEqual(true, RUNNING_MEDIAN_USE_MALLOC); + assertEqual(255, MEDIAN_MAX_SIZE); + assertEqual( 3, MEDIAN_MIN_SIZE); +} + + unittest(test_constructor) { RunningMedian samples = RunningMedian(5); assertEqual(5, samples.getSize()); assertEqual(0, samples.getCount()); - // TODO default values? + // TODO default values? } @@ -75,7 +83,7 @@ unittest(test_basic_add) assertEqualFloat(00, samples.getLowest(), 0.0001); assertEqualFloat(40, samples.getHighest(), 0.0001); - samples.add(100); // 6th element + samples.add(100); // 6th element assertEqual(5, samples.getSize()); assertEqual(5, samples.getCount());