# `Controls.Ewma`
[🔗](https://github.com/harmont-dev/hyper/blob/main/lib/controls/ewma.ex#L1)

First-order exponential moving average - a discrete low-pass filter (LPF) with
an irregular-sampling-correct gain.

The continuous first-order LPF `tau*y' + y = x` has the exact discrete solution,
for a step-held input over an interval `dt`:

    alpha  = 1 - exp(-dt/tau)
    y_n = alpha*x_n + (1-alpha)*y_{n-1}

Deriving `alpha` from the *measured* `dt` (never a hardcoded constant) pins the
filter's cutoff at `1/(2*pi*tau)` regardless of scheduler jitter or differing
per-monitor sample periods. `tau` (`tau_ms`) is the time constant: the output
reaches ~63 % of a step after one `tau` and ~95 % after `3tau`. The first sample
seeds the filter directly, avoiding a warm-up ramp from zero.

A sample is either a plain number or any `Unit.Quantity` (a `Unit.Information`,
a `Unit.Bandwidth`, ...). The filter is written as `y + alpha*(x - y)` using the
unit-aware `+`/`-` from `Unit.Operators`, with the `alpha*` scaling done on the
quantity's canonical scalar via `Unit.Quantity`. A filtered reading therefore
keeps its unit, and the filter is not tied to `float()`.

# `sample`

```elixir
@type sample() :: number() | Unit.Quantity.t()
```

A filterable sample: a plain number or any unit quantity.

# `t`

```elixir
@type t() :: %Controls.Ewma{tau_ms: pos_integer(), value: sample() | nil}
```

# `new`

```elixir
@spec new(pos_integer()) :: t()
```

Build a filter with time constant `tau_ms` (milliseconds).

# `update`

```elixir
@spec update(t(), sample(), pos_integer()) :: t()
```

Fold one `sample`, taken `dt_ms` after the previous one, into the filter.

The first sample seeds the average (its `dt_ms` is ignored).

# `value`

```elixir
@spec value(t()) :: sample() | nil
```

The current filtered value, or `nil` before the first sample.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
