Implement OBPRingBuffer class and adjust PageWindPlot accordingly
This commit is contained in:
parent
9ada5be7cb
commit
1f90cefbd6
|
@ -1,3 +1,8 @@
|
|||
{
|
||||
"cmake.configureOnOpen": false
|
||||
"cmake.configureOnOpen": false,
|
||||
"files.associations": {
|
||||
"stdexcept": "cpp",
|
||||
"limits": "cpp",
|
||||
"functional": "cpp"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
template <typename T>
|
||||
class RingBuffer {
|
||||
private:
|
||||
std::vector<T> buffer;
|
||||
size_t capacity;
|
||||
size_t head; // Points to the next insertion position
|
||||
size_t first; // Points to the first (oldest) valid element
|
||||
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>
|
||||
|
||||
// metadata for buffer
|
||||
int updFreq; // Update frequency in milliseconds
|
||||
int smallest; // Value range of buffer: smallest value
|
||||
int biggest; // Value range of buffer: biggest value
|
||||
|
||||
public:
|
||||
RingBuffer(size_t size);
|
||||
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
|
||||
T getLast() const; // Get the last (newest) value in buffer
|
||||
T getMin() const; // Get the lowest value in buffer
|
||||
T getMin(size_t amount) const; // Get minimum value of the last <amount> values of buffer
|
||||
T getMax() const; // Get the highest value in buffer
|
||||
T getMax(size_t amount) const; // Get maximum value of the last <amount> values of buffer
|
||||
T getMid() const; // Get mid value between <min> and <max> value in buffer
|
||||
T getMid(size_t amount) const; // Get mid value between <min> and <max> value of the last <amount> values of buffer
|
||||
T getRng(T center, size_t amount) const; // Get maximum difference of last <amount> of buffer values to center value
|
||||
T getMedian() const; // Get the median value in buffer
|
||||
T 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 getCurrentSize() const; // Get the current number of elements in 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
|
||||
void clear(); // Clear buffer
|
||||
T operator[](size_t index) const;
|
||||
std::vector<T> getAllValues() const; // Operator[] for convenient access (same as get())
|
||||
|
||||
};
|
||||
|
||||
#include "OBPRingBuffer.tpp"
|
|
@ -0,0 +1,356 @@
|
|||
#include "OBPRingBuffer.h"
|
||||
|
||||
template <typename T>
|
||||
RingBuffer<T>::RingBuffer(size_t size)
|
||||
: capacity(size)
|
||||
, head(0)
|
||||
, first(0)
|
||||
, last(0)
|
||||
, count(0)
|
||||
, is_Full(false)
|
||||
{
|
||||
if (size == 0) {
|
||||
// return false;
|
||||
}
|
||||
|
||||
MIN_VAL = std::numeric_limits<T>::lowest();
|
||||
MAX_VAL = std::numeric_limits<T>::max();
|
||||
buffer.resize(size, MIN_VAL);
|
||||
|
||||
// return true;
|
||||
}
|
||||
|
||||
// Add a new value to the buffer
|
||||
template <typename T>
|
||||
void RingBuffer<T>::add(const T& value)
|
||||
{
|
||||
buffer[head] = value;
|
||||
last = head;
|
||||
|
||||
if (is_Full) {
|
||||
first = (first + 1) % capacity; // Move pointer to oldest element when overwriting
|
||||
} else {
|
||||
count++;
|
||||
if (count == capacity) {
|
||||
is_Full = true;
|
||||
}
|
||||
}
|
||||
|
||||
head = (head + 1) % capacity;
|
||||
}
|
||||
|
||||
// Get value at specific position (0-based index from oldest to newest)
|
||||
template <typename T>
|
||||
T RingBuffer<T>::get(size_t index) const
|
||||
{
|
||||
if (isEmpty()) {
|
||||
throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
if (index < 0 || index >= count) {
|
||||
return MIN_VAL;
|
||||
// throw std::out_of_range("Index out of range");
|
||||
}
|
||||
|
||||
size_t realIndex = (first + index) % capacity;
|
||||
return buffer[realIndex];
|
||||
}
|
||||
|
||||
// Operator[] for convenient access (same as get())
|
||||
template <typename T>
|
||||
T RingBuffer<T>::operator[](size_t index) const
|
||||
{
|
||||
return get(index);
|
||||
}
|
||||
|
||||
// Get the first (oldest) value in the buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getFirst() const
|
||||
{
|
||||
if (isEmpty()) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
return buffer[first];
|
||||
}
|
||||
|
||||
// Get the last (newest) value in the buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getLast() const
|
||||
{
|
||||
if (isEmpty()) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
return buffer[last];
|
||||
}
|
||||
|
||||
// Get the lowest value in the buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMin() const
|
||||
{
|
||||
if (isEmpty()) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
|
||||
T minVal = get(first);
|
||||
T value;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
value = get(i);
|
||||
if (value < minVal) {
|
||||
minVal = value;
|
||||
}
|
||||
}
|
||||
return minVal;
|
||||
}
|
||||
|
||||
// Get minimum value of the last <amount> values of buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMin(size_t amount) const
|
||||
{
|
||||
if (isEmpty() || amount <= 0) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
if (amount > count)
|
||||
amount = count;
|
||||
|
||||
T minVal = get(last);
|
||||
T value;
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
value = get((last + capacity - i) % capacity);
|
||||
if (value < minVal && value != MIN_VAL) {
|
||||
minVal = value;
|
||||
}
|
||||
}
|
||||
return minVal;
|
||||
}
|
||||
|
||||
// Get the highest value in the buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMax() const
|
||||
{
|
||||
if (isEmpty()) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
|
||||
T maxVal = get(first);
|
||||
T value;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
value = get(i);
|
||||
if (value > maxVal) {
|
||||
maxVal = value;
|
||||
}
|
||||
}
|
||||
return maxVal;
|
||||
}
|
||||
|
||||
// Get maximum value of the last <amount> values of buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMax(size_t amount) const
|
||||
{
|
||||
if (isEmpty() || amount <= 0) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
if (amount > count)
|
||||
amount = count;
|
||||
|
||||
T maxVal = get(last);
|
||||
T value;
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
value = get((last + capacity - i) % capacity);
|
||||
if (value > maxVal && value != MIN_VAL) {
|
||||
maxVal = value;
|
||||
}
|
||||
}
|
||||
return maxVal;
|
||||
}
|
||||
|
||||
// Get mid value between <min> and <max> value in the buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMid() const
|
||||
{
|
||||
if (isEmpty()) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
|
||||
return (getMin() + getMax()) / static_cast<T>(2);
|
||||
}
|
||||
|
||||
// Get mid value between <min> and <max> value of the last <amount> values of buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMid(size_t amount) const
|
||||
{
|
||||
if (isEmpty() || amount <= 0) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
|
||||
if (amount > count)
|
||||
amount = count;
|
||||
|
||||
return (getMin(amount) + getMax(amount)) / static_cast<T>(2);
|
||||
}
|
||||
|
||||
// ******************* works for wind direction only -> move out of here *******************************
|
||||
// Get maximum difference of last <amount> of buffer values to center value
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getRng(T center, size_t amount) const
|
||||
{
|
||||
if (isEmpty() || amount <= 0) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
if (amount > count)
|
||||
amount = count;
|
||||
|
||||
T value = 0;
|
||||
T rng = 0;
|
||||
T maxRng = MIN_VAL;
|
||||
// Start from the newest value (last) and go backwards x times
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
value = get((last + capacity - i) % capacity);
|
||||
if (value == MIN_VAL) {
|
||||
continue;
|
||||
}
|
||||
rng = abs(((value - center + 540) % 360) - 180);
|
||||
if (rng > maxRng)
|
||||
maxRng = rng;
|
||||
}
|
||||
if (maxRng > 180) {
|
||||
maxRng = 180;
|
||||
}
|
||||
return maxRng;
|
||||
}
|
||||
|
||||
// Get the median value in the buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMedian() const
|
||||
{
|
||||
if (isEmpty()) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
|
||||
// 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 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 (temp[count / 2 - 1] + temp[count / 2]) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the median value of the last <amount> values of buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMedian(size_t amount) const
|
||||
{
|
||||
if (isEmpty() || amount <= 0) {
|
||||
return MIN_VAL;
|
||||
// throw std::runtime_error("Buffer is empty");
|
||||
}
|
||||
if (amount > count)
|
||||
amount = count;
|
||||
|
||||
// Create a temporary vector with current valid elements
|
||||
std::vector<T> temp;
|
||||
temp.reserve(amount);
|
||||
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
temp.push_back(get(i));
|
||||
}
|
||||
|
||||
// Sort to find median
|
||||
std::sort(temp.begin(), temp.end());
|
||||
|
||||
if (amount % 2 == 1) {
|
||||
// Odd number of elements
|
||||
return temp[amount / 2];
|
||||
} else {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the buffer capacity (maximum size)
|
||||
template <typename T>
|
||||
size_t RingBuffer<T>::getCapacity() const
|
||||
{
|
||||
return capacity;
|
||||
}
|
||||
|
||||
// Get the current number of elements in the buffer
|
||||
template <typename T>
|
||||
size_t RingBuffer<T>::getCurrentSize() const
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
// Check if buffer is empty
|
||||
template <typename T>
|
||||
bool RingBuffer<T>::isEmpty() const
|
||||
{
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
// Check if buffer is full
|
||||
template <typename T>
|
||||
bool RingBuffer<T>::isFull() const
|
||||
{
|
||||
return is_Full;
|
||||
}
|
||||
|
||||
// Get lowest possible value for buffer; used for initialized buffer data
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMinVal()
|
||||
{
|
||||
return MIN_VAL;
|
||||
}
|
||||
|
||||
// Get highest possible value for buffer
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMaxVal()
|
||||
{
|
||||
return MAX_VAL;
|
||||
}
|
||||
|
||||
// Clear buffer
|
||||
template <typename T>
|
||||
void RingBuffer<T>::clear()
|
||||
{
|
||||
head = 0;
|
||||
first = 0;
|
||||
last = 0;
|
||||
count = 0;
|
||||
is_Full = false;
|
||||
}
|
||||
|
||||
// Get all current values as a vector
|
||||
template <typename T>
|
||||
std::vector<T> RingBuffer<T>::getAllValues() const
|
||||
{
|
||||
std::vector<T> result;
|
||||
result.reserve(count);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
result.push_back(get(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
#include "ObpNmea0183.h" // Check NMEA0183 sentence for uncorrect content
|
||||
#include "OBP60Extensions.h" // Lib for hardware extensions
|
||||
#include "movingAvg.h" // Lib for moving average building
|
||||
#include "OBPRingBuffer.h" // Lib with ring buffer for history storage of some boat data
|
||||
#include "time.h" // For getting NTP time
|
||||
#include <ESP32Time.h> // Internal ESP32 RTC clock
|
||||
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
#include "BoatDataCalibration.h"
|
||||
#include "OBP60Extensions.h"
|
||||
#include "Pagedata.h"
|
||||
#include "OBPRingBuffer.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// ****************************************************************
|
||||
class wndHistory {
|
||||
class OldwndHistory {
|
||||
// provides a circular buffer to store wind history values
|
||||
private:
|
||||
int SIZE;
|
||||
|
@ -273,9 +274,6 @@ public:
|
|||
GwConfigHandler* config = commonData->config;
|
||||
GwLog* logger = commonData->logger;
|
||||
|
||||
static wndHistory windDirHstry; // Circular buffer to store wind direction values
|
||||
static wndHistory windSpdHstry; // Circular buffer to store wind speed values
|
||||
|
||||
GwApi::BoatValue* bvalue;
|
||||
const int numCfgValues = 9;
|
||||
String dataName[numCfgValues];
|
||||
|
@ -325,15 +323,18 @@ public:
|
|||
int distVals; // helper to check wndCenter crossing
|
||||
int distMid; // helper to check wndCenter crossing
|
||||
|
||||
static RingBuffer<int> windDirHstry(bufSize); // Circular buffer to store wind direction values
|
||||
static RingBuffer<int>windSpdHstry(bufSize); // Circular buffer to store wind speed values
|
||||
|
||||
LOG_DEBUG(GwLog::LOG, "Display page WindPlot");
|
||||
unsigned long start = millis();
|
||||
|
||||
// Data initialization
|
||||
if (windDirHstry.getSize() == 0) {
|
||||
if (!windDirHstry.begin(bufSize)) {
|
||||
if (windDirHstry.getCurrentSize() == 0) {
|
||||
/* if (!windDirHstry.begin(bufSize)) {
|
||||
logger->logDebug(GwLog::ERROR, "Failed to initialize wind direction history buffer");
|
||||
return;
|
||||
}
|
||||
} */
|
||||
simWnd = 0;
|
||||
simTWS = 0;
|
||||
twdValue = 0;
|
||||
|
@ -363,7 +364,6 @@ public:
|
|||
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
|
||||
calibrationData.calibrateInstance(dataName[i], bvalue, logger); // Check if boat data value is to be calibrated
|
||||
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;
|
||||
|
@ -396,7 +396,7 @@ public:
|
|||
|
||||
// Identify buffer sizes and buffer position to print on the chart
|
||||
intvBufSize = cHeight * dataIntv;
|
||||
count = windDirHstry.getSize();
|
||||
count = windDirHstry.getCurrentSize();
|
||||
numWndValues = min(count, intvBufSize);
|
||||
newDate++;
|
||||
if (dataIntv != oldDataIntv) {
|
||||
|
@ -522,7 +522,6 @@ public:
|
|||
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
|
||||
// windDirHstry.mvStart(); // virtually delete 40 values from buffer
|
||||
if ((windDirHstry.getMin(numWndValues) > wndCenter) || (windDirHstry.getMax(numWndValues) < wndCenter)) {
|
||||
// Check if all wind value are left or right of center value -> optimize chart range
|
||||
int mid = windDirHstry.getMid(numWndValues);
|
||||
|
|
Loading…
Reference in New Issue