Improve handling of invalid values in OBPringbuffer; avoid some double coding

This commit is contained in:
Ulrich Meine
2026-06-21 02:15:16 +02:00
parent c17f8238a4
commit 63af2807fb
2 changed files with 42 additions and 100 deletions
+5 -5
View File
@@ -45,9 +45,9 @@ private:
size_t last; // Points to the last (newest) valid element
size_t count; // Number of valid elements currently in buffer
bool is_Full; // Indicates that all buffer elements are used and ringing is in use
T MIN_VAL; // lowest possible value of buffer of type <T>
T MAX_VAL; // highest possible value of buffer of type <T> -> indicates invalid value in buffer
double dblMIN_VAL, dblMAX_VAL; // MIN_VAL, MAX_VAL in double format
T NUMLIMIT_LOW; // internally lowest possible value of buffer of type <T>
T NUMLIMIT_HIGH; // internally highest possible value of buffer of type <T>
double BUFMIN_VAL, BUFMAX_VAL; // lowest/highest possible buffer value considering multiplier -> externally used
mutable SemaphoreHandle_t bufLocker;
// metadata for buffer
@@ -55,8 +55,8 @@ private:
String dataFmt; // Format of boat data in buffer
int updFreq; // Update frequency in milliseconds
double mltplr; // Multiplier which transforms original <double> value into buffer type format
double smallest; // Value range of buffer: smallest value; needs to be => MIN_VAL
double largest; // Value range of buffer: biggest value; needs to be < MAX_VAL, since MAX_VAL indicates invalid entries
double lowest; // low value range for boat data in this buffer; needs to be => BUFMIN_VAL
double highest; // high value range for boat data in this buffer; needs to be < BUFMAX_VAL, since BUFMAX_VAL indicates invalid entries
void initCommon();
+37 -95
View File
@@ -6,16 +6,16 @@
template <typename T>
void RingBuffer<T>::initCommon()
{
MIN_VAL = std::numeric_limits<T>::lowest();
MAX_VAL = std::numeric_limits<T>::max();
dblMIN_VAL = static_cast<double>(MIN_VAL);
dblMAX_VAL = static_cast<double>(MAX_VAL);
NUMLIMIT_LOW = std::numeric_limits<T>::lowest();
NUMLIMIT_HIGH = std::numeric_limits<T>::max();
dataName = "";
dataFmt = "";
updFreq = -1;
mltplr = 1;
smallest = dblMIN_VAL;
largest = dblMAX_VAL;
BUFMIN_VAL = static_cast<double>(NUMLIMIT_LOW);
BUFMAX_VAL = static_cast<double>(NUMLIMIT_HIGH);
lowest = BUFMIN_VAL;
highest = BUFMAX_VAL;
bufLocker = xSemaphoreCreateMutex();
}
@@ -44,7 +44,7 @@ RingBuffer<T>::RingBuffer(size_t size)
initCommon();
buffer.reserve(size);
buffer.resize(size, MAX_VAL); // MAX_VAL indicate invalid values
buffer.resize(size, NUMLIMIT_HIGH); // NUMLIMIT_HIGH indicate invalid values
}
// Specify meta data of buffer content
@@ -56,8 +56,10 @@ void RingBuffer<T>::setMetaData(String name, String format, int updateFrequency,
dataFmt = format;
updFreq = updateFrequency;
mltplr = multiplier;
smallest = std::max(dblMIN_VAL, minValue);
largest = std::min(dblMAX_VAL, maxValue);
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
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
}
// Specify format of buffer content
@@ -81,8 +83,8 @@ bool RingBuffer<T>::getMetaData(String& name, String& format, int& updateFrequen
format = dataFmt;
updateFrequency = updFreq;
multiplier = mltplr;
minValue = smallest;
maxValue = largest;
minValue = lowest;
maxValue = highest;
return true;
}
@@ -126,8 +128,8 @@ template <typename T>
void RingBuffer<T>::add(const double& value)
{
GWSYNCHRONIZED(&bufLocker);
if (value < smallest || value > largest) {
buffer[head] = MAX_VAL; // Store MAX_VAL if value is out of range
if (value < lowest || value > highest) {
buffer[head] = NUMLIMIT_HIGH; // Store maximum buffer value if data value is out of range
} else {
buffer[head] = static_cast<T>(std::round(value * mltplr));
}
@@ -151,11 +153,11 @@ double RingBuffer<T>::get(size_t index) const
{
GWSYNCHRONIZED(&bufLocker);
if (isEmpty() || index < 0 || index >= count) {
return dblMAX_VAL;
return BUFMAX_VAL;
}
size_t realIndex = (first + index) % capacity;
return static_cast<double>(buffer[realIndex] / mltplr);
return static_cast<double>(buffer[realIndex] / mltplr); // is BUFMAX_VAL if value is invalid
}
// Operator[] for convenient access (same as get())
@@ -170,7 +172,7 @@ template <typename T>
double RingBuffer<T>::getFirst() const
{
if (isEmpty()) {
return dblMAX_VAL;
return BUFMAX_VAL;
}
return get(0);
}
@@ -180,7 +182,7 @@ template <typename T>
double RingBuffer<T>::getLast() const
{
if (isEmpty()) {
return dblMAX_VAL;
return BUFMAX_VAL;
}
return get(count - 1);
}
@@ -189,19 +191,7 @@ double RingBuffer<T>::getLast() const
template <typename T>
double RingBuffer<T>::getMin() const
{
if (isEmpty()) {
return dblMAX_VAL;
}
double minVal = dblMAX_VAL;
double value;
for (size_t i = 0; i < count; i++) {
value = get(i);
if (value < minVal && value != dblMAX_VAL) {
minVal = value;
}
}
return minVal;
return getMin(getCurrentSize());
}
// Get minimum value of the last <amount> values of buffer
@@ -209,16 +199,16 @@ template <typename T>
double RingBuffer<T>::getMin(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return dblMAX_VAL;
return BUFMAX_VAL;
}
if (amount > count)
amount = count;
double minVal = dblMAX_VAL;
double minVal = BUFMAX_VAL;
double value;
for (size_t i = 0; i < amount; i++) {
value = get(count - 1 - i);
if (value < minVal && value != dblMAX_VAL) {
if (value < minVal && value != BUFMAX_VAL) {
minVal = value;
}
}
@@ -229,22 +219,7 @@ double RingBuffer<T>::getMin(size_t amount) const
template <typename T>
double RingBuffer<T>::getMax() const
{
if (isEmpty()) {
return dblMAX_VAL;
}
double maxVal = dblMIN_VAL;
double value;
for (size_t i = 0; i < count; i++) {
value = get(i);
if (value > maxVal && value != dblMAX_VAL) {
maxVal = value;
}
}
if (maxVal == dblMIN_VAL) { // no change of initial value -> buffer has only invalid values (MAX_VAL)
maxVal = dblMAX_VAL;
}
return maxVal;
return getMax(getCurrentSize());
}
// Get maximum value of the last <amount> values of buffer
@@ -252,21 +227,21 @@ template <typename T>
double RingBuffer<T>::getMax(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return dblMAX_VAL;
return BUFMAX_VAL;
}
if (amount > count)
amount = count;
double maxVal = dblMIN_VAL;
double maxVal = BUFMIN_VAL;
double value;
for (size_t i = 0; i < amount; i++) {
value = get(count - 1 - i);
if (value > maxVal && value != dblMAX_VAL) {
if (value > maxVal && value != BUFMAX_VAL) {
maxVal = value;
}
}
if (maxVal == dblMIN_VAL) { // no change of initial value -> buffer has only invalid values (MAX_VAL)
maxVal = dblMAX_VAL;
if (maxVal == BUFMIN_VAL) { // no change of initial value -> buffer has only invalid values (BUFMAX_VAL)
maxVal = BUFMAX_VAL;
}
return maxVal;
}
@@ -275,11 +250,7 @@ double RingBuffer<T>::getMax(size_t amount) const
template <typename T>
double RingBuffer<T>::getMid() const
{
if (isEmpty()) {
return dblMAX_VAL;
}
return (getMin() + getMax()) / 2;
return getMid(getCurrentSize());
}
// Get mid value between <min> and <max> value of the last <amount> values of buffer
@@ -287,7 +258,7 @@ template <typename T>
double RingBuffer<T>::getMid(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return dblMAX_VAL;
return BUFMAX_VAL;
}
if (amount > count)
@@ -300,29 +271,7 @@ double RingBuffer<T>::getMid(size_t amount) const
template <typename T>
double RingBuffer<T>::getMedian() const
{
if (isEmpty()) {
return dblMAX_VAL;
}
// Create a temporary vector with current valid elements
std::vector<T> temp;
temp.reserve(count);
for (size_t i = 0; i < count; i++) {
temp.push_back(get(i));
}
// Sort to find median
std::sort(temp.begin(), temp.end());
if (count % 2 == 1) {
// Odd number of elements
return static_cast<double>(temp[count / 2]);
} else {
// Even number of elements - return average of middle two
// Note: For integer types, this truncates. For floating point, it's exact.
return static_cast<double>((temp[count / 2 - 1] + temp[count / 2]) / 2);
}
return getMedian(getCurrentSize());
}
// Get the median value of the last <amount> values of buffer
@@ -330,7 +279,7 @@ template <typename T>
double RingBuffer<T>::getMedian(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return dblMAX_VAL;
return BUFMAX_VAL;
}
if (amount > count)
amount = count;
@@ -402,14 +351,14 @@ bool RingBuffer<T>::isFull() const
template <typename T>
double RingBuffer<T>::getMinVal() const
{
return dblMIN_VAL;
return BUFMIN_VAL;
}
// Get highest possible value for buffer; used for unset/invalid buffer data
template <typename T>
double RingBuffer<T>::getMaxVal() const
{
return dblMAX_VAL;
return BUFMAX_VAL;
}
// Clear buffer
@@ -438,21 +387,14 @@ void RingBuffer<T>::resize(size_t newSize)
buffer.clear();
buffer.reserve(newSize);
buffer.resize(newSize, MAX_VAL);
buffer.resize(newSize, NUMLIMIT_HIGH);
}
// Get all current values in native buffer format as a vector
template <typename T>
std::vector<double> RingBuffer<T>::getAllValues() const
{
std::vector<double> result;
result.reserve(count);
for (size_t i = 0; i < count; i++) {
result.push_back(get(i));
}
return result;
return getAllValues(getCurrentSize());
}
// Get last <amount> values in native buffer format as a vector