Add method for reliable identification of mid value for angle buffer values to RingBuffer; fix getMedian()

This commit is contained in:
Ulrich Meine
2026-07-04 18:27:19 +02:00
parent 92ee3a0c4f
commit 53cf497a70
2 changed files with 76 additions and 6 deletions
+2
View File
@@ -80,6 +80,8 @@ public:
double getMax(size_t amount) const; // Get maximum value of the last <amount> values of buffer double getMax(size_t amount) const; // Get maximum value of the last <amount> values of buffer
double getMid() const; // Get mid value between <min> and <max> value in buffer double getMid() const; // Get mid value between <min> and <max> value in buffer
double getMid(size_t amount) const; // Get mid value between <min> and <max> value of the last <amount> values of buffer double getMid(size_t amount) const; // Get mid value between <min> and <max> value of the last <amount> values of buffer
double getCircularMid() const; // Get mid value of circle (degree) values of buffer
double getCircularMid(size_t amount) const; // Get mid value of circle (degree) values of the last <amount> values of buffer
double getMedian() const; // Get the median value in buffer double getMedian() const; // Get the median value in buffer
double getMedian(size_t amount) const; // Get the median value of the last <amount> values of buffer double getMedian(size_t amount) const; // Get the median value of the last <amount> values of buffer
size_t getCapacity() const; // Get the buffer capacity (maximum size) size_t getCapacity() const; // Get the buffer capacity (maximum size)
+74 -6
View File
@@ -1,4 +1,3 @@
#include "OBPRingBuffer.h"
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
#include <cmath> #include <cmath>
@@ -59,7 +58,7 @@ void RingBuffer<T>::setMetaData(String name, String format, int updateFrequency,
BUFMIN_VAL = static_cast<double>(NUMLIMIT_LOW) / mltplr; // lowest possible buffer value; converted to external view BUFMIN_VAL = static_cast<double>(NUMLIMIT_LOW) / mltplr; // lowest possible buffer value; converted to external view
BUFMAX_VAL = static_cast<double>(NUMLIMIT_HIGH) / mltplr; // highest possible buffer value; converted to external view BUFMAX_VAL = static_cast<double>(NUMLIMIT_HIGH) / mltplr; // highest possible buffer value; converted to external view
lowest = std::max(BUFMIN_VAL, minValue); // low value range, set by user lowest = std::max(BUFMIN_VAL, minValue); // low value range, set by user
highest = std::min(std::nextafter(BUFMAX_VAL, -std::numeric_limits<double>::infinity()), maxValue); // high value range, set by user; max. is 1 tick smaller than BUFMAX_VAL highest = std::min(std::nextafter(BUFMAX_VAL, -std::numeric_limits<double>::infinity()), maxValue); // high value range, set by user; maximum is 1 tick lower than BUFMAX_VAL
} }
// Specify format of buffer content // Specify format of buffer content
@@ -285,7 +284,7 @@ double RingBuffer<T>::getMedian(size_t amount) const
amount = count; amount = count;
// Create a temporary vector with current valid elements // Create a temporary vector with current valid elements
std::vector<T> temp; std::vector<double> temp;
temp.reserve(amount); temp.reserve(amount);
for (size_t i = 0; i < amount; i++) { for (size_t i = 0; i < amount; i++) {
@@ -295,16 +294,85 @@ double RingBuffer<T>::getMedian(size_t amount) const
// Sort to find median // Sort to find median
std::sort(temp.begin(), temp.end()); std::sort(temp.begin(), temp.end());
if (temp[0] == BUFMAX_VAL) { // 1st element of sorted vector is already BUFMAX_VAL -> only invalid entries in buffer, so we return invalid value
return BUFMAX_VAL;
}
if (amount % 2 == 1) { if (amount % 2 == 1) {
// Odd number of elements // Odd number of elements
return static_cast<double>(temp[amount / 2]); return temp[amount / 2];
} else { } else {
// Even number of elements - return average of middle two // Even number of elements - return average of middle two
// Note: For integer types, this truncates. For floating point, it's exact. return (temp[amount / 2 - 1] + temp[amount / 2]) / 2;
return static_cast<double>((temp[amount / 2 - 1] + temp[amount / 2]) / 2);
} }
} }
// Get mid value of circle (degree) values of buffer
template <typename T>
double RingBuffer<T>::getCircularMid() const
{
return getCircularMid(getCurrentSize());
}
// Get mid value of circle (degree) values of the last <amount> values of buffer
template <typename T>
double RingBuffer<T>::getCircularMid(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return BUFMAX_VAL;
}
if (amount > count)
amount = count;
std::vector<double> a;
// Create a temporary vector with current valid elements
std::vector<double> temp;
temp.reserve(amount);
for (size_t i = 0; i < amount; i++) {
temp.push_back(get(count - 1 - i));
}
// Sort to find largest gap
std::sort(temp.begin(), temp.end());
if (temp[0] == BUFMAX_VAL) { // 1st element of sorted vector is already BUFMAX_VAL -> only invalid entries in buffer, so we return invalid value
return BUFMAX_VAL;
}
// Find the largest gap
double largestGap = BUFMIN_VAL;
std::size_t gapIndex = 0;
for (std::size_t i = 0; i < temp.size(); ++i)
{
std::size_t next = (i + 1) % temp.size();
double gap;
if (next == 0)
gap = (temp[0] + M_TWOPI) - temp[i];
else
gap = temp[next] - temp[i];
if (gap > largestGap)
{
largestGap = gap;
gapIndex = i;
}
}
double start = temp[(gapIndex + 1) % temp.size()]; // Start of occupied arc = first angle after largest gap
double arcWidth = M_TWOPI - largestGap; // Width of occupied arc
arcWidth = start + arcWidth / 2.0;
arcWidth = fmod(arcWidth, M_TWOPI);
if (arcWidth < 0.0) {
arcWidth += M_TWOPI;
}
return arcWidth; // Midpoint of occupied arc
// return WindUtils::to2PI(start + arcWidth / 2.0); // Midpoint of occupied arc
}
// Get the buffer capacity (maximum size) // Get the buffer capacity (maximum size)
template <typename T> template <typename T>
size_t RingBuffer<T>::getCapacity() const size_t RingBuffer<T>::getCapacity() const