This class performs averaging with a fixed time window (variable number of values).
From the platform, it only needs some method to get the current time. This code contains implementations for both ESP-IDF and Arduino
#pragma once #include <optional> // Find the correct driver #ifdef ARDUINO #include <Arduino.h> #define TTAA_GETTIME_MILLIS millis() #elif defined(IDF_VER) && !defined(ARDUINO) #include <esp_timer.h> #define TTAA_GETTIME_MILLIS esp_timer_get_time() / 1000 #else #error "Could not determine how to get the current time (Arduino or ESP-IDF)" #endif /** * @brief This class is designed to accumulate and average values over a specified time threshold. * * The class is templated to allow accumulation of different numerical types, defaulting to float. It uses the Arduino `millis()` * function to track elapsed time, automatically calculating the average of accumulated values once the predefined time threshold is reached. * The `add()` method adds a new value to the accumulation, returning the average as a `std::optional<T>` when the threshold is reached, * and `std::nullopt` otherwise. The `lastAverage()` method allows retrieval of the last calculated average at any time. */ template<typename T = float> class TimeThresholdAccumulatingAverager { public: /** * Constructs a new TimeThresholdAccumulatingAverager object. * * @param threshold The time threshold in milliseconds over which to accumulate and average values. */ TimeThresholdAccumulatingAverager(unsigned long threshold) : _threshold(threshold), _startTime(TTAA_GETTIME_MILLIS), _sum(), _count() {} /** * Adds a value to the accumulator. If the time threshold has been reached, * calculates and returns the average of the accumulated values since the last threshold reset. * Resets the accumulator for the next period if the threshold is reached. * * The given value is not added to the current average if the threshold has been * exceeded. It is automatically added to the next period's average. * * @param value The value to add to the accumulator. * @return std::optional<T> The average of accumulated values if the threshold is reached, otherwise std::nullopt. */ std::optional<T> add(T value) { unsigned long currentTime = TTAA_GETTIME_MILLIS; if (currentTime - _startTime >= _threshold) { // Time threshold exceeded. Calculcate average T average = _count > 0 ? _sum / static_cast<T>(_count) : 0; _lastCount = _count; // Reset sum & count to zero, but add the current value immediatey _sum = value; _count = 1; _startTime = currentTime; _lastAverage = average; return average; } else { // Time threshold not exceeded _sum += value; _count++; return std::nullopt; } } /** * Returns the average of the last completed accumulation period. * * @return T The average value of the last completed period. */ inline T lastAverage() const { return _lastAverage; } /** * Returns the count value of the last completed accumulation period. * * @return The last count value. */ inline unsigned int lastCount() const { return _lastCount; } private: unsigned long _threshold; unsigned long _startTime; T _sum; int _count = 0; T _lastAverage = 0; unsigned int _lastCount = 0; };
Usage example:
#include <optional> #include <iostream> #include "TimeThresholdAccumulatingAverager.hpp" int main() { TimeThresholdAccumulatingAverager<float> averager(1000); // 1 second threshold while (true) { float newValue = acquireValue(); // Pseudocode for acquiring a new value auto maybeAverage = averager.add(newValue); if (maybeAverage.has_value()) { std::cout << "Average over threshold period: " << average.value() << std::endl; } // Add a delay or wait for a real new value in a real application delay(10); } }