From 851149bae6b4ec09da348d9c897349cdd1a549fc Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Sat, 23 Aug 2025 13:43:02 +0200 Subject: [PATCH] Convert invalid marker of ringbuffer to MAX_VAL -> required for unsigned types --- lib/obp60task/OBPDataOperations.cpp | 41 +++++++++++++---------------- lib/obp60task/OBPDataOperations.h | 1 + lib/obp60task/OBPRingBuffer.h | 12 ++++----- lib/obp60task/OBPRingBuffer.tpp | 40 ++++++++++++++-------------- lib/obp60task/PageWindPlot.cpp | 40 +++++++++++++++------------- 5 files changed, 67 insertions(+), 67 deletions(-) diff --git a/lib/obp60task/OBPDataOperations.cpp b/lib/obp60task/OBPDataOperations.cpp index 937a326..73908ac 100644 --- a/lib/obp60task/OBPDataOperations.cpp +++ b/lib/obp60task/OBPDataOperations.cpp @@ -16,7 +16,7 @@ void HstryBuf::init(BoatValueList* boatValues, GwLog *log) { twsHstryMin = hstryMinVal; awdHstryMin = hstryMinVal; awsHstryMin = hstryMinVal; - const double DBL_MIN = std::numeric_limits::lowest(); + const double DBL_MAX = std::numeric_limits::max(); // Initialize history buffers with meta data hstryBufList.twdHstry->setMetaData("TWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax); @@ -33,7 +33,7 @@ void HstryBuf::init(BoatValueList* boatValues, GwLog *log) { if (!awdBVal->valid) { // AWD usually does not exist awdBVal->setFormat(hstryBufList.awdHstry->getFormat()); - awdBVal->value = DBL_MIN; + awdBVal->value = DBL_MAX; } // collect boat values for true wind calculation @@ -221,15 +221,14 @@ double WindUtils::calcHDT(const double* hdmVal, const double* varVal, const doub { double hdt; double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor - static const double DBL_MIN = std::numeric_limits::lowest(); - if (*hdmVal != DBL_MIN) { - hdt = *hdmVal + (*varVal != DBL_MIN ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available) + if (*hdmVal != DBL_MAX) { + hdt = *hdmVal + (*varVal != DBL_MAX ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available) hdt = to2PI(hdt); - } else if (*cogVal != DBL_MIN && *sogVal >= minSogVal) { + } else if (*cogVal != DBL_MAX && *sogVal >= minSogVal) { hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise } else { - hdt = DBL_MIN; // Cannot calculate HDT without valid HDM or HDM+VAR or COG + hdt = DBL_MAX; // Cannot calculate HDT without valid HDM or HDM+VAR or COG } return hdt; @@ -242,24 +241,23 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal, double stw, hdt, ctw; double twd, tws, twa; double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor - static const double DBL_MIN = std::numeric_limits::lowest(); - if (*hdtVal != DBL_MIN) { + if (*hdtVal != DBL_MAX) { hdt = *hdtVal; // Use HDT if available } else { hdt = calcHDT(hdmVal, varVal, cogVal, sogVal); } - if (*cogVal != DBL_MIN && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG + if (*cogVal != DBL_MAX && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG ctw = *cogVal; // Use COG for CTW if available } else { ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code } - if (*stwVal != DBL_MIN) { + if (*stwVal != DBL_MAX) { stw = *stwVal; // Use STW if available - } else if (*sogVal != DBL_MIN) { + } else if (*sogVal != DBL_MAX) { stw = *sogVal; } else { // If STW and SOG are not available, we cannot calculate true wind @@ -267,7 +265,7 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal, } // Serial.println("\ncalcTrueWind: HDT: " + String(hdt) + ", CTW: " + String(ctw) + ", STW: " + String(stw)); - if ((*awaVal == DBL_MIN) || (*awsVal == DBL_MIN)) { + if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) { // Cannot calculate true wind without valid AWA, AWS; other checks are done earlier return false; } else { @@ -288,16 +286,15 @@ bool WindUtils::addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog* log) { double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal; double twd, tws, twa; bool isCalculated = false; - const double DBL_MIN = std::numeric_limits::lowest(); - awaVal = awaBVal->valid ? awaBVal->value : DBL_MIN; - awsVal = awsBVal->valid ? awsBVal->value : DBL_MIN; - cogVal = cogBVal->valid ? cogBVal->value : DBL_MIN; - stwVal = stwBVal->valid ? stwBVal->value : DBL_MIN; - sogVal = sogBVal->valid ? sogBVal->value : DBL_MIN; - hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MIN; - hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MIN; - varVal = varBVal->valid ? varBVal->value : DBL_MIN; + awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX; + awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX; + cogVal = cogBVal->valid ? cogBVal->value : DBL_MAX; + stwVal = stwBVal->valid ? stwBVal->value : DBL_MAX; + sogVal = sogBVal->valid ? sogBVal->value : DBL_MAX; + hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX; + hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX; + varVal = varBVal->valid ? varBVal->value : DBL_MAX; LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852, cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG); diff --git a/lib/obp60task/OBPDataOperations.h b/lib/obp60task/OBPDataOperations.h index 6e7083f..c93c1fe 100644 --- a/lib/obp60task/OBPDataOperations.h +++ b/lib/obp60task/OBPDataOperations.h @@ -54,6 +54,7 @@ class WindUtils { private: GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal; GwApi::BoatValue *awaBVal, *awsBVal, *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal; + static constexpr double DBL_MAX = std::numeric_limits::max(); public: WindUtils(BoatValueList* boatValues){ diff --git a/lib/obp60task/OBPRingBuffer.h b/lib/obp60task/OBPRingBuffer.h index e4b30fa..62e701e 100644 --- a/lib/obp60task/OBPRingBuffer.h +++ b/lib/obp60task/OBPRingBuffer.h @@ -16,16 +16,16 @@ 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 - T MAX_VAL; // highest possible value of buffer of type + T MIN_VAL; // lowest possible value of buffer of type + T MAX_VAL; // highest possible value of buffer of type -> indicates invalid value in buffer mutable SemaphoreHandle_t bufLocker; // metadata for buffer String dataName; // Name of boat data in buffer String dataFmt; // Format of boat data in buffer int updFreq; // Update frequency in milliseconds - T smallest; // Value range of buffer: smallest value - T largest; // Value range of buffer: biggest value + T smallest; // Value range of buffer: smallest value; needs to be => MIN_VAL + T largest; // Value range of buffer: biggest value; needs to be < MAX_VAL, since MAX_VAL indicates invalid entries void initCommon(); @@ -55,8 +55,8 @@ public: size_t getLastIdx() const; // Get the index of newest value in buffer bool isEmpty() const; // Check if buffer is empty bool isFull() const; // Check if buffer is full - T getMinVal() const; // Get lowest possible value for buffer; used for initialized buffer data - T getMaxVal() const; // Get highest possible value for buffer + T getMinVal() const; // Get lowest possible value for buffer + T getMaxVal() const; // Get highest possible value for buffer; used for unset/invalid buffer data void clear(); // Clear buffer void resize(size_t size); // Delete buffer and set new size T operator[](size_t index) const; // Operator[] for convenient access (same as get()) diff --git a/lib/obp60task/OBPRingBuffer.tpp b/lib/obp60task/OBPRingBuffer.tpp index f361ec6..e6951c5 100644 --- a/lib/obp60task/OBPRingBuffer.tpp +++ b/lib/obp60task/OBPRingBuffer.tpp @@ -35,7 +35,7 @@ RingBuffer::RingBuffer(size_t size) , is_Full(false) { initCommon(); - buffer.resize(size, MIN_VAL); + buffer.resize(size, MAX_VAL); // MAX_VAL indicate invalid values } // Specify meta data of buffer content @@ -101,7 +101,7 @@ void RingBuffer::add(const T& value) { GWSYNCHRONIZED(&bufLocker); if (value < smallest || value > largest) { - buffer[head] = MIN_VAL; // Store MIN_VAL if value is out of range + buffer[head] = MAX_VAL; // Store MAX_VAL if value is out of range } else { buffer[head] = value; } @@ -125,7 +125,7 @@ T RingBuffer::get(size_t index) const { GWSYNCHRONIZED(&bufLocker); if (isEmpty() || index < 0 || index >= count) { - return MIN_VAL; + return MAX_VAL; } size_t realIndex = (first + index) % capacity; @@ -144,7 +144,7 @@ template T RingBuffer::getFirst() const { if (isEmpty()) { - return MIN_VAL; + return MAX_VAL; } return get(0); } @@ -154,7 +154,7 @@ template T RingBuffer::getLast() const { if (isEmpty()) { - return MIN_VAL; + return MAX_VAL; } return get(count - 1); } @@ -164,14 +164,14 @@ template T RingBuffer::getMin() const { if (isEmpty()) { - return MIN_VAL; + return MAX_VAL; } T minVal = MAX_VAL; T value; for (size_t i = 0; i < count; i++) { value = get(i); - if (value < minVal && value != MIN_VAL) { + if (value < minVal && value != MAX_VAL) { minVal = value; } } @@ -183,7 +183,7 @@ template T RingBuffer::getMin(size_t amount) const { if (isEmpty() || amount <= 0) { - return MIN_VAL; + return MAX_VAL; } if (amount > count) amount = count; @@ -192,7 +192,7 @@ T RingBuffer::getMin(size_t amount) const T value; for (size_t i = 0; i < amount; i++) { value = get(count - 1 - i); - if (value < minVal && value != MIN_VAL) { + if (value < minVal && value != MAX_VAL) { minVal = value; } } @@ -204,14 +204,14 @@ template T RingBuffer::getMax() const { if (isEmpty()) { - return MIN_VAL; + return MAX_VAL; } T maxVal = MIN_VAL; T value; for (size_t i = 0; i < count; i++) { value = get(i); - if (value > maxVal && value != MIN_VAL) { + if (value > maxVal && value != MAX_VAL) { maxVal = value; } } @@ -223,7 +223,7 @@ template T RingBuffer::getMax(size_t amount) const { if (isEmpty() || amount <= 0) { - return MIN_VAL; + return MAX_VAL; } if (amount > count) amount = count; @@ -232,7 +232,7 @@ T RingBuffer::getMax(size_t amount) const T value; for (size_t i = 0; i < amount; i++) { value = get(count - 1 - i); - if (value > maxVal && value != MIN_VAL) { + if (value > maxVal && value != MAX_VAL) { maxVal = value; } } @@ -244,7 +244,7 @@ template T RingBuffer::getMid() const { if (isEmpty()) { - return MIN_VAL; + return MAX_VAL; } return (getMin() + getMax()) / static_cast(2); @@ -255,7 +255,7 @@ template T RingBuffer::getMid(size_t amount) const { if (isEmpty() || amount <= 0) { - return MIN_VAL; + return MAX_VAL; } if (amount > count) @@ -269,7 +269,7 @@ template T RingBuffer::getMedian() const { if (isEmpty()) { - return MIN_VAL; + return MAX_VAL; } // Create a temporary vector with current valid elements @@ -298,7 +298,7 @@ template T RingBuffer::getMedian(size_t amount) const { if (isEmpty() || amount <= 0) { - return MIN_VAL; + return MAX_VAL; } if (amount > count) amount = count; @@ -366,14 +366,14 @@ bool RingBuffer::isFull() const return is_Full; } -// Get lowest possible value for buffer; used for non-set buffer data +// Get lowest possible value for buffer template T RingBuffer::getMinVal() const { return MIN_VAL; } -// Get highest possible value for buffer +// Get highest possible value for buffer; used for unset/invalid buffer data template T RingBuffer::getMaxVal() const { @@ -405,7 +405,7 @@ void RingBuffer::resize(size_t newSize) is_Full = false; buffer.clear(); - buffer.resize(newSize, MIN_VAL); + buffer.resize(newSize, MAX_VAL); } // Get all current values as a vector diff --git a/lib/obp60task/PageWindPlot.cpp b/lib/obp60task/PageWindPlot.cpp index 8ebd55f..4bc79f7 100644 --- a/lib/obp60task/PageWindPlot.cpp +++ b/lib/obp60task/PageWindPlot.cpp @@ -12,7 +12,7 @@ static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians t // Get maximum difference of last of TWD ringbuffer values to center chart; returns "0" if data is not valid int getCntr(const RingBuffer& windDirHstry, size_t amount) { - int minVal = windDirHstry.getMinVal(); + const int MAX_VAL = windDirHstry.getMaxVal(); size_t count = windDirHstry.getCurrentSize(); if (windDirHstry.isEmpty() || amount <= 0) { @@ -25,7 +25,7 @@ int getCntr(const RingBuffer& windDirHstry, size_t amount) int wndCenter = 0; midWndDir = windDirHstry.getMid(amount); - if (midWndDir != INT16_MIN) { + if (midWndDir != MAX_VAL) { midWndDir = midWndDir / 1000.0 * radToDeg; wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value minWndDir = windDirHstry.getMin(amount) / 1000.0 * radToDeg; @@ -42,10 +42,11 @@ int getCntr(const RingBuffer& windDirHstry, size_t amount) int getRng(const RingBuffer& windDirHstry, int center, size_t amount) { int minVal = windDirHstry.getMinVal(); + const int MAX_VAL = windDirHstry.getMaxVal(); size_t count = windDirHstry.getCurrentSize(); if (windDirHstry.isEmpty() || amount <= 0) { - return minVal; + return MAX_VAL; } if (amount > count) amount = count; @@ -57,8 +58,8 @@ int getRng(const RingBuffer& windDirHstry, int center, size_t amount) for (size_t i = 0; i < amount; i++) { value = windDirHstry.get(count - 1 - i); - if (value == minVal) { - continue; + if (value == MAX_VAL) { + continue; // ignore invalid values } value = value / 1000.0 * radToDeg; @@ -70,7 +71,7 @@ int getRng(const RingBuffer& windDirHstry, int center, size_t amount) maxRng = 180; } - return maxRng; + return (maxRng != minVal ? maxRng : MAX_VAL); } // **************************************************************** @@ -104,7 +105,7 @@ public: virtual void setupKeys() { Page::setupKeys(); - // commonData->keydata[0].label = "MODE"; + // commonData->keydata[0].label = "MODE"; #if defined BOARD_OBP60S3 commonData->keydata[1].label = "SRC"; commonData->keydata[4].label = "INTV"; @@ -184,6 +185,7 @@ public: static RingBuffer* wsHstry; // Wind speed data buffer static String wdName, wdFormat; // Wind direction name and format static String wsName, wsFormat; // Wind speed name and format + static int16_t wdMAX_VAL; // Max. value of wd history buffer, indicating invalid values float wsValue; // Wind speed value in chart area String wsUnit; // Wind speed unit in chart area static GwApi::BoatValue* wsBVal = new GwApi::BoatValue("TWS"); // temp BoatValue for wind speed unit identification; required by OBP60Formater @@ -240,7 +242,7 @@ public: oldDataIntv = 0; wsValue = 0; numAddedBufVals, currIdx, lastIdx = 0; - wndCenter = INT_MIN; + wndCenter = INT_MAX; midWndDir = 0; diffRng = dfltRng; chrtRng = dfltRng; @@ -270,6 +272,7 @@ public: } wdHstry->getMetaData(wdName, wdFormat); wsHstry->getMetaData(wsName, wsFormat); + wdMAX_VAL = wdHstry->getMaxVal(); bufSize = wdHstry->getCapacity(); wsBVal->setFormat(wsHstry->getFormat()); lastAddedIdx = wdHstry->getLastIdx(); @@ -300,14 +303,14 @@ public: showTruW ? "True" : "App"); // Set wndCenter from 1st real buffer value - if (wndCenter == INT_MIN || (wndCenter == 0 && count == 1)) { + if (wndCenter == INT_MAX || (wndCenter == 0 && count == 1)) { wndCenter = getCntr(*wdHstry, numWndVals); - LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, xWD: %.1f, wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", count, wdHstry->getLast() / 10000.0 * radToDeg, - wndCenter, diffRng, chrtRng, wdHstry->getMin(numWndVals) / 10000.0 * radToDeg, wdHstry->getMax(numWndVals) / 10000.0 * radToDeg); + LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, xWD: %.1f, wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", count, wdHstry->getLast() / 1000.0 * radToDeg, + wndCenter, diffRng, chrtRng, wdHstry->getMin(numWndVals) / 1000.0 * radToDeg, wdHstry->getMax(numWndVals) / 1000.0 * radToDeg); } else { // check and adjust range between left, center, and right chart limit diffRng = getRng(*wdHstry, wndCenter, numWndVals); - diffRng = (diffRng == INT16_MIN ? 0 : diffRng); + diffRng = (diffRng == wdMAX_VAL ? 0 : diffRng); if (diffRng > chrtRng) { chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value } else if (diffRng + 10 < chrtRng) { // Reduce chart range for higher resolution if possible @@ -355,11 +358,11 @@ public: getdisplay().drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // symbol getdisplay().drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // symbol - if (wdHstry->getMax() == wdHstry->getMinVal()) { - // only values in buffer -> no valid wind data available + if (wdHstry->getMax() == wdMAX_VAL) { + // only values in buffer -> no valid wind data available wndDataValid = false; } else if (!BDataValid[0] && !useSimuData) { - // currently no valid TWD data available and no simulation mode + // currently no valid xWD data available and no simulation mode numNoData++; wndDataValid = true; if (numNoData > 3) { @@ -375,8 +378,8 @@ public: if (wndDataValid) { for (int i = 0; i < (numWndVals / dataIntv); i++) { chrtVal = static_cast(wdHstry->get(bufStart + (i * dataIntv))); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer - if (chrtVal == INT16_MIN) { - chrtPrevVal = INT16_MIN; + if (chrtVal == wdMAX_VAL) { + chrtPrevVal = wdMAX_VAL; } else { chrtVal = static_cast((chrtVal / 1000.0 * radToDeg) + 0.5); // Convert to degrees and round x = ((chrtVal - wndLeft + 360) % 360) * chrtScl; @@ -385,7 +388,7 @@ public: if (i >= (numWndVals / dataIntv) - 1) // log chart data of 1 line (adjust for test purposes) LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv)); - if ((i == 0) || (chrtPrevVal == INT16_MIN)) { + if ((i == 0) || (chrtPrevVal == wdMAX_VAL)) { // just a dot for 1st chart point or after some invalid values prevX = x; prevY = y; @@ -517,7 +520,6 @@ PageDescription registerPageWindPlot( "WindPlot", // Page name createPage, // Action 0, // Number of bus values depends on selection in Web configuration -// { "TWD", "TWS", "AWD", "AWS" }, // Bus values we need in the page { "TWD", "AWD" }, // Bus values we need in the page true // Show display header on/off );