# `Sidereon.Estimation`
[🔗](https://github.com/neilberkman/sidereon-ex/blob/main/lib/sidereon/estimation.ex#L874)

Scalar estimation and detection primitives.

These functions delegate to `sidereon-core` estimation primitives. Inputs are
plain scalar values; state units follow the caller's level unit and the
supplied `dt`. Innovation variance is in squared measurement units, NIS gates
use chi-square degrees of freedom, and CA-CFAR functions use the exponential
cell-averaging model.

# `primitive_error`

```elixir
@type primitive_error() :: {:invalid_input, String.t(), String.t()}
```

# `alpha_beta_apply_measurement`

```elixir
@spec alpha_beta_apply_measurement(
  Sidereon.Estimation.AlphaBetaState.t() | map(),
  number(),
  number(),
  Sidereon.Estimation.AlphaBetaGains.t() | map()
) ::
  {:ok, Sidereon.Estimation.AlphaBetaState.t()}
  | {:error, primitive_error() | :invalid_state | :invalid_gains}
```

Apply one scalar measurement to a predicted alpha-beta state.

# `alpha_beta_filter_step`

```elixir
@spec alpha_beta_filter_step(
  Sidereon.Estimation.AlphaBetaState.t() | map(),
  number(),
  number(),
  Sidereon.Estimation.AlphaBetaGains.t() | map()
) ::
  {:ok, Sidereon.Estimation.AlphaBetaStep.t()}
  | {:error, primitive_error() | :invalid_state | :invalid_gains}
```

Run one alpha-beta predict and measurement update step.

# `alpha_beta_predict`

```elixir
@spec alpha_beta_predict(Sidereon.Estimation.AlphaBetaState.t() | map(), number()) ::
  {:ok, Sidereon.Estimation.AlphaBetaState.t()}
  | {:error, primitive_error() | :invalid_state}
```

Project an alpha-beta state by `dt` without applying a measurement.

# `alpha_beta_steady_state_gains`

```elixir
@spec alpha_beta_steady_state_gains(number()) ::
  {:ok, Sidereon.Estimation.AlphaBetaGains.t()} | {:error, primitive_error()}
```

Compute steady-state alpha-beta gains from a positive tracking index.

# `cfar_ca_false_alarm_probability`

```elixir
@spec cfar_ca_false_alarm_probability(pos_integer(), number(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

CA-CFAR false-alarm probability from absolute threshold and noise level.

# `cfar_ca_multiplier_from_pfa`

```elixir
@spec cfar_ca_multiplier_from_pfa(pos_integer(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

CA-CFAR threshold multiplier from searched-cell count and target false-alarm probability.

# `cfar_ca_pfa_from_multiplier`

```elixir
@spec cfar_ca_pfa_from_multiplier(pos_integer(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

CA-CFAR false-alarm probability from searched-cell count and multiplier.

# `cfar_ca_threshold`

```elixir
@spec cfar_ca_threshold(pos_integer(), number(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

CA-CFAR absolute threshold from searched cells, target false-alarm probability, and noise level.

# `ewma`

```elixir
@spec ewma(number(), number(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

Exponentially weighted moving average update.

`alpha` must be in `[0, 1]`.

# `ewma_power_of_two`

```elixir
@spec ewma_power_of_two(number(), number(), non_neg_integer()) ::
  {:ok, float()} | {:error, primitive_error()}
```

EWMA update with `alpha = 1 / 2^shift`.

# `ewma_update`

```elixir
@spec ewma_update(number(), number(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

Exponentially weighted moving average update.

This mirrors the canonical `sidereon-core` helper name; `ewma/3` remains
available as the shorter Elixir alias.

# `ewma_update_power_of_two`

```elixir
@spec ewma_update_power_of_two(number(), number(), non_neg_integer()) ::
  {:ok, float()} | {:error, primitive_error()}
```

EWMA update with `alpha = 1 / 2^shift`.

This mirrors the canonical `sidereon-core` helper name;
`ewma_power_of_two/3` remains available as the shorter Elixir alias.

# `kalman_cv_steady_state_gains`

```elixir
@spec kalman_cv_steady_state_gains(number(), number(), number()) ::
  {:ok, Sidereon.Estimation.ScalarKalmanGains.t()} | {:error, primitive_error()}
```

Compute steady-state gains for the scalar constant-velocity Kalman model.

`dt` is the sample interval in seconds and `measurement_variance` is in
squared measurement units.

# `mad`

```elixir
@spec mad([number()], number()) :: {:ok, float()} | {:error, primitive_error()}
```

Median absolute deviation spread estimate with Gaussian consistency scaling.

`scale_floor` is a non-negative lower bound on the returned spread.

# `mad_gaussian_consistency`

```elixir
@spec mad_gaussian_consistency() :: float()
```

MAD Gaussian consistency factor `1 / Phi^-1(3/4)`.

# `mad_spread`

```elixir
@spec mad_spread([number()], number()) :: {:ok, float()} | {:error, primitive_error()}
```

Median absolute deviation spread estimate with Gaussian consistency scaling.

This mirrors the canonical `sidereon-core` helper name; `mad/2` remains
available as the shorter Elixir alias.

# `nis`

```elixir
@spec nis(number(), number()) :: {:ok, float()} | {:error, primitive_error()}
```

Normalized innovation squared statistic.

# `nis_expected_value`

```elixir
@spec nis_expected_value(pos_integer()) ::
  {:ok, float()} | {:error, primitive_error()}
```

Expected NIS value for a positive number of degrees of freedom.

# `nis_gate`

```elixir
@spec nis_gate(number(), number(), pos_integer(), number()) ::
  {:ok, Sidereon.Estimation.NisGate.t()} | {:error, primitive_error()}
```

Test a scalar innovation against a chi-square NIS gate.

# `nis_gate_test`

```elixir
@spec nis_gate_test(number(), number(), pos_integer(), number()) ::
  {:ok, Sidereon.Estimation.NisGate.t()} | {:error, primitive_error()}
```

Test a scalar innovation against a chi-square NIS gate.

This mirrors the canonical `sidereon-core` helper name; `nis_gate/4` remains
available as the shorter Elixir alias.

# `nis_gate_threshold`

```elixir
@spec nis_gate_threshold(pos_integer(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

Chi-square gate threshold for `dof` and confidence in `(0, 1)`.

# `nis_statistic`

```elixir
@spec nis_statistic(number(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

Normalized innovation squared statistic.

This is the canonical `sidereon-core` helper name; `nis/2` is retained as the
shorter Elixir alias.

# `normalized_innovation`

```elixir
@spec normalized_innovation(number(), number()) ::
  {:ok, float()} | {:error, primitive_error()}
```

Scalar normalized innovation `innovation / sqrt(innovation_variance)`.

# `smooth_track_rts`

```elixir
@spec smooth_track_rts(Sidereon.Estimation.TrackRtsHistory.t()) ::
  {:ok, Sidereon.Estimation.SmoothedTrack.t()} | {:error, term()}
```

Apply a fixed-interval RTS smoother to a recorded no-IMU track history.

---

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