Program Listing for File ewma_covariance_provider.cpp
↰ Return to documentation for file (src/ewma_covariance_provider.cpp)
// Copyright 2026 Analog Devices, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "adi_imu/ewma_covariance_provider.h"
#include <algorithm>
#include <cmath>
#include <utility>
namespace adi_imu
{
EwmaCovarianceProvider::EwmaCovarianceProvider(
double alpha, size_t warmup_samples, double min_variance, MotionDetector motion_detector)
: m_alpha(alpha),
m_warmup_samples(warmup_samples),
m_sample_count(0),
m_min_variance(min_variance),
m_motion_detector(std::move(motion_detector)),
m_accel_mean{},
m_accel_var{},
m_gyro_mean{},
m_gyro_var{}
{
}
void EwmaCovarianceProvider::addSample(const Vec3 & accel, const Vec3 & gyro)
{
// Guard against NaN from sensor
if (
std::isnan(accel.x) || std::isnan(accel.y) || std::isnan(accel.z) || std::isnan(gyro.x) ||
std::isnan(gyro.y) || std::isnan(gyro.z)) {
return;
}
// Skip non-stationary samples to avoid motion-induced variance inflation
if (!m_motion_detector.isStationary(accel, gyro)) {
return;
}
// Only increment until warmup completes - prevents overflow
if (m_sample_count < m_warmup_samples) {
m_sample_count++;
}
if (m_sample_count == 1) {
// Initialize with first sample
m_accel_mean = accel;
m_gyro_mean = gyro;
return;
}
// EWMA mean update: mean_t = alpha * x_t + (1-alpha) * mean_{t-1}
auto updateMean = [this](double x, double & mean) {
mean = m_alpha * x + (1.0 - m_alpha) * mean;
};
// EWMA variance update: var_t = alpha * (x_t - mean_t)^2 + (1-alpha) * var_{t-1}
auto updateVar = [this](double x, double mean, double & var) {
double diff = x - mean;
var = m_alpha * (diff * diff) + (1.0 - m_alpha) * var;
};
// Update accelerometer
updateVar(accel.x, m_accel_mean.x, m_accel_var.x);
updateVar(accel.y, m_accel_mean.y, m_accel_var.y);
updateVar(accel.z, m_accel_mean.z, m_accel_var.z);
updateMean(accel.x, m_accel_mean.x);
updateMean(accel.y, m_accel_mean.y);
updateMean(accel.z, m_accel_mean.z);
// Update gyroscope
updateVar(gyro.x, m_gyro_mean.x, m_gyro_var.x);
updateVar(gyro.y, m_gyro_mean.y, m_gyro_var.y);
updateVar(gyro.z, m_gyro_mean.z, m_gyro_var.z);
updateMean(gyro.x, m_gyro_mean.x);
updateMean(gyro.y, m_gyro_mean.y);
updateMean(gyro.z, m_gyro_mean.z);
}
bool EwmaCovarianceProvider::isReady() const { return m_sample_count >= m_warmup_samples; }
CovarianceMatrix EwmaCovarianceProvider::getAccelCovariance() const
{
return {std::max(m_accel_var.x, m_min_variance), 0.0, 0.0, 0.0,
std::max(m_accel_var.y, m_min_variance), 0.0, 0.0, 0.0,
std::max(m_accel_var.z, m_min_variance)};
}
CovarianceMatrix EwmaCovarianceProvider::getGyroCovariance() const
{
return {std::max(m_gyro_var.x, m_min_variance), 0.0, 0.0, 0.0,
std::max(m_gyro_var.y, m_min_variance), 0.0, 0.0, 0.0,
std::max(m_gyro_var.z, m_min_variance)};
}
void EwmaCovarianceProvider::reset()
{
m_sample_count = 0;
m_accel_mean = Vec3();
m_accel_var = Vec3();
m_gyro_mean = Vec3();
m_gyro_var = Vec3();
}
double EwmaCovarianceProvider::getCalibrationProgress() const
{
if (m_sample_count >= m_warmup_samples) {
return 1.0;
}
return static_cast<double>(m_sample_count) / static_cast<double>(m_warmup_samples);
}
} // namespace adi_imu