diff --git a/.vscode/settings.json b/.vscode/settings.json index 2ef566a..eb2cff6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,6 @@ "stdexcept": "cpp", "limits": "cpp", "functional": "cpp" - } + }, + "github.copilot.nextEditSuggestions.enabled": false } \ No newline at end of file diff --git a/lib/obp60task/OBPRingBuffer.h b/lib/obp60task/OBPRingBuffer.h index d373b0c..1a57266 100644 --- a/lib/obp60task/OBPRingBuffer.h +++ b/lib/obp60task/OBPRingBuffer.h @@ -3,6 +3,7 @@ #include #include #include +#include "WString.h" template class RingBuffer { @@ -18,12 +19,16 @@ private: T MAX_VAL; // highest possible value of buffer of type // 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 int smallest; // Value range of buffer: smallest value - int biggest; // Value range of buffer: biggest value + int largest; // Value range of buffer: biggest value public: RingBuffer(size_t size); + void setMetaData(String name, String format, int updateFrequency, int minValue, int maxValue); // Set meta data for buffer + bool getMetaData(String& name, String& format, int& updateFrequency, int& minValue, int& maxValue); // Get meta data of buffer void add(const T& value); // Add a new value to buffer T get(size_t index) const; // Get value at specific position (0-based index from oldest to newest) T getFirst() const; // Get the first (oldest) value in buffer @@ -39,12 +44,13 @@ public: T getMedian(size_t amount) const; // Get the median value of the last values of buffer size_t getCapacity() const; // Get the buffer capacity (maximum size) size_t getCurrentSize() const; // Get the current number of elements in buffer + size_t getLastIdx() const; // Get the last index of buffer bool isEmpty() const; // Check if buffer is empty bool isFull() const; // Check if buffer is full - T getMinVal(); // Get lowest possible value for buffer; used for initialized buffer data - T getMaxVal(); // Get highest possible value for buffer + T getMinVal() const; // Get lowest possible value for buffer; used for initialized buffer data + T getMaxVal() const; // Get highest possible value for buffer void clear(); // Clear buffer - T operator[](size_t index) const; + T operator[](size_t index); std::vector getAllValues() const; // Operator[] for convenient access (same as get()) }; diff --git a/lib/obp60task/OBPRingBuffer.tpp b/lib/obp60task/OBPRingBuffer.tpp index b966bec..c3814b8 100644 --- a/lib/obp60task/OBPRingBuffer.tpp +++ b/lib/obp60task/OBPRingBuffer.tpp @@ -15,15 +15,50 @@ RingBuffer::RingBuffer(size_t size) MIN_VAL = std::numeric_limits::lowest(); MAX_VAL = std::numeric_limits::max(); + dataName = ""; + dataFmt = ""; + updFreq = MIN_VAL; + smallest = MIN_VAL; + largest = MAX_VAL; buffer.resize(size, MIN_VAL); // return true; } -// Add a new value to the buffer +// Specify meta data of buffer content +template +void RingBuffer::setMetaData(String name, String format, int updateFrequency, int minValue, int maxValue) +{ + dataName = name; + dataFmt = format; + updFreq = updateFrequency; + smallest = minValue; + largest = maxValue; +} + +// Get meta data of buffer content +template +bool RingBuffer::getMetaData(String& name, String& format, int& updateFrequency, int& minValue, int& maxValue) +{ + if (updFreq == MIN_VAL || smallest == MIN_VAL || largest == MAX_VAL) { + return false; // Meta data not set + } + + name = dataName; + format = dataFmt; + updFreq = updFreq; + smallest = smallest; + largest = largest; + return true; // Meta data successfully retrieved +} + +// Add a new value to buffer template void RingBuffer::add(const T& value) { + if (value < smallest || value > largest) { + buffer[head] = MIN_VAL; // Store MIN_VAL if value is out of range + } buffer[head] = value; last = head; @@ -48,7 +83,7 @@ T RingBuffer::get(size_t index) const } if (index < 0 || index >= count) { return MIN_VAL; - // throw std::out_of_range("Index out of range"); + // throw std::out_of_range("Index out of range"); } size_t realIndex = (first + index) % capacity; @@ -57,7 +92,7 @@ T RingBuffer::get(size_t index) const // Operator[] for convenient access (same as get()) template -T RingBuffer::operator[](size_t index) const +T RingBuffer::operator[](size_t index) { return get(index); } @@ -302,6 +337,13 @@ size_t RingBuffer::getCurrentSize() const return count; } +// Get the last index of buffer +template +size_t RingBuffer::getLastIdx() const +{ + return last; +} + // Check if buffer is empty template bool RingBuffer::isEmpty() const @@ -318,14 +360,14 @@ bool RingBuffer::isFull() const // Get lowest possible value for buffer; used for initialized buffer data template -T RingBuffer::getMinVal() +T RingBuffer::getMinVal() const { return MIN_VAL; } // Get highest possible value for buffer template -T RingBuffer::getMaxVal() +T RingBuffer::getMaxVal() const { return MAX_VAL; } diff --git a/lib/obp60task/OBPSensorTask.cpp b/lib/obp60task/OBPSensorTask.cpp index c2f1ac8..90ce62a 100644 --- a/lib/obp60task/OBPSensorTask.cpp +++ b/lib/obp60task/OBPSensorTask.cpp @@ -70,6 +70,12 @@ void sensorTask(void *param){ batV.begin(); batC.begin(); + // Create ring buffers for history storage of some boat data + // later read data types from config and specify buffers accordingly + RingBuffer twdHstry(960); // Circular buffer to store wind direction values; store 960 TWD values for 16 minutes history + RingBuffer twsHstry(960); // Circular buffer to store wind speed values (TWS) + RingBuffer dbtHstry(960); // Circular buffer to store water depth values (DBT) + // Start timer Timer1.start(); // Start Timer1 for blinking LED @@ -361,6 +367,7 @@ void sensorTask(void *param){ long starttime11 = millis(); // Copy GPS data to RTC all 5min long starttime12 = millis(); // Get RTC data all 500ms long starttime13 = millis(); // Get 1Wire sensor data all 2s + unsigned long starttime20 = millis(); // Get TWD and TWS data every 1000ms tN2kMsg N2kMsg; shared->setSensorData(sensors); //set initially read values @@ -370,6 +377,21 @@ void sensorTask(void *param){ GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP); GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop}; + // Prepare boat data values for history storage + // later read data types from config and specify hstryvalList accordingly + GwApi::BoatValue *twdBVal=new GwApi::BoatValue(GwBoatData::_TWD); + GwApi::BoatValue *twsBVal=new GwApi::BoatValue(GwBoatData::_TWS); + GwApi::BoatValue *dbtBVal=new GwApi::BoatValue(GwBoatData::_DBT); + GwApi::BoatValue *hstryValList[]={twdBVal, twsBVal, dbtBVal}; // List of boat values for history storage + int twdHstryMin = 0; + int twsHstryMin = 0; + int dbtHstryMin = 0; + // Initialize history buffers with meta data + api->getBoatDataValues(3,hstryValList); + twdHstry.setMetaData(twdBVal->getName(), twdBVal->getFormat(), 1000, twdHstryMin, 360); // Set meta data for TWD buffer: update frequency 1000ms, min value 0, max value 360 + twsHstry.setMetaData(twsBVal->getName(), twsBVal->getFormat(), 1000, twsHstryMin, 100); // Set meta data for TWS buffer: update frequency 1000ms, min value 0, max value 100 + dbtHstry.setMetaData(dbtBVal->getName(), dbtBVal->getFormat(), 1000, dbtHstryMin, 10928); // Set meta data for TWS buffer: update frequency 1000ms, min value 0, max value 10,928 + // Internal RTC with NTP init ESP32Time rtc(0); if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") { @@ -775,6 +797,39 @@ void sensorTask(void *param){ } } + // Read TWD, TWS, DBT data from boatData every 1000ms for history and windplot display + if(millis() > starttime20 + 1000){ + starttime20 = millis(); + api->getBoatDataValues(3,hstryValList); + int16_t bValue; + if (twdBVal->valid) { + bValue = int16_t(RadToDeg(twdBVal->value)); + twdHstry.add(bValue); + } else { + twdHstry.add(INT16_MIN); // Add invalid value + } + if (twsBVal->valid) { + bValue = int16_t(twsBVal->value); + twsHstry.add(bValue); + } else { + twsHstry.add(INT16_MIN); // Add invalid value + } + if (dbtBVal->valid) { + bValue = int16_t(dbtBVal->value); + dbtHstry.add(bValue); + } else { + dbtHstry.add(INT16_MIN); // Add invalid value + } + String TmpName; + String TmpFormat; + int TmpUpdFreq; + int TmpSmallest; + int TmpBiggest; + twdHstry.getMetaData(TmpName, TmpFormat, TmpUpdFreq, TmpSmallest, TmpBiggest); + api->getLogger()->logDebug(GwLog::ERROR,"History buffer TWD: name:%s format:%s Freq:%d, Min: %d, Max: %d", TmpName.c_str(), TmpFormat.c_str(), TmpUpdFreq, TmpSmallest, TmpBiggest); + api->getLogger()->logDebug(GwLog::ERROR,"History buffers: TWD:%d TWS:%d DBT:%d", twdHstry.getLast(), twsHstry.getLast(), dbtHstry.getLast()); + } + shared->setSensorData(sensors); } vTaskDelete(NULL); diff --git a/lib/obp60task/PageWindPlot.cpp b/lib/obp60task/PageWindPlot.cpp index 80365aa..cc50b58 100644 --- a/lib/obp60task/PageWindPlot.cpp +++ b/lib/obp60task/PageWindPlot.cpp @@ -2,8 +2,8 @@ #include "BoatDataCalibration.h" #include "OBP60Extensions.h" -#include "Pagedata.h" #include "OBPRingBuffer.h" +#include "Pagedata.h" #include @@ -201,12 +201,44 @@ public: } }; +// Get maximum difference of last of TWD ringbuffer values to center chart +int getRng(const RingBuffer& windDirHstry, int center, size_t amount) +{ + int minVal = windDirHstry.getMinVal(); + size_t count = windDirHstry.getCurrentSize(); + size_t capacity = windDirHstry.getCapacity(); + size_t last = windDirHstry.getLastIdx(); + + if (windDirHstry.isEmpty() || amount <= 0) { + return minVal; + } + if (amount > count) + amount = count; + + int value = 0; + int rng = 0; + int maxRng = minVal; + // Start from the newest value (last) and go backwards x times + for (size_t i = 0; i < amount; i++) { + value = windDirHstry.get((last + capacity - i) % capacity); + if (value == minVal) { + continue; + } + rng = abs(((value - center + 540) % 360) - 180); + if (rng > maxRng) + maxRng = rng; + } + if (maxRng > 180) { + maxRng = 180; + } + return maxRng; +} + // **************************************************************** class PageWindPlot : public Page { bool keylock = false; // Keylock - // int16_t lp = 80; // Pointer length - char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both06121990 + char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both int dataIntv = 1; // Update interval for wind history chart: // (1)|(2)|(3)|(4) seconds for approx. 4, 8, 12, 16 min. history chart bool showTWS = true; // Show TWS value in chart area @@ -323,18 +355,18 @@ public: int distVals; // helper to check wndCenter crossing int distMid; // helper to check wndCenter crossing - static RingBuffer windDirHstry(bufSize); // Circular buffer to store wind direction values - static RingBufferwindSpdHstry(bufSize); // Circular buffer to store wind speed values + static RingBuffer windDirHstry(bufSize); // Circular buffer to store wind direction values + static RingBuffer windSpdHstry(bufSize); // Circular buffer to store wind speed values LOG_DEBUG(GwLog::LOG, "Display page WindPlot"); unsigned long start = millis(); // Data initialization if (windDirHstry.getCurrentSize() == 0) { - /* if (!windDirHstry.begin(bufSize)) { - logger->logDebug(GwLog::ERROR, "Failed to initialize wind direction history buffer"); - return; - } */ + /* if (!windDirHstry.begin(bufSize)) { + logger->logDebug(GwLog::ERROR, "Failed to initialize wind direction history buffer"); + return; + } */ simWnd = 0; simTWS = 0; twdValue = 0; @@ -420,13 +452,13 @@ public: // initialize chart range values if (wndCenter == INT_MIN) { - wndCenter = max(0, windDirHstry.get(numWndValues - intvBufSize)); // get 1st value of current data interval + wndCenter = max(0, int(windDirHstry.get(numWndValues - intvBufSize))); // get 1st value of current data interval wndCenter = (int((wndCenter + (wndCenter >= 0 ? 5 : -5)) / 10) * 10) % 360; // Set new center value; round to nearest 10 degree value; 360° -> 0° diffRng = dfltRng; chrtRng = dfltRng; } else { // check and adjust range between left, center, and right chart limit - diffRng = windDirHstry.getRng(wndCenter, numWndValues); + diffRng = getRng(windDirHstry, wndCenter, numWndValues); diffRng = (diffRng == INT_MIN ? 0 : diffRng); if (diffRng > chrtRng) { chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value