Skip to main content
The EWMA (Exponentially Weighted Moving Average) filter automatically adjusts smoothing strength to minimize lag when moving and minimize noise when still. It’s an excellent general-purpose filter that requires minimal tuning.

How It Works

The EWMA filter uses delta filtering and noise variance detection to distinguish intentional movement from sensor noise:
  1. Delta filtering: Smooths the rate of change over the last 1/60 second (16ms)
  2. Noise estimation: Tracks average noise variance over the last 60 seconds
  3. Adaptive smoothing: Scales filtering from maxSmooth to minSmooth based on detected motion

Algorithm

The filter compares current delta to historical noise statistics:
// Smooth the delta over 1/60 sec
delta_alpha = dt / (dt + 0.0167)  // delta_RC = 1/60
last_delta[i] += delta_alpha * (delta - last_delta[i])

// Smooth noise variance over 60 sec
noise_alpha = dt / (dt + noise_RC)
last_noise[i] = noise_alpha * noise + (1 - noise_alpha) * last_noise[i]

// Normalize noise: 0->1 for 0->9 variances (0->3 std devs)
norm_noise = min(noise / (9.0 * last_noise[i]), 1.0)

// Calculate smoothing strength
smoothing = 1.0 - pow(norm_noise, smoothing_scale_curve)
RC = min_smoothing + smoothing * (max_smoothing - min_smoothing)

// Apply adaptive alpha
alpha = dt / (dt + RC)
output[i] += alpha * (input[i] - output[i])
As the delta increases from 0 to 3 standard deviations of the noise, the filtering scales down from maxSmooth to minSmooth at a rate controlled by smoothing_scale_curve.
The EWMA filter automatically adapts to your tracking environment over the first ~60 seconds. No manual calibration is required.

Parameters

The EWMA filter has only three parameters, making it easy to configure:
min_smoothing
slider
default:"0.02"
Minimum smoothing during fast movement.
  • Lower values: Faster response, more visible jitter during motion
  • Higher values: More smoothing even during fast movements, increased lag
Default: 0.02 (2%)
max_smoothing
slider
default:"0.7"
Maximum smoothing when stationary or moving slowly.
  • Lower values: Less jitter reduction when still, faster initial response
  • Higher values: Maximum jitter suppression, slower initial response
Default: 0.7 (70%)
The filter ensures max_smoothing >= min_smoothing automatically.
smoothing_scale_curve
slider
default:"0.8"
Shape of the transition from max to min smoothing.
  • Lower values (< 1.0): Opens up earlier, more immediate feel
  • Higher values (> 1.0): Keeps heavy smoothing longer during motion
Default: 0.8This is the exponent applied to norm_noise in the smoothing calculation:
smoothing = 1.0 - pow(norm_noise, smoothing_scale_curve)

Tuning Guide

For Simulators and Precision Work

Maximize smoothness when stationary:
min_smoothing: 0.01
max_smoothing: 0.9
smoothing_scale_curve: 1.2
  • Very low min_smoothing (0.01) for minimal lag during intentional movement
  • High max_smoothing (0.9) for maximum jitter reduction when still
  • Curve > 1.0 keeps heavy smoothing applied longer during small motions

For Fast Action Games

Minimize lag while maintaining some smoothing:
min_smoothing: 0.05
max_smoothing: 0.5
smoothing_scale_curve: 0.5
  • Higher min_smoothing (0.05) ensures some smoothing even during fast turns
  • Lower max_smoothing (0.5) reduces lag when starting to move
  • Curve < 1.0 opens up quickly as soon as motion is detected

General Purpose (Default)

Balanced for most use cases:
min_smoothing: 0.02
max_smoothing: 0.7
smoothing_scale_curve: 0.8

How Noise Detection Works

The EWMA filter builds a statistical model of your tracking noise:
1

Measure instantaneous delta

Calculate the difference between input and last output for each axis.
double delta = input[i] - last_output[i]
2

Smooth delta over 1/60 sec

Apply fast exponential smoothing to the delta itself:
last_delta[i] += delta_alpha * (delta - last_delta[i])
This removes very high-frequency noise components.
3

Track variance over 60 sec

Build a long-term estimate of noise variance:
double noise = last_delta[i] * last_delta[i]
last_noise[i] = noise_alpha * noise + (1 - noise_alpha) * last_noise[i]
This converges to the typical noise level of your tracker.
4

Normalize current noise

Compare instantaneous noise to historical variance:
double norm_noise = min(noise / (9.0 * last_noise[i]), 1.0)
  • 0.0 = noise within expected bounds (probably stationary)
  • 1.0 = noise 3+ standard deviations above normal (intentional movement)
5

Calculate adaptive alpha

Map normalized noise through the curve to determine smoothing:
double smoothing = 1.0 - pow(norm_noise, smoothing_scale_curve)
double RC = min_smoothing + smoothing * (max_smoothing - min_smoothing)
double alpha = dt / (dt + RC)
The filter takes approximately 60 seconds to converge to accurate noise statistics after startup. During this period, noise_RC gradually increases from 0 to 60 seconds.

Implementation Details

Time Constants

The filter uses two time constants:
static constexpr double delta_RC = 1.0 / 60.0;  // 16.7ms
static constexpr double noise_RC_max = 60.0;     // 60 seconds
  • delta_RC: Fast response for instantaneous motion detection
  • noise_RC_max: Slow convergence for stable noise statistics

Angle Wrap-Around

Rotation angles (Yaw, Pitch, Roll) crossing ±180° are handled automatically:
if (i >= 3 && fabs(delta) > 180.0)  // rotation axis
{
    delta -= copysign(360.0, delta);
    input_value -= copysign(360.0, input_value);
}
This prevents discontinuities when your head rotates through North.

State Reset

When recentering, the filter resets its state:
void center() override { first_run = true; }
On the next frame:
  • noise_RC resets to 0
  • Output jumps to current input
  • Delta and noise arrays clear
After recentering, the filter needs ~60 seconds to rebuild accurate noise statistics. You may experience suboptimal smoothing during this convergence period.

Comparison to Other Filters

FeatureEWMAAlpha SpectrumHamiltonAccela
Auto noise detection✅ Yes✅ Yes❌ No❌ No
Parameters to tune38+84
Convergence time~60sImmediateImmediateImmediate
Rotation handlingPer-axisPer-axisQuaternionPer-axis
Predictive heads❌ No✅ Yes❌ No❌ No
CPU usageLowMediumLowLow
Use EWMA if:
  • You want automatic adaptation without manual tuning
  • You have a consistent tracking environment
  • You prefer simplicity over maximum control
Use Alpha Spectrum if:
  • You need predictive filtering for extremely low latency
  • You want separate control over rotation vs translation
  • You’re willing to tune advanced parameters
  • You need real-time status monitoring

Troubleshooting

  • Decrease max_smoothing to 0.5 or lower
  • Decrease smoothing_scale_curve to open up faster
  • Consider switching to Accela filter for velocity-based response
  • Increase max_smoothing to 0.8 or 0.9
  • Wait for full convergence (~60 seconds)
  • Check tracker mounting and lighting conditions
  • Verify noise isn’t from external vibration (fans, desk movement)
This is normal behavior. The filter needs ~60 seconds to rebuild noise statistics after recentering.If this is problematic, consider using Hamilton or Alpha Spectrum which don’t require convergence time.
The EWMA filter applies the same parameters to all 6 axes (X, Y, Z, Yaw, Pitch, Roll).If you need independent control, use Alpha Spectrum which has separate rotation and translation parameters.

Code Reference

Relevant source files:
  • Implementation: filter-ewma2/ftnoir_filter_ewma2.cpp
  • Header/settings: filter-ewma2/ftnoir_filter_ewma2.h
  • Dialog: filter-ewma2/ftnoir_filter_ewma2_dialog.cpp

Key Implementation Lines

Noise normalization (ftnoir_filter_ewma2.cpp:74):
double norm_noise = last_noise[i] < 1e-10 ? 0 : 
                    std::fmin(noise/(9.0*last_noise[i]), 1.0);
Smoothing calculation (ftnoir_filter_ewma2.cpp:76-77):
double smoothing = 1.0 - pow(norm_noise, smoothing_scale_curve);
double RC = (min_smoothing + smoothing*(max_smoothing - min_smoothing));
Dynamic alpha (ftnoir_filter_ewma2.cpp:79):
double alpha = dt/(dt + RC);

Next Steps

Filter Overview

Compare all available filters

Alpha Spectrum

Advanced filter with predictive heads

Hamilton Filter

Quaternion-based smooth rotations

Accela Filter

Velocity-dependent acceleration filtering