blob: becaa0752817c8bae8469a828e64cce476404163 [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FFL_EXPONENTIAL_AVERAGE_H_
#define FFL_EXPONENTIAL_AVERAGE_H_
#include <ffl/fixed.h>
namespace ffl {
// Represents an exponential moving average. It is calculated as follows:
//
// If the caller specifies only alpha, then a single-rate moving average is used. Letting Yn be the
// value of the last observation, the moving average is calculated as:
// Sn = Sn-1 + a * (Yn - Sn-1)
//
// If the caller specifies both alpha and beta, then a dual-rate moving average is used:
// D = Yn - Sn-1
// { Sn-1 + a * D if D < 0
// Sn = {
// { Sn-1 + b * D if D >= 0
//
// In either case, the smoothing factors (a and b) are always required to be between 0 and 1.
//
// This object also keeps track of the variance of the exponential moving average, which is
// calculated as follows:
//
// { (1 - a) * (Vn-1 + a * D^2) if D < 0
// Vn = {
// { (1 - b) * (Vn-1 + b * D^2) if D >= 0
//
// Because 0 <= a <= 1 and 0 <= b <= 1, this quantity must be non-negative.
template <typename Value, typename Alpha, typename Beta>
class ExponentialAverage;
template <typename V, size_t VFractionalBits, //
typename A, size_t AFractionalBits, //
typename B, size_t BFractionalBits> //
class ExponentialAverage<Fixed<V, VFractionalBits>, //
Fixed<A, AFractionalBits>, //
Fixed<B, BFractionalBits>> {
public:
using Value = Fixed<V, VFractionalBits>;
using Alpha = Fixed<A, AFractionalBits>;
using Beta = Fixed<B, BFractionalBits>;
constexpr ExponentialAverage(Value value, Alpha alpha)
: ExponentialAverage(value, alpha, alpha) {}
constexpr ExponentialAverage(Value value, Alpha alpha, Beta beta)
: average_(value), alpha_(alpha), beta_(beta) {
if (alpha < 0) {
__builtin_abort();
}
if (alpha > 1) {
__builtin_abort();
}
if (beta < 0) {
__builtin_abort();
}
if (beta > 1) {
__builtin_abort();
}
}
constexpr void AddSample(Value sample) {
const Value delta = sample - average_;
if (delta >= 0) {
average_ += Value{beta_ * delta};
variance_ = (Beta(1) - beta_) * (variance_ + (beta_ * delta) * delta);
} else {
average_ += Value{alpha_ * delta};
variance_ = (Alpha(1) - alpha_) * (variance_ + (alpha_ * delta) * delta);
}
}
constexpr Value value() { return average_; }
constexpr Value variance() { return variance_; }
private:
Value average_;
Value variance_ = Value{0};
const Alpha alpha_;
const Beta beta_;
};
// User-defined deduction guide for construction of a single-rate exponential average.
template <typename V, size_t VFractionalBits, //
typename A, size_t AFractionalBits> //
ExponentialAverage(Fixed<V, VFractionalBits>, //
Fixed<A, AFractionalBits>) //
->ExponentialAverage<Fixed<V, VFractionalBits>, //
Fixed<A, AFractionalBits>, //
Fixed<A, AFractionalBits>>;
// User-defined deduction guide for construction of a dual-rate exponential average.
template <typename V, size_t VFractionalBits, //
typename A, size_t AFractionalBits, //
typename B, size_t BFractionalBits> //
ExponentialAverage(Fixed<V, VFractionalBits>, //
Fixed<A, AFractionalBits>, //
Fixed<B, BFractionalBits>) //
->ExponentialAverage<Fixed<V, VFractionalBits>, //
Fixed<A, AFractionalBits>, //
Fixed<B, BFractionalBits>>;
} // namespace ffl
#endif // FFL_EXPONENTIAL_AVERAGE_H_