From 59cf52b5d2a469944f4d6f5cc0a4c576b935b222 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Sun, 13 Jul 2025 00:26:16 +0200 Subject: [PATCH] Semaphore + chart fixes; added simulation data --- .vscode/settings.json | 9 +- lib/obp60task/OBPRingBuffer.h | 15 +- lib/obp60task/OBPRingBuffer.tpp | 37 +- lib/obp60task/OBPSensorTask.cpp | 66 ++-- lib/obp60task/OBPSensorTask.h | 3 +- lib/obp60task/PageWindPlot.cpp | 597 ++++++++++++-------------------- lib/obp60task/obp60task.cpp | 6 +- 7 files changed, 289 insertions(+), 444 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 55a7256..cad7657 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,3 @@ { - "cmake.configureOnOpen": false, - "files.associations": { - "stdexcept": "cpp", - "limits": "cpp", - "functional": "cpp", - "*.tpp": "cpp" - }, - "github.copilot.nextEditSuggestions.enabled": false + "cmake.configureOnOpen": false } \ No newline at end of file diff --git a/lib/obp60task/OBPRingBuffer.h b/lib/obp60task/OBPRingBuffer.h index 7d4067f..3aba92b 100644 --- a/lib/obp60task/OBPRingBuffer.h +++ b/lib/obp60task/OBPRingBuffer.h @@ -9,7 +9,7 @@ template class RingBuffer { private: - SemaphoreHandle_t locker; + mutable SemaphoreHandle_t bufLocker; std::vector buffer; size_t capacity; size_t head; // Points to the next insertion position @@ -24,13 +24,13 @@ private: 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 largest; // Value range of buffer: biggest value + T smallest; // Value range of buffer: smallest value + T 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 setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer + bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& 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 @@ -45,13 +45,14 @@ 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 + size_t getFirstIdx() const; // Get the index of oldest value in buffer + 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 void clear(); // Clear buffer - T operator[](size_t index); // Operator[] for convenient access (same as get()) + T operator[](size_t index) const; // Operator[] for convenient access (same as get()) std::vector getAllValues() const; // Get all current values as a vector }; diff --git a/lib/obp60task/OBPRingBuffer.tpp b/lib/obp60task/OBPRingBuffer.tpp index e5589fd..662bfcb 100644 --- a/lib/obp60task/OBPRingBuffer.tpp +++ b/lib/obp60task/OBPRingBuffer.tpp @@ -9,6 +9,8 @@ RingBuffer::RingBuffer(size_t size) , count(0) , is_Full(false) { + bufLocker=xSemaphoreCreateMutex(); + if (size == 0) { // return false; } @@ -17,7 +19,7 @@ RingBuffer::RingBuffer(size_t size) MAX_VAL = std::numeric_limits::max(); dataName = ""; dataFmt = ""; - updFreq = MIN_VAL; + updFreq = -1; smallest = MIN_VAL; largest = MAX_VAL; @@ -28,20 +30,22 @@ RingBuffer::RingBuffer(size_t size) // Specify meta data of buffer content template -void RingBuffer::setMetaData(String name, String format, int updateFrequency, int minValue, int maxValue) +void RingBuffer::setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue) { + GWSYNCHRONIZED(&bufLocker); dataName = name; dataFmt = format; updFreq = updateFrequency; - smallest = minValue; - largest = maxValue; + smallest = std::max(MIN_VAL, minValue); + largest = std::min(MAX_VAL, maxValue); } // Get meta data of buffer content template -bool RingBuffer::getMetaData(String& name, String& format, int& updateFrequency, int& minValue, int& maxValue) +bool RingBuffer::getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue) { - if (updFreq == MIN_VAL || smallest == MIN_VAL || largest == MAX_VAL) { + GWSYNCHRONIZED(&bufLocker); + if (dataName == "" || dataFmt == "" || updFreq == -1) { return false; // Meta data not set } @@ -50,17 +54,19 @@ bool RingBuffer::getMetaData(String& name, String& format, int& updateFrequen updateFrequency = updFreq; minValue = smallest; maxValue = largest; - return true; // Meta data successfully retrieved + return true; } // Add a new value to buffer template 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 + } else { + buffer[head] = value; } - buffer[head] = value; last = head; if (is_Full) { @@ -79,6 +85,7 @@ void RingBuffer::add(const T& value) template T RingBuffer::get(size_t index) const { + GWSYNCHRONIZED(&bufLocker); if (isEmpty() || index < 0 || index >= count) { return MIN_VAL; } @@ -89,7 +96,7 @@ T RingBuffer::get(size_t index) const // Operator[] for convenient access (same as get()) template -T RingBuffer::operator[](size_t index) +T RingBuffer::operator[](size_t index) const { return get(index); } @@ -101,7 +108,7 @@ T RingBuffer::getFirst() const if (isEmpty()) { return MIN_VAL; } - return buffer[first]; + return get(first); } // Get the last (newest) value in the buffer @@ -111,7 +118,7 @@ T RingBuffer::getLast() const if (isEmpty()) { return MIN_VAL; } - return buffer[last]; + return get(last); } // Get the lowest value in the buffer @@ -293,6 +300,13 @@ size_t RingBuffer::getCurrentSize() const return count; } +// Get the first index of buffer +template +size_t RingBuffer::getFirstIdx() const +{ + return first; +} + // Get the last index of buffer template size_t RingBuffer::getLastIdx() const @@ -332,6 +346,7 @@ T RingBuffer::getMaxVal() const template void RingBuffer::clear() { + GWSYNCHRONIZED(&bufLocker); head = 0; first = 0; last = 0; diff --git a/lib/obp60task/OBPSensorTask.cpp b/lib/obp60task/OBPSensorTask.cpp index b0271a1..fe1bcb9 100644 --- a/lib/obp60task/OBPSensorTask.cpp +++ b/lib/obp60task/OBPSensorTask.cpp @@ -75,6 +75,8 @@ void sensorTask(void *param){ 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) + // Link ring buffer pointers to shared memory for transfer to pages + shared->setHstryBuf(twdHstry, twsHstry, dbtHstry); // Start timer Timer1.start(); // Start Timer1 for blinking LED @@ -354,6 +356,24 @@ void sensorTask(void *param){ } } + // Boat data history buffer initialization + // 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 + api->getBoatDataValues(3, hstryValList); + int hstryUpdFreq = 1000; // Update frequency for history buffers in ms + int hstryMinVal = 0; // Minimum value for these history buffers + int twdHstryMax = 6283; // Max value for wind direction (TWD) in rad (0...2*PI), shifted by 1000 for 3 decimals + int twsHstryMax = 1000; // Max value for wind speed (TWS) in m/s, shifted by 10 for 1 decimal + int dbtHstryMax = 3276; // Max value for depth in m (=327), shifted by 10 for 1 decimal + // Initialize history buffers with meta data + twdHstry.setMetaData(twdBVal->getName(), twdBVal->getFormat(), hstryUpdFreq, hstryMinVal, twdHstryMax); + twsHstry.setMetaData(twsBVal->getName(), twsBVal->getFormat(), hstryUpdFreq, hstryMinVal, twsHstryMax); + dbtHstry.setMetaData(dbtBVal->getName(), dbtBVal->getFormat(), hstryUpdFreq, hstryMinVal, dbtHstryMax); + bool simulation = api->getConfig()->getBool(api->getConfig()->useSimuData, false); + int rotoffset = api->getConfig()->getConfigItem(api->getConfig()->rotOffset,true)->asInt(); static long loopCounter = 0; // Loop counter for 1Wire data transmission @@ -367,7 +387,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 + unsigned long starttime20 = millis(); // Get history TWD, TWS, DBT data each 1s tN2kMsg N2kMsg; shared->setSensorData(sensors); //set initially read values @@ -377,23 +397,6 @@ 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::_STW); // STW just for testing - GwApi::BoatValue *hstryValList[]={twdBVal, twsBVal, dbtBVal}; // List of boat values for history storage - // Initialize history buffers with meta data - api->getBoatDataValues(3,hstryValList); - int hstryUpdFreq = 1000; // Update frequency for history buffers in ms - int hstryMinVal = 0; // Minimum value for history buffers - int twdHstryMax = 360; - int twsHstryMax = 100; - int dbtHstryMax = 327; // Max value for depth (due to signed 16 bit integer and decimals shift) - twdHstry.setMetaData(twdBVal->getName(), twdBVal->getFormat(), hstryUpdFreq, hstryMinVal, twdHstryMax); - twsHstry.setMetaData(twsBVal->getName(), twsBVal->getFormat(), hstryUpdFreq, hstryMinVal, twsHstryMax); - dbtHstry.setMetaData(dbtBVal->getName(), dbtBVal->getFormat(), hstryUpdFreq, hstryMinVal, dbtHstryMax); - // Internal RTC with NTP init ESP32Time rtc(0); if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") { @@ -799,22 +802,22 @@ void sensorTask(void *param){ } } - // Read TWD, TWS, DBT data from boatData every 1000ms for history and windplot display - if(millis() > starttime20 + 1000){ - starttime20 = millis(); + // Read TWD, TWS, DBT data from boatData each 1000ms for history and windplot display + if((millis() > starttime20 + 1000) && !simulation){ int16_t val; - api->getBoatDataValues(3,hstryValList); + starttime20 = millis(); + api->getBoatDataValues(3, hstryValList); if (twdBVal->valid) { - val = static_cast(std::round(RadToDeg(twdBVal->value))); + val = static_cast(std::round(twdBVal->value * 1000)); // Shift value to store decimals in int16_t); if (val < hstryMinVal || val > twdHstryMax) { - val = INT16_MIN; // Add invalid value + val = INT16_MIN; // Add invalid value - to be fixed later } } else { val = INT16_MIN; } twdHstry.add(val); if (twsBVal->valid) { - val = static_cast(twsBVal->value * 100); // Shift value to store decimals in int16_t + val = static_cast(twsBVal->value * 10); // Shift value to store decimals in int16_t if (val < hstryMinVal || val > twsHstryMax) { val = INT16_MIN; // Add invalid value } @@ -823,7 +826,7 @@ void sensorTask(void *param){ } twsHstry.add(val); if (dbtBVal->valid) { - val = static_cast(dbtBVal->value * 100); // Shift value to store decimals in int16_t + val = static_cast(dbtBVal->value * 10); // Shift value to store decimals in int16_t if (val < hstryMinVal || val > dbtHstryMax) { val = INT16_MIN; // Add invalid value } @@ -832,18 +835,11 @@ void sensorTask(void *param){ } dbtHstry.add(val); -// api->getLogger()->logDebug(GwLog::ERROR,"SensorTask pointer: TWD: %p, TWS: %p, STW: %p", twdHstry, twsHstry, dbtHstry); - api->getLogger()->logDebug(GwLog::ERROR,"SensorTask Data: TWD:%d TWS:%f DBT:%f", std::round(RadToDeg(twdBVal->value)), twsBVal->value, dbtBVal->value); - api->getLogger()->logDebug(GwLog::ERROR,"SensorTask buffers: TWD:%d TWS:%f DBT:%f", twdHstry.getLast(), twsHstry.getLast(), dbtHstry.getLast()); - + int counttime = millis() - starttime20; + api->getLogger()->logDebug(GwLog::ERROR,"SensorTask write time: %d", counttime); } - // Add sensor and history data to shared memory for transfer to pages shared->setSensorData(sensors); - shared->setHstryBuf(twdHstry, twsHstry, dbtHstry); -// tBoatHstryData tmpHstryData = shared->getHstryBuf(); -// api->getLogger()->logDebug(GwLog::ERROR,"SensorTask tmpHstryData pointer: TWD: %p, TWS: %p, STW: %p", tmpHstryData.twdHstry, tmpHstryData.twsHstry, tmpHstryData.dbtHstry); - } vTaskDelete(NULL); } diff --git a/lib/obp60task/OBPSensorTask.h b/lib/obp60task/OBPSensorTask.h index 352632a..60a94e0 100644 --- a/lib/obp60task/OBPSensorTask.h +++ b/lib/obp60task/OBPSensorTask.h @@ -23,9 +23,10 @@ class SharedData{ GWSYNCHRONIZED(&locker); return sensors; } - void setHstryBuf(RingBuffer twdHstry,RingBuffer twsHstry,RingBuffer dbtHstry) { + void setHstryBuf(RingBuffer& twdHstry, RingBuffer& twsHstry, RingBuffer& dbtHstry) { GWSYNCHRONIZED(&locker); boatHstry={&twdHstry, &twsHstry, &dbtHstry}; +// api->getLogger()->logDebug(GwLog::ERROR, "SharedData setHstryBuf - passed object ptrs: TWD: %p, TWS: %p, STW: %p", &twdHstry, &twsHstry, &dbtHstry); // api->getLogger()->logDebug(GwLog::ERROR, "SharedData setHstryBuf: TWD: %p, TWS: %p, STW: %p", boatHstry.twdHstry, boatHstry.twsHstry, boatHstry.dbtHstry); } tBoatHstryData getHstryBuf() { diff --git a/lib/obp60task/PageWindPlot.cpp b/lib/obp60task/PageWindPlot.cpp index bcd1dc3..77d0580 100644 --- a/lib/obp60task/PageWindPlot.cpp +++ b/lib/obp60task/PageWindPlot.cpp @@ -5,202 +5,9 @@ #include "OBPRingBuffer.h" #include "Pagedata.h" #include // just for RadToDeg function - #include -// **************************************************************** -class OldwndHistory { - // provides a circular buffer to store wind history values -private: - int SIZE; - std::vector buffer; - int first; // points to the first (oldest) valid element - int last; // points to the last (newest) valid element - int head; // points to the next insertion index - int count; // number of valid elements - -public: - bool begin(int size) - // start buffer - { - if (size <= 0 || size > 10000) { - return false; - } - SIZE = size; - buffer.resize(size, INT_MIN); // allocate buffer - head = 0; - first = 0; - last = 0; - count = 0; - return true; - } - - void add(int value) - // Add a new value; store in [0..360 deg] format - { - if (value < 0 || value > 360) - value = INT_MIN; - buffer[head] = value; - last = head; - head = (head + 1) % SIZE; - if (count < SIZE) { - count++; - } else { - first = head - 1; // When buffer is full, first points to the oldest value - if (first < 0) - first += SIZE; - } - } - - int get(int index) const - // Get value by index in [0..360 deg] format (0 = oldest, count-1 = newest) - // INT_MIN indicates missing value or wrong index - { - int realIndex; - - if (index < 0 || index >= count) { - return INT_MIN; // Invalid index - } - realIndex = (first + index) % SIZE; - return buffer[realIndex]; - } - - int get(int index, int deg) const - // Get value by index in [-180..180 deg] or [0..360 deg] format (0 = oldest, count-1 = newest) - { - switch (deg) { - case 180: - // Return value in [-180..180 deg] format - return get(index); - case 360: { - // Return value in [0..360 deg] format - int value = get(index); - if (value < 0) { - value += 360; - }; - return value; - } - default: - return -1; - } - } - - int getSize() const - // Get number of valid elements - { - return count; - } - - int getMin() const - // Get minimum value of buffer - { - if (count == 0) { - return -1; // Buffer is empty - } else if (first + count <= SIZE) { - // No wrap-around - return *std::min_element(buffer.begin() + first, buffer.begin() + first + count); - } else { - // Wrap-around: check [first, end) and [begin, (first+count)%SIZE) - int min1 = *std::min_element(buffer.begin() + first, buffer.end()); - int min2 = *std::min_element(buffer.begin(), buffer.begin() + ((first + count) % SIZE)); - return std::min(min1, min2); - } - } - - int getMin(int amount) const - // Get minimum value of the last values of buffer - // INT_MIN indicates missing value or wrong index - - { - if (count == 0 || amount <= 0) - return INT_MIN; - if (amount > count) - amount = count; - - int minVal = INT_MAX; - int value = 0; - // Start from the newest value (last) and go backwards x times - for (int i = 0; i < amount; ++i) { - value = get(i); - if (value < minVal) - minVal = value; - } - return minVal; - } - - int getMax() const - // Get maximum value of buffer - { - if (count == 0) { - return -1; // Buffer is empty - } else if (first + count <= SIZE) { - // No wrap-around - return *std::max_element(buffer.begin() + first, buffer.begin() + first + count); - } else { - // Wrap-around: check [first, end) and [begin, (first+count)%SIZE) - int max1 = *std::max_element(buffer.begin() + first, buffer.end()); - int max2 = *std::max_element(buffer.begin(), buffer.begin() + ((first + count) % SIZE)); - return std::max(max1, max2); - } - } - - int getMax(int amount) const - // Get maximum value of the last values of buffer - // INT_MIN indicates missing value or wrong index - - { - if (count == 0 || amount <= 0) - return -1; - if (amount > count) - amount = count; - - int maxVal = INT_MIN; - int value = 0; - // Start from the newest value (last) and go backwards x times - for (int i = 0; i < amount; ++i) { - value = get(i); - if (value > maxVal) - maxVal = value; - } - return maxVal; - } - - int getMid(int amount) const - // Get middle value in the buffer - { - if (count == 0) { - return INT_MIN; // Buffer is empty - } - return (getMin(amount) + getMax(amount)) / 2; - } - - int getRng(int center, int amount) const - // Get maximum difference of last of buffer values to center value - { - if (count == 0 || amount <= 0) - return INT_MIN; - if (amount > count) - amount = count; - - int value = 0; - int maxRng = INT_MIN; - int rng = 0; - // Start from the newest value (last) and go backwards x times - for (int i = 0; i < amount; ++i) { - value = get(i); - if (value == INT_MIN) { - continue; - } - rng = abs(((value - center + 540) % 360) - 180); - if (rng > maxRng) - maxRng = rng; - } - if (maxRng > 180) { - maxRng = 180; - } - return maxRng; - } -}; +static const float radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees // Get maximum difference of last of TWD ringbuffer values to center chart int getRng(const RingBuffer& windDirHstry, int center, size_t amount) @@ -221,10 +28,13 @@ int getRng(const RingBuffer& windDirHstry, int center, size_t amount) 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); + value = windDirHstry.get(((last - i) % capacity + capacity) % capacity); + if (value == minVal) { continue; } + + value = value / 1000.0 * radToDeg; rng = abs(((value - center + 540) % 360) - 180); if (rng > maxRng) maxRng = rng; @@ -232,9 +42,47 @@ int getRng(const RingBuffer& windDirHstry, int center, size_t amount) if (maxRng > 180) { maxRng = 180; } + return maxRng; } +void fillSimData(PageData& pageData) +// Fill the TWD history buffer with simulated data +{ + int value = 20; + int16_t value2 = 0; + for (int i = 0; i < 600; i++) { + value += random(-20, 20); + if (value < 0) + value += 360; + if (value >= 360) + value -= 360; + value2 = static_cast(DegToRad(value) * 1000.0); + pageData.boatHstry.twdHstry->add(value2); // Fill the buffer with some test data + } +} + +void fillTstBuffer(PageData& pageData) +{ + float value = 0; + int value2 = 0; + for (int i = 0; i < 60; i++) { + pageData.boatHstry.twdHstry->add(-10); // Fill the buffer with some test data + } + for (int i = 0; i < 20; i++) { + for (int j = 0; j < 20; j++) { + value += 10; + value2 = static_cast(DegToRad(value) * 1000.0); + pageData.boatHstry.twdHstry->add(value2); // Fill the buffer with some test data + } + for (int j = 0; j < 20; j++) { + value -= 10; + value2 = static_cast(DegToRad(value) * 1000.0); + pageData.boatHstry.twdHstry->add(value2); // Fill the buffer with some test data + } + } +} + // **************************************************************** class PageWindPlot : public Page { @@ -307,174 +155,151 @@ public: GwConfigHandler* config = commonData->config; GwLog* logger = commonData->logger; - GwApi::BoatValue* bvalue; - const int numCfgValues = 9; - String dataName[numCfgValues]; - double dataValue[numCfgValues]; - bool dataValid[numCfgValues]; - String dataSValue[numCfgValues]; - String dataUnit[numCfgValues]; - String dataSValueOld[numCfgValues]; - String dataUnitOld[numCfgValues]; - int twdValue; - static const float radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees + float twsValue; // TWS value in chart area + String twdName, twdUnit; // TWD name and unit + int updFreq; // Update frequency for TWD + int16_t twdLowest, twdHighest; // TWD range - bool isInitialized = false; // Flag to indicate that page is initialized - bool wndDataValid = false; // Flag to indicate if wind data is valid - bool simulation = false; - bool holdValues = false; + static bool isInitialized = false; // Flag to indicate that page is initialized + static bool wndDataValid = false; // Flag to indicate if wind data is valid + static int numNoData; // Counter for multiple invalid data values in a row + static bool simulation = false; + static bool holdValues = false; - int width = getdisplay().width(); // Get screen width - int height = getdisplay().height(); // Get screen height - int xCenter = width / 2; // Center of screen in x direction + static int width; // Screen width + static int height; // Screen height + static int xCenter; // Center of screen in x direction static const int yOffset = 48; // Offset for y coordinates of chart area - int cHeight = height - yOffset - 22; // height of chart area - // cHeight = 60; - int bufSize = cHeight * 4; // Buffer size: 920 values for appox. 16 min. history chart + static int cHeight; // height of chart area + static int bufSize; // History buffer size: 960 values for appox. 16 min. history chart int intvBufSize; // Buffer size used for currently selected time interval int count; // current size of buffer - int numWndValues; // number of wind values available for current interval selection + int numWndVals; // number of wind values available for current interval selection static int linesToShow; // current number of lines to display on chart static int bufStart; // 1st data value in buffer to show + int numAddedBufVals; // Number of values added to buffer since last display + size_t currIdx; // Current index in TWD history buffer + static size_t lastIdx; // Last index of TWD history buffer + static size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added static int oldDataIntv; // remember recent user selection of data interval - static int newDate; // indicates for higher time intervals that new date is available - static int wndCenter = INT_MIN; // chart wind center value position; init value indicates that wndCenter is not set yet + static int wndCenter; // chart wind center value position static int wndLeft; // chart wind left value position static int wndRight; // chart wind right value position static int chrtRng; // Range of wind values from mid wind value to min/max wind value in degrees int diffRng; // Difference between mid and current wind value static const int dfltRng = 40; // Default range for chart - static int simWnd = 0; // Simulation value for wind data - static float simTWS = 0; // Simulation value for TWS data - static const int simStep = 10; // Simulation step for wind data + static int simTwd; // Simulation value for TWD + static float simTws; // Simulation value for TWS int x, y; // x and y coordinates for drawing static int prevX, prevY; // Last x and y coordinates for drawing static float chrtScl; // Scale for wind values in pixels per degree int chrtVal; // Current wind value static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line -// 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 RingBuffer windSpdHstry(bufSize); // Circular buffer to store wind speed values LOG_DEBUG(GwLog::LOG, "Display page WindPlot"); unsigned long start = millis(); - // Data initialization - LOG_DEBUG(GwLog::ERROR, "PageWindPlot buffers: TWD:%d TWS:%f DBT:%f", pageData.boatHstry.twdHstry->getLast(), - pageData.boatHstry.twsHstry->getLast() * 0.0194384, pageData.boatHstry.dbtHstry->getLast() / 100); - - // if (windDirHstry.getCurrentSize() == 0) { - if (!isInitialized) { - simWnd = 0; - simTWS = 0; - twdValue = 0; - bufStart = 0; - linesToShow = 0; - oldDataIntv = dataIntv; - newDate = 0; - isInitialized = true; // Set flag to indicate that page is now initialized - } - // Get config data - // String lengthformat = config->getString(config->lengthFormat); simulation = config->getBool(config->useSimuData); holdValues = config->getBool(config->holdvalues); String flashLED = config->getString(config->flashLED); String backlightMode = config->getString(config->backlight); + if (!isInitialized) { + + if (simulation) { + fillSimData(pageData); // Fill the buffer with some test data + } + + width = getdisplay().width(); + height = getdisplay().height(); + xCenter = width / 2; + cHeight = height - yOffset - 22; + bufSize = pageData.boatHstry.twdHstry->getCapacity(); + numNoData = 0; + simTwd = pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg; + simTws = 0; + twsValue = 0; + bufStart = 0; + linesToShow = 0; + oldDataIntv = 0; + numAddedBufVals, currIdx, lastIdx = 0; + lastAddedIdx = pageData.boatHstry.twdHstry->getLastIdx(); + pageData.boatHstry.twdHstry->getMetaData(twdName, twdUnit, updFreq, twdLowest, twdHighest); + wndCenter = INT_MIN; + isInitialized = true; // Set flag to indicate that page is now initialized + LOG_DEBUG(GwLog::ERROR, "PageWindPlot Start1: lastAddedIdx: %d, simTwd: %.1f, isInitialized: %d, SimData: %d", lastAddedIdx, simTwd / 1000 + radToDeg, isInitialized, simulation); + } + // Optical warning by limit violation (unused) if (String(flashLED) == "Limit Violation") { setBlinkingLED(false); setFlashLED(false); } - /* // Read boatdata values for TWD, TWA, TWS, HDM, AWA, AWS, STW, COG, SOG, if available - for (int i = 0; i < numCfgValues; i++) { - bvalue = pageData.values[i]; - dataName[i] = xdrDelete(bvalue->getName()); - dataName[i] = dataName[i].substring(0, 6); // String length limit for value name - calibrationData.calibrateInstance(dataName[i], bvalue, logger); // Check if boat data value is to be calibrated - dataValue[i] = bvalue->value; // Value as double in SI unit - dataValid[i] = bvalue->valid; - dataSValue[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places - dataUnit[i] = formatValue(bvalue, *commonData).unit; - if (dataValid[i]) { - dataSValueOld[i] = dataSValue[i]; // Save old value - dataUnitOld[i] = dataUnit[i]; // Save old unit - } - } + if (simulation) { + simTwd += random(-20, 20); + if (simTwd < 0.0) + simTwd += 360.0; + if (simTwd >= 360.0) + simTwd -= 360.0; - // Store TWD wind value in buffer, regardless of validity -> one value per second (if delivered in that frequency) - twdValue = int((dataValue[0] * radToDeg) + 0.5); // Read TWD value in degrees and round to integer - if (dataValid[0]) { // TWD data existing - wndDataValid = true; - } else { - // Try to calculate TWD value from other data, if available - // wndDataValid = windValues.calcTWD(&twdValue, dataValue[1], dataValue[2], dataValue[3], dataValue[4], dataValue[5], dataValue[6]); - } + int16_t z = static_cast(DegToRad(simTwd) * 1000.0); + pageData.boatHstry.twdHstry->add(z); // Fill the buffer with some test data - if (simulation) { - // Simulate data if simulation is enabled; use default simulation values for TWS - simWnd += random(simStep * -1, simStep); // random value between -simStep and +simStep - if (simWnd < 0) - simWnd += 360; - simWnd = simWnd % 360; - windDirHstry.add(simWnd); - // LOG_DEBUG(GwLog::DEBUG, "PageWindPlot simulation data: windValue: %d, windSpeed: %s", simWnd, dataSValue[2].c_str()); - } else if (wndDataValid) { - windDirHstry.add(twdValue); - } */ + simTws += random(-20, 20); // TWS value in knots + simTws = constrain(simTws, 0.0f, 50.0f); // Ensure TWS is between 0 and 50 knots + twsValue = simTws; + LOG_DEBUG(GwLog::ERROR, "PageWindPlot Simulation: simTwd: %f, twsValue: %f", simTwd, twsValue); + } else { + twsValue = pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384; // TWS value in knots + } - // Identify buffer sizes and buffer position to print on the chart + // Identify buffer size and buffer start position for chart intvBufSize = cHeight * dataIntv; - // count = windDirHstry.getCurrentSize(); count = pageData.boatHstry.twdHstry->getCurrentSize(); - numWndValues = min(count, intvBufSize); - newDate++; + numWndVals = min(count, intvBufSize); + currIdx = pageData.boatHstry.twdHstry->getLastIdx(); + numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display if (dataIntv != oldDataIntv) { - linesToShow = min(numWndValues / dataIntv, max(0, cHeight - 40)); - bufStart = max(0, (count - (linesToShow * dataIntv))); + // new data interval selected by user + linesToShow = min(numWndVals / dataIntv, cHeight - 60); + bufStart = max(0, count - (linesToShow * dataIntv)); + currIdx++; // eliminate current added value for bufStart calculation oldDataIntv = dataIntv; - } else if (newDate >= dataIntv) { - linesToShow = min(numWndValues / dataIntv, linesToShow + 1); - newDate = 0; + } else { + if (numAddedBufVals >= dataIntv) { + linesToShow += (numAddedBufVals / dataIntv); // Number of lines to show on chart + LOG_DEBUG(GwLog::ERROR, "PageWindPlot bufStart: bufStart: %d, numAddedBufVals: %d, linesToShow: %d, count %d, bufSize %d, Cnt=Size: %d", bufStart, numAddedBufVals, linesToShow, count, bufSize, count == bufSize); + lastAddedIdx = currIdx; + } + if (count == bufSize && currIdx != lastIdx) { + int numVals = (currIdx - lastIdx + bufSize) % bufSize; + bufStart = ((bufStart - numVals) % bufSize + bufSize) % bufSize; // keep 1st chart value constant in a rolling buffer when new data is added + lastIdx = currIdx; + } } - if (count == bufSize) { - bufStart--; // show the latest wind values in buffer; keep 1st value constant in a rolling buffer when new data is added - } - LOG_DEBUG(GwLog::ERROR, "PageWindPlot Dataset: TWD: %d, TWS: %f, STW: %f, count: %d, intvBufSize: %d, numWndValues: %d, bufStart: %d, linesToShow: %d, newDate: %d", pageData.boatHstry.twdHstry->getLast(), - pageData.boatHstry.twsHstry->getLast() * 0.0194384, pageData.boatHstry.dbtHstry->getLast() / 100, count, intvBufSize, numWndValues, bufStart, linesToShow, newDate); -// LOG_DEBUG(GwLog::ERROR, "PageWindPlot Pointer: TWD: %p, TWS: %p, STW: %p", pageData.boatHstry.twdHstry, pageData.boatHstry.twsHstry, pageData.boatHstry.dbtHstry); - -// LOG_DEBUG(GwLog::LOG, "PageWindPlot, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, cnt: %d, valid0: %d", dataName[0].c_str(), dataValue[0], -// dataName[1].c_str(), dataValue[1], dataName[2].c_str(), dataValue[2], dataName[3].c_str(), dataValue[3], dataName[4].c_str(), dataValue[4], -// dataName[5].c_str(), dataValue[5], dataName[6].c_str(), dataValue[6], dataName[7].c_str(), dataValue[7], dataName[8].c_str(), dataValue[8], count, dataValid[0]); + LOG_DEBUG(GwLog::ERROR, "PageWindPlot Dataset: TWD: %.1f, TWS: %.1f, DBT: %.1f, count: %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, linesToShow: %d, numAddedBufVals: %d, lastIdx: %d", + pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384, pageData.boatHstry.dbtHstry->getLast() / 10.0, + count, intvBufSize, numWndVals, bufStart, linesToShow, numAddedBufVals, lastIdx); // initialize chart range values if (wndCenter == INT_MIN) { - // wndCenter = max(0, int(windDirHstry.get(numWndValues - intvBufSize))); // get 1st value of current data interval - wndCenter = max(0, int(pageData.boatHstry.twdHstry->get(numWndValues - intvBufSize))); // get 1st value of current data interval + wndCenter = max(0, int(pageData.boatHstry.twdHstry->get(numWndVals - intvBufSize) / 1000.0 * radToDeg)); // 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 = getRng(windDirHstry, wndCenter, numWndValues); - diffRng = getRng(*pageData.boatHstry.twdHstry, wndCenter, numWndValues); - diffRng = (diffRng == INT_MIN ? 0 : diffRng); + diffRng = getRng(*pageData.boatHstry.twdHstry, wndCenter, numWndVals); + diffRng = (diffRng == INT16_MIN ? 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 chrtRng = max(dfltRng, int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10); } - // int debugMin = windDirHstry.getMin(numWndValues); - int debugMin = pageData.boatHstry.twdHstry->getMin(numWndValues); - int debugMax = pageData.boatHstry.twdHstry->getMax(numWndValues); - LOG_DEBUG(GwLog::ERROR, "PageWindPlot Range. wndCenter: %d, numWndValues: %d, min: %d, max: %d, diffrng: %d, chrtRng: %d ", wndCenter, numWndValues, debugMin, debugMax, diffRng, chrtRng); } chrtScl = float(width) / float(chrtRng) / 2.0; // Chart scale: pixels per degree wndLeft = wndCenter - chrtRng; @@ -483,11 +308,11 @@ public: wndRight = (chrtRng < 180 ? wndCenter + chrtRng : wndCenter + chrtRng - 1); if (wndRight >= 360) wndRight -= 360; - // LOG_DEBUG(GwLog::DEBUG, "PageWindPlot dataValue[0]: %f, windValue: %d, count: %d, diffRng: %d, chartRng: %d, Center: %d, scale: %f", double(dataValue[0] * radToDeg), - // (!windDirHstry.get(count - 1) < 0 ? 0 : windDirHstry.get(count - 1)), count, diffRng, chrtRng, wndCenter, chrtScl); + LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FirstVal: %f, LastVal: %d, count: %d, diffRng: %d, chartRng: %d, Center: %d, scale: %f", pageData.boatHstry.twdHstry->getFirst() / 1000.0 * radToDeg, + pageData.boatHstry.twdHstry->get(linesToShow) / 1000.0 * radToDeg, count, diffRng, chrtRng, wndCenter, chrtScl); // Draw page - //*********************************************************** + //*********************************************************************** // Set display in partial refresh mode getdisplay().setPartialWindow(0, 0, width, height); // Set partial update @@ -519,66 +344,80 @@ public: getdisplay().drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // symbol getdisplay().drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // symbol - if (pageData.boatHstry.twdHstry->getMax() == INT16_MIN) { - wndDataValid = false; // only values in buffer -> no valid wind data available + if (pageData.boatHstry.twdHstry->getMax() == twdLowest) { + // only values in buffer -> no valid wind data available + wndDataValid = false; } else { wndDataValid = true; // At least some wind data available } // Draw wind values in chart - //*********************************************************** + //*********************************************************************** if (wndDataValid) { for (int i = 0; i < linesToShow; i++) { - // chrtVal = windDirHstry.get(bufStart + (i * dataIntv)); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer - chrtVal = pageData.boatHstry.twdHstry->get(bufStart + (i * dataIntv)); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer + chrtVal = static_cast(pageData.boatHstry.twdHstry->get((bufStart + (i * dataIntv)) % bufSize)); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer if (chrtVal == INT16_MIN) { chrtPrevVal = INT16_MIN; - continue; // skip invalid values - } - x = ((chrtVal - wndLeft + 360) % 360) * chrtScl; - y = yOffset + cHeight - i; // Position in chart area - if (i > linesToShow - 15) - LOG_DEBUG(GwLog::ERROR, "PageWindPlot Chart: i: %d, chrtVal: %d, chrtPrevVal: %d, bufStart: %d count: %d, linesToShow: %d", i, chrtVal, chrtPrevVal, bufStart, count, linesToShow); - -// if (i == 0) { - if ((i == 0) || (chrtPrevVal == INT16_MIN)) { - prevX = x; // just a dot for 1st chart point or after some invalid values - prevY = y; - } else { - // cross borders check; shift values to [-180..0..180]; when crossing borders, range is 2x 180 degrees - int wndLeftDlt = -180 - ((wndLeft >= 180) ? (wndLeft - 360) : wndLeft); - int chrtVal180 = ((chrtVal + wndLeftDlt + 180) % 360 + 360) % 360 - 180; - int chrtPrevVal180 = ((chrtPrevVal + wndLeftDlt + 180) % 360 + 360) % 360 - 180; - // if (i > linesToShow - 15) - // LOG_DEBUG(GwLog::ERROR, "PageWindPlot Chart: i: %d, chrtVal: %d, chrtVal180: %d, chrtPrevVal: %d, chrtPrevVal180: %d, wndLeftDlt: %d", i, chrtVal, chrtVal180, chrtPrevVal, chrtPrevVal180, wndLeftDlt); - if (((chrtPrevVal180 >= -180) && (chrtPrevVal180 < -90) && (chrtVal180 > 90)) || ((chrtPrevVal180 <= 179) && (chrtPrevVal180 > 90) && chrtVal180 <= -90)) { - // If current value crosses chart borders, compared to previous value, split line - int xSplit = (((chrtPrevVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl; - getdisplay().drawLine(prevX, prevY, xSplit, y, commonData->fgcolor); - getdisplay().drawLine(prevX, prevY - 1, ((xSplit != prevX) ? xSplit : xSplit - 1), ((xSplit != prevX) ? y - 1 : y), commonData->fgcolor); - prevX = (((chrtVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl; - // LOG_DEBUG(GwLog::ERROR, "PageWindPlot Cross: i: %d, chrtVal: %d, chrtPrevVal: %d, wndLeft: %d wndRight: %d, curr:{%d,%d} prev:{%d,%d}", i, chrtVal, chrtPrevVal, wndLeft, wndRight, x, y, prevX, prevY); + /* if (i == linesToShow - 1) { + numNoData++; + // If more than 4 invalid values in a row, reset chart + } else { + numNoData = 0; // Reset invalid value counter } - } + if (numNoData > 4) { + // If more than 4 invalid values in a row, send message + getdisplay().setFont(&Ubuntu_Bold10pt7b); + getdisplay().fillRect(xCenter - 66, height / 2 - 20, 146, 24, commonData->bgcolor); // Clear area for TWS value + drawTextCenter(xCenter, height / 2 - 10, "No sensor data"); + } */ + } else { + chrtVal = (chrtVal / 1000.0 * radToDeg) + 0.5; // Convert to degrees and round + x = ((chrtVal - wndLeft + 360) % 360) * chrtScl; + y = yOffset + cHeight - i; // Position in chart area + if (i > linesToShow - 30) + LOG_DEBUG(GwLog::ERROR, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d count: %d, linesToShow: %d", i, chrtVal, bufStart, count, linesToShow); - // Draw line with 2 pixels width + make sure vertical line are drawn correctly - getdisplay().drawLine(prevX, prevY, x, y, commonData->fgcolor); - getdisplay().drawLine(prevX, prevY - 1, ((x != prevX) ? x : x - 1), ((x != prevX) ? y - 1 : y), commonData->fgcolor); - chrtPrevVal = chrtVal; - prevX = x; - prevY = y; - - if (i == (cHeight - 1)) { // Reaching chart area top end () - linesToShow -= min(40, cHeight); // free top 40 lines of chart for new values - bufStart = max(0, count - (linesToShow * dataIntv)); // next start value in buffer to show - // if ((windDirHstry.getMin(numWndValues) > wndCenter) || (windDirHstry.getMax(numWndValues) < wndCenter)) { - if ((pageData.boatHstry.twdHstry->getMin(numWndValues) > wndCenter) || (pageData.boatHstry.twdHstry->getMax(numWndValues) < wndCenter)) { - // Check if all wind value are left or right of center value -> optimize chart range - int mid = pageData.boatHstry.twdHstry->getMid(numWndValues); - if (mid != INT16_MIN) { - wndCenter = int((mid + (mid >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value + if ((i == 0) || (chrtPrevVal == INT16_MIN)) { + // just a dot for 1st chart point or after some invalid values + prevX = x; + prevY = y; + } else { + // cross borders check; shift values to [-180..0..180]; when crossing borders, range is 2x 180 degrees + int wndLeftDlt = -180 - ((wndLeft >= 180) ? (wndLeft - 360) : wndLeft); + int chrtVal180 = ((chrtVal + wndLeftDlt + 180) % 360 + 360) % 360 - 180; + int chrtPrevVal180 = ((chrtPrevVal + wndLeftDlt + 180) % 360 + 360) % 360 - 180; + if (((chrtPrevVal180 >= -180) && (chrtPrevVal180 < -90) && (chrtVal180 > 90)) || ((chrtPrevVal180 <= 179) && (chrtPrevVal180 > 90) && chrtVal180 <= -90)) { + // If current value crosses chart borders compared to previous value, split line + int xSplit = (((chrtPrevVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl; + getdisplay().drawLine(prevX, prevY, xSplit, y, commonData->fgcolor); + getdisplay().drawLine(prevX, prevY - 1, ((xSplit != prevX) ? xSplit : xSplit - 1), ((xSplit != prevX) ? y - 1 : y), commonData->fgcolor); + prevX = (((chrtVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl; } } - // LOG_DEBUG(GwLog::ERROR, "PageWindPlot FreeTop: cHeight: %d, LinesToShow: %d, numWndValues: %d, wndCenter: %d, bufStart: %d", cHeight, linesToShow, numWndValues, wndCenter, bufStart); + + // Draw line with 2 pixels width + make sure vertical line are drawn correctly + getdisplay().drawLine(prevX, prevY, x, y, commonData->fgcolor); + getdisplay().drawLine(prevX, prevY - 1, ((x != prevX) ? x : x - 1), ((x != prevX) ? y - 1 : y), commonData->fgcolor); + chrtPrevVal = chrtVal; + prevX = x; + prevY = y; + } + // Reaching chart area top end + if (i >= (cHeight - 1)) { + linesToShow -= min(60, cHeight); // free top 40 lines of chart for new values + if (count >= numWndVals) { + bufStart = (bufStart + (60 * dataIntv)) % bufSize; // next start value in buffer to show + } + int minWndDir = pageData.boatHstry.twdHstry->getMin(numWndVals) / 1000.0 * radToDeg; + int maxWndDir = pageData.boatHstry.twdHstry->getMax(numWndVals) / 1000.0 * radToDeg; + LOG_DEBUG(GwLog::ERROR, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter); + if ((minWndDir > wndCenter) || (maxWndDir < wndCenter)) { + // Check if all wind value are left or right of center value -> optimize chart range + int midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals) / 1000.0 * radToDeg; + if (midWndDir != INT16_MIN) { + wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value + } + } + LOG_DEBUG(GwLog::ERROR, "PageWindPlot FreeTop: cHeight: %d, LinesToShow: %d, bufStart: %d, numWndVals: %d, wndCenter: %d", cHeight, linesToShow, bufStart, numWndVals, wndCenter); break; } } @@ -616,42 +455,46 @@ public: getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setCursor(xPosTws, yPosTws); - getdisplay().print(dataSValue[2]); // Value + // twsValue = pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384; // TWS value in knots + if (twsValue < 0 || twsValue >= 100) { + getdisplay().print("--.-"); + } else { + getdisplay().printf("%2.1f", twsValue); // Value + } getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setCursor(xPosTws + 82, yPosTws - 14); - getdisplay().print(dataName[2]); // Name + getdisplay().print("TWS"); // Name getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setCursor(xPosTws + 78, yPosTws + 1); - getdisplay().print(" "); - if (holdValues == false) { - getdisplay().print(dataUnit[2]); // Unit - } else { - getdisplay().print(dataUnitOld[2]); // Unit - } + getdisplay().printf(" kn"); // Unit } - // chart Y axis labels; print last to overwrite potential chart lines in label area - char sWndYAx[4]; // char buffer for wind Y axis labels + // chart Y axis labels; print at last to overwrite potential chart lines in label area int yPos; + int chrtLbl; getdisplay().setFont(&Ubuntu_Bold8pt7b); for (int i = 1; i <= 3; i++) { - if (numWndValues < intvBufSize) { - // chart initially filled from botton to top -> revert minute labels - yPos = yOffset + cHeight - (i * 60); - } else { - yPos = yOffset + (i * 60); - } + yPos = yOffset + (i * 60); getdisplay().fillRect(0, yPos, width, 1, commonData->fgcolor); getdisplay().fillRect(0, yPos - 8, 26, 16, commonData->bgcolor); // Clear small area to remove potential chart lines getdisplay().setCursor(1, yPos + 4); - snprintf(sWndYAx, 4, "%3d", i * dataIntv * -1); - getdisplay().print(sWndYAx); // Wind value label + if (count >= intvBufSize) { + // Calculate minute value for label + chrtLbl = ((i - 1 + (prevY < yOffset + 30)) * dataIntv) * -1; // change label if last data point is more than 30 lines (= seconds) from chart line + } else { + int j = 3 - i; + // chrtLbl = (int((((count / dataIntv) - 50) * dataIntv / 60) + 1) - ((j - 1) * dataIntv)) * -1; // 50 lines left below last chart line + chrtLbl = (int(((linesToShow - 50) * dataIntv / 60) + 1) - (j * dataIntv)) * -1; // 50 lines left below last chart line + } + // if (chrtLbl <= 0) { + getdisplay().printf("%3d", chrtLbl); // Wind value label + // } } - // Update display - getdisplay().nextPage(); // Partial update (fast) unsigned long finish = millis() - start; LOG_DEBUG(GwLog::ERROR, "PageWindPlot Time: %lu", finish); + // Update display + getdisplay().nextPage(); // Partial update (fast) }; }; diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index 8d14c18..581c999 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -837,11 +837,7 @@ void OBP60Task(GwApi *api){ currentPage->displayNew(pages[pageNumber].parameters); lastPage=pageNumber; } - pages[pageNumber].parameters.boatHstry = shared->getHstryBuf(); // Add boat history to page parameters - LOG_DEBUG(GwLog::ERROR,"obp60task buffers: TWD:%d TWS:%f DBT:%f", pages[pageNumber].parameters.boatHstry.twdHstry->getLast(), - pages[pageNumber].parameters.boatHstry.twsHstry->getLast() * 0.0194384, pages[pageNumber].parameters.boatHstry.dbtHstry->getLast() / 100); -// LOG_DEBUG(GwLog::ERROR, "obp60task pointer: TWD: %p, TWS: %p, STW: %p", pages[pageNumber].parameters.boatHstry.twdHstry, -// pages[pageNumber].parameters.boatHstry.twsHstry, pages[pageNumber].parameters.boatHstry.dbtHstry); + pages[pageNumber].parameters.boatHstry = shared->getHstryBuf(); // Add boat history data to page parameters //call the page code LOG_DEBUG(GwLog::DEBUG,"calling page %d",pageNumber); // Show footer if enabled (together with header)