#include #include #include template void RingBuffer::initCommon() { NUMLIMIT_LOW = std::numeric_limits::lowest(); NUMLIMIT_HIGH = std::numeric_limits::max(); dataName = ""; dataFmt = ""; updFreq = -1; mltplr = 1; BUFMIN_VAL = static_cast(NUMLIMIT_LOW); BUFMAX_VAL = static_cast(NUMLIMIT_HIGH); lowest = BUFMIN_VAL; highest = BUFMAX_VAL; bufLocker = xSemaphoreCreateMutex(); } template RingBuffer::RingBuffer() : capacity(0) , head(0) , first(0) , last(0) , count(0) , is_Full(false) { initCommon(); // stays empty } template RingBuffer::RingBuffer(size_t size) : capacity(size) , head(0) , first(0) , last(0) , count(0) , is_Full(false) { initCommon(); buffer.reserve(size); buffer.resize(size, NUMLIMIT_HIGH); // NUMLIMIT_HIGH indicate invalid values } // Specify meta data of buffer content template void RingBuffer::setMetaData(String name, String format, int updateFrequency, double multiplier, double minValue, double maxValue) { GWSYNCHRONIZED(&bufLocker); dataName = name; dataFmt = format; updFreq = updateFrequency; mltplr = multiplier; BUFMIN_VAL = static_cast(NUMLIMIT_LOW) / mltplr; // lowest possible buffer value; converted to external view BUFMAX_VAL = static_cast(NUMLIMIT_HIGH) / mltplr; // highest possible buffer value; converted to external view lowest = std::max(BUFMIN_VAL, minValue); // low value range, set by user highest = std::min(std::nextafter(BUFMAX_VAL, -std::numeric_limits::infinity()), maxValue); // high value range, set by user; maximum is 1 tick lower than BUFMAX_VAL } // Specify format of buffer content template void RingBuffer::setFormat(String format) { GWSYNCHRONIZED(&bufLocker); dataFmt = format; } // Get meta data of buffer content template bool RingBuffer::getMetaData(String& name, String& format, int& updateFrequency, double& multiplier, double& minValue, double& maxValue) { if (dataName == "" || dataFmt == "" || updFreq == -1) { return false; // Meta data not set } GWSYNCHRONIZED(&bufLocker); name = dataName; format = dataFmt; updateFrequency = updFreq; multiplier = mltplr; minValue = lowest; maxValue = highest; return true; } // Get meta data of buffer content template bool RingBuffer::getMetaData(String& name, String& format) { if (dataName == "" || dataFmt == "") { return false; // Meta data not set } GWSYNCHRONIZED(&bufLocker); name = dataName; format = dataFmt; return true; } // Get buffer name template String RingBuffer::getName() const { return dataName; } // Get buffer data format template String RingBuffer::getFormat() const { return dataFmt; } // Get buffer update frequency template int RingBuffer::getUpdFreq() const { return updFreq; } // Add a new value to buffer template void RingBuffer::add(const double& value) { GWSYNCHRONIZED(&bufLocker); if (value < lowest || value > highest) { buffer[head] = NUMLIMIT_HIGH; // Store maximum buffer value if data value is out of range } else { buffer[head] = static_cast(std::round(value * mltplr)); } last = head; if (is_Full) { first = (first + 1) % capacity; // Move pointer to oldest element when overwriting } else { count++; if (count == capacity) { is_Full = true; } } // Serial.printf("Ringbuffer: value %.3f, multiplier: %.1f, buffer: %d\n", value, mltplr, buffer[head]); head = (head + 1) % capacity; } // Get value at specific position (0-based index from oldest to newest) template double RingBuffer::get(size_t index) const { GWSYNCHRONIZED(&bufLocker); if (isEmpty() || index < 0 || index >= count) { return BUFMAX_VAL; } size_t realIndex = (first + index) % capacity; return static_cast(buffer[realIndex] / mltplr); // is BUFMAX_VAL if value is invalid } // Operator[] for convenient access (same as get()) template double RingBuffer::operator[](size_t index) const { return get(index); } // Get the first (oldest) value in the buffer template double RingBuffer::getFirst() const { if (isEmpty()) { return BUFMAX_VAL; } return get(0); } // Get the last (newest) value in the buffer template double RingBuffer::getLast() const { if (isEmpty()) { return BUFMAX_VAL; } return get(count - 1); } // Get the lowest value in the buffer template double RingBuffer::getMin() const { return getMin(getCurrentSize()); } // Get minimum value of the last values of buffer template double RingBuffer::getMin(size_t amount) const { if (isEmpty() || amount <= 0) { return BUFMAX_VAL; } if (amount > count) amount = count; double minVal = BUFMAX_VAL; double value; for (size_t i = 0; i < amount; i++) { value = get(count - 1 - i); if (value < minVal && value != BUFMAX_VAL) { minVal = value; } } return minVal; } // Get the highest value in the buffer template double RingBuffer::getMax() const { return getMax(getCurrentSize()); } // Get maximum value of the last values of buffer template double RingBuffer::getMax(size_t amount) const { if (isEmpty() || amount <= 0) { return BUFMAX_VAL; } if (amount > count) amount = count; double maxVal = BUFMIN_VAL; double value; for (size_t i = 0; i < amount; i++) { value = get(count - 1 - i); if (value > maxVal && value != BUFMAX_VAL) { maxVal = value; } } if (maxVal == BUFMIN_VAL) { // no change of initial value -> buffer has only invalid values (BUFMAX_VAL) maxVal = BUFMAX_VAL; } return maxVal; } // Get mid value between and value in the buffer template double RingBuffer::getMid() const { return getMid(getCurrentSize()); } // Get mid value between and value of the last values of buffer template double RingBuffer::getMid(size_t amount) const { if (isEmpty() || amount <= 0) { return BUFMAX_VAL; } if (amount > count) amount = count; return (getMin(amount) + getMax(amount)) / 2; } // Get the median value in the buffer template double RingBuffer::getMedian() const { return getMedian(getCurrentSize()); } // Get the median value of the last values of buffer template double RingBuffer::getMedian(size_t amount) const { if (isEmpty() || amount <= 0) { return BUFMAX_VAL; } if (amount > count) amount = count; // Create a temporary vector with current valid elements std::vector temp; temp.reserve(amount); for (size_t i = 0; i < amount; i++) { temp.push_back(get(count - 1 - i)); } // Sort to find median std::sort(temp.begin(), temp.end()); if (temp[0] == BUFMAX_VAL) { // 1st element of sorted vector is already BUFMAX_VAL -> only invalid entries in buffer, so we return invalid value return BUFMAX_VAL; } if (amount % 2 == 1) { // Odd number of elements return temp[amount / 2]; } else { // Even number of elements - return average of middle two return (temp[amount / 2 - 1] + temp[amount / 2]) / 2; } } // Get mid value of circle (degree) values of buffer template double RingBuffer::getCircularMid() const { return getCircularMid(getCurrentSize()); } // Get mid value of circle (degree) values of the last values of buffer template double RingBuffer::getCircularMid(size_t amount) const { if (isEmpty() || amount <= 0) { return BUFMAX_VAL; } if (amount > count) amount = count; std::vector a; // Create a temporary vector with current valid elements std::vector temp; temp.reserve(amount); for (size_t i = 0; i < amount; i++) { temp.push_back(get(count - 1 - i)); } // Sort to find largest gap std::sort(temp.begin(), temp.end()); if (temp[0] == BUFMAX_VAL) { // 1st element of sorted vector is already BUFMAX_VAL -> only invalid entries in buffer, so we return invalid value return BUFMAX_VAL; } // Find the largest gap double largestGap = BUFMIN_VAL; std::size_t gapIndex = 0; for (std::size_t i = 0; i < temp.size(); ++i) { std::size_t next = (i + 1) % temp.size(); double gap; if (next == 0) gap = (temp[0] + M_TWOPI) - temp[i]; else gap = temp[next] - temp[i]; if (gap > largestGap) { largestGap = gap; gapIndex = i; } } double start = temp[(gapIndex + 1) % temp.size()]; // Start of occupied arc = first angle after largest gap double arcWidth = M_TWOPI - largestGap; // Width of occupied arc arcWidth = start + arcWidth / 2.0; arcWidth = fmod(arcWidth, M_TWOPI); if (arcWidth < 0.0) { arcWidth += M_TWOPI; } return arcWidth; // Midpoint of occupied arc // return WindUtils::to2PI(start + arcWidth / 2.0); // Midpoint of occupied arc } // Get the buffer capacity (maximum size) template size_t RingBuffer::getCapacity() const { return capacity; } // Get the current number of elements in the buffer template 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 { return last; } // Check if buffer is empty template bool RingBuffer::isEmpty() const { return count == 0; } // Check if buffer is full template bool RingBuffer::isFull() const { return is_Full; } // Get lowest possible value for buffer template double RingBuffer::getMinVal() const { return BUFMIN_VAL; } // Get highest possible value for buffer; used for unset/invalid buffer data template double RingBuffer::getMaxVal() const { return BUFMAX_VAL; } // Clear buffer template void RingBuffer::clear() { GWSYNCHRONIZED(&bufLocker); head = 0; first = 0; last = 0; count = 0; is_Full = false; } // Delete buffer and set new size template void RingBuffer::resize(size_t newSize) { GWSYNCHRONIZED(&bufLocker); capacity = newSize; head = 0; first = 0; last = 0; count = 0; is_Full = false; buffer.clear(); buffer.reserve(newSize); buffer.resize(newSize, NUMLIMIT_HIGH); } // Get all current values in native buffer format as a vector template std::vector RingBuffer::getAllValues() const { return getAllValues(getCurrentSize()); } // Get last values in native buffer format as a vector template std::vector RingBuffer::getAllValues(size_t amount) const { std::vector result; if (isEmpty() || amount <= 0) { return result; } if (amount > count) amount = count; result.reserve(amount); for (size_t i = 0; i < amount; i++) { result.push_back(get(count - 1 - i)); } return result; }