GY-63_MS5611/libraries/RunningMedian/RunningMedian.cpp

375 lines
7.5 KiB
C++
Raw Normal View History

2011-10-09 16:24:29 -04:00
//
// FILE: RunningMedian.cpp
2021-01-29 06:31:58 -05:00
// AUTHOR: Rob Tillaart
2023-07-13 08:27:45 -04:00
// VERSION: 0.3.8
2011-10-09 16:24:29 -04:00
// PURPOSE: RunningMedian library for Arduino
//
2022-11-06 04:24:44 -05:00
// HISTORY: see changelog.md
2021-01-29 06:31:58 -05:00
2011-10-09 16:24:29 -04:00
#include "RunningMedian.h"
2021-01-29 06:31:58 -05:00
RunningMedian::RunningMedian(const uint8_t size)
2011-10-09 16:24:29 -04:00
{
2021-01-29 06:31:58 -05:00
_size = size;
if (_size < MEDIAN_MIN_SIZE) _size = MEDIAN_MIN_SIZE;
2022-06-06 04:32:23 -04:00
#if !RUNNING_MEDIAN_USE_MALLOC
if (_size > MEDIAN_MAX_SIZE) _size = MEDIAN_MAX_SIZE;
#endif
2022-06-06 04:32:23 -04:00
#if RUNNING_MEDIAN_USE_MALLOC
2021-01-29 06:31:58 -05:00
_values = (float *) malloc(_size * sizeof(float));
_sortIdx = (uint8_t *) malloc(_size * sizeof(uint8_t));
#endif
clear();
2011-10-09 16:24:29 -04:00
}
2021-01-29 06:31:58 -05:00
RunningMedian::~RunningMedian()
2013-08-17 08:54:10 -04:00
{
2022-06-06 04:32:23 -04:00
#if RUNNING_MEDIAN_USE_MALLOC
2021-01-29 06:31:58 -05:00
free(_values);
free(_sortIdx);
#endif
2013-08-17 08:54:10 -04:00
}
2021-01-29 06:31:58 -05:00
2022-11-06 04:24:44 -05:00
// resets all internal counters
2011-10-09 16:24:29 -04:00
void RunningMedian::clear()
{
2021-01-29 06:31:58 -05:00
_count = 0;
_index = 0;
_sorted = false;
2020-11-27 05:33:55 -05:00
for (uint8_t i = 0; i < _size; i++)
{
2021-01-29 06:31:58 -05:00
_sortIdx[i] = i;
2020-11-27 05:33:55 -05:00
}
2011-10-09 16:24:29 -04:00
}
2021-01-29 06:31:58 -05:00
2022-11-06 04:24:44 -05:00
// adds a new value to the data-set
// or overwrites the oldest if full.
void RunningMedian::add(float value)
2011-10-09 16:24:29 -04:00
{
2021-01-29 06:31:58 -05:00
_values[_index++] = value;
2022-11-06 04:24:44 -05:00
if (_index >= _size) _index = 0; // wrap around
2021-01-29 06:31:58 -05:00
if (_count < _size) _count++;
_sorted = false;
2011-10-09 16:24:29 -04:00
}
2021-01-29 06:31:58 -05:00
float RunningMedian::getMedian()
2011-10-09 16:24:29 -04:00
{
2021-01-29 06:31:58 -05:00
if (_count == 0) return NAN;
if (_sorted == false) sort();
2022-11-06 04:24:44 -05:00
if (_count & 0x01) // is it odd sized?
2020-11-27 05:33:55 -05:00
{
2021-01-29 06:31:58 -05:00
return _values[_sortIdx[_count / 2]];
2020-11-27 05:33:55 -05:00
}
2021-01-29 06:31:58 -05:00
return (_values[_sortIdx[_count / 2]] + _values[_sortIdx[_count / 2 - 1]]) / 2;
2020-11-27 05:33:55 -05:00
}
2021-01-29 06:31:58 -05:00
2021-12-28 04:25:17 -05:00
float RunningMedian::getQuantile(float quantile)
2020-11-27 05:33:55 -05:00
{
2021-01-29 06:31:58 -05:00
if (_count == 0) return NAN;
2021-12-28 04:25:17 -05:00
if ((quantile < 0) || (quantile > 1)) return NAN;
2020-11-27 05:33:55 -05:00
if (_sorted == false) sort();
2021-12-28 04:25:17 -05:00
const float index = (_count - 1) * quantile;
const uint8_t lo = floor(index);
const uint8_t hi = ceil(index);
const float qs = _values[_sortIdx[lo]];
const float h = (index - lo);
2020-11-27 05:33:55 -05:00
2021-01-29 06:31:58 -05:00
return (1.0 - h) * qs + h * _values[_sortIdx[hi]];
2011-10-09 16:24:29 -04:00
}
2021-01-29 06:31:58 -05:00
float RunningMedian::getAverage()
{
2021-01-29 06:31:58 -05:00
if (_count == 0) return NAN;
2013-08-17 08:54:10 -04:00
float sum = 0;
2021-01-29 06:31:58 -05:00
for (uint8_t i = 0; i < _count; i++)
2020-11-27 05:33:55 -05:00
{
2021-01-29 06:31:58 -05:00
sum += _values[i];
2020-11-27 05:33:55 -05:00
}
2021-01-29 06:31:58 -05:00
return sum / _count;
}
2013-08-17 08:54:10 -04:00
2021-01-29 06:31:58 -05:00
float RunningMedian::getAverage(uint8_t nMedians)
2013-08-17 08:54:10 -04:00
{
2021-01-29 06:31:58 -05:00
if ((_count == 0) || (nMedians == 0)) return NAN;
2022-11-06 04:24:44 -05:00
// when filling the array for first time
if (_count < nMedians) nMedians = _count;
2021-01-29 06:31:58 -05:00
uint8_t start = ((_count - nMedians) / 2);
uint8_t stop = start + nMedians;
if (_sorted == false) sort();
float sum = 0;
2020-11-27 05:33:55 -05:00
for (uint8_t i = start; i < stop; i++)
{
2021-01-29 06:31:58 -05:00
sum += _values[_sortIdx[i]];
2020-11-27 05:33:55 -05:00
}
return sum / nMedians;
2013-08-17 08:54:10 -04:00
}
2021-01-29 06:31:58 -05:00
2023-07-13 08:27:45 -04:00
// nMedians is the spread, or the middle N
// this version compensated for bias #22
float RunningMedian::getMedianAverage(uint8_t nMedians)
{
// handle special cases.
if ((_count == 0) || (nMedians == 0)) return NAN;
if (_count == 1) return _values[0];
if (_count == 2) return (_values[0] + _values[1]) * 0.5;
// nMedians can not be larger than current nr of elements.
if (_count <= nMedians) return getAverage();
// _count is at least 3 from here
// Eliminate the bias when the nMedians would fall slightly
// to the left or right of the centre.
// If count and nMedians are not both odd or both even reduce
// the spread by 1 to make them the same.
// If nMedians becomes 0 correct this. to 2.
if ((_count & 0x01) != (nMedians & 0x01))
{
--nMedians;
// nmedians can not become 0
if (nMedians == 0) nMedians = 2;
}
uint8_t start = (_count - nMedians) / 2;
uint8_t stop = start + nMedians;
if (_sorted == false) sort();
float sum = 0;
for (uint8_t i = start; i < stop; i++)
{
sum += _values[_sortIdx[i]];
}
return sum / nMedians;
}
float RunningMedian::getElement(const uint8_t n)
{
2021-01-29 06:31:58 -05:00
if ((_count == 0) || (n >= _count)) return NAN;
2021-01-29 06:31:58 -05:00
uint8_t pos = _index + n;
2022-11-06 04:24:44 -05:00
if (pos >= _count) // faster than %
{
2021-01-29 06:31:58 -05:00
pos -= _count;
}
2021-01-29 06:31:58 -05:00
return _values[pos];
}
2021-01-29 06:31:58 -05:00
float RunningMedian::getSortedElement(const uint8_t n)
{
2021-01-29 06:31:58 -05:00
if ((_count == 0) || (n >= _count)) return NAN;
if (_sorted == false) sort();
2021-01-29 06:31:58 -05:00
return _values[_sortIdx[n]];
}
2021-01-29 06:31:58 -05:00
2022-11-06 04:24:44 -05:00
// n can be max <= half the (filled) size
float RunningMedian::predict(const uint8_t n)
{
2021-01-29 06:31:58 -05:00
uint8_t mid = _count / 2;
if ((_count == 0) || (n >= mid)) return NAN;
2022-11-06 04:24:44 -05:00
float med = getMedian(); // takes care of sorting !
if (_count & 0x01) // odd # elements
{
2021-01-29 06:31:58 -05:00
return max(med - _values[_sortIdx[mid - n]], _values[_sortIdx[mid + n]] - med);
}
2022-11-06 04:24:44 -05:00
// even # elements
2021-01-29 06:31:58 -05:00
float f1 = (_values[_sortIdx[mid - n]] + _values[_sortIdx[mid - n - 1]]) / 2;
float f2 = (_values[_sortIdx[mid + n]] + _values[_sortIdx[mid + n - 1]]) / 2;
2020-11-27 05:33:55 -05:00
return max(med - f1, f2 - med) / 2;
}
2021-01-29 06:31:58 -05:00
2022-11-06 04:24:44 -05:00
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
2011-10-09 16:24:29 -04:00
void RunningMedian::sort()
{
2021-12-28 04:25:17 -05:00
// insertSort
2022-11-06 04:24:44 -05:00
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()
{
2021-01-29 06:31:58 -05:00
for (uint16_t i = 1; i < _count; i++)
{
2021-01-29 06:31:58 -05:00
uint16_t z = i;
uint16_t temp = _sortIdx[z];
while ((z > 0) && (_values[temp] < _values[_sortIdx[z - 1]]))
{
2021-01-29 06:31:58 -05:00
_sortIdx[z] = _sortIdx[z - 1];
z--;
}
2021-01-29 06:31:58 -05:00
_sortIdx[z] = temp;
}
_sorted = true;
2011-10-09 16:24:29 -04:00
2022-11-06 04:24:44 -05:00
// // 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");
}
*/
2021-12-28 04:25:17 -05:00
2020-11-27 05:33:55 -05:00
// -- END OF FILE --
2021-12-28 04:25:17 -05:00