Skip to main content

aegis_filter/
common.rs

1//! Core mathematical types, errors, and `#![no_std]` floating point helpers.
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub enum FilterError {
6    /// Provided float was NaN or infinite.
7    NonFiniteInput,
8    /// Provided covariance is non-positive definite or ill-conditioned.
9    InvalidCovariance,
10    /// Innovation covariance is singular or ill-conditioned for inversion.
11    SingularInnovation,
12    /// Measurement exceeded gating threshold and was rejected as an outlier.
13    MeasurementGated,
14    /// Internal matrix inversion failure or non-positive square root.
15    NumericalInstability,
16    /// Invalid parameter value provided (e.g., negative variance or zero threshold).
17    InvalidParameter,
18    /// Static capacity exceeded for fixed-size sensor buffers.
19    CapacityExceeded,
20}
21
22impl core::fmt::Display for FilterError {
23    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
24        match self {
25            Self::NonFiniteInput => write!(
26                f,
27                "Input contains non-finite floating point values (NaN or Inf)"
28            ),
29            Self::InvalidCovariance => write!(f, "Covariance is non-positive or ill-conditioned"),
30            Self::SingularInnovation => {
31                write!(f, "Innovation covariance is singular or ill-conditioned")
32            }
33            Self::MeasurementGated => write!(
34                f,
35                "Measurement was gated out as an outlier or adversarial spike"
36            ),
37            Self::NumericalInstability => write!(
38                f,
39                "Numerical instability occurred during matrix factorization or square root"
40            ),
41            Self::InvalidParameter => write!(f, "Invalid parameter value provided to filter"),
42            Self::CapacityExceeded => write!(f, "Maximum registered sensor capacity exceeded"),
43        }
44    }
45}
46
47#[cfg(feature = "std")]
48impl std::error::Error for FilterError {}
49
50/// Trait providing safe, deterministic `#![no_std]` math operations for f64.
51pub trait FloatOps {
52    fn is_valid_float(&self) -> bool;
53    fn abs_val(self) -> f64;
54    fn sqrt_val(self) -> f64;
55}
56
57impl FloatOps for f64 {
58    #[inline(always)]
59    fn is_valid_float(&self) -> bool {
60        self.is_finite()
61    }
62
63    #[inline(always)]
64    fn abs_val(self) -> f64 {
65        f64::from_bits(self.to_bits() & !(1u64 << 63))
66    }
67
68    #[inline]
69    fn sqrt_val(self) -> f64 {
70        #[cfg(feature = "std")]
71        {
72            self.sqrt()
73        }
74        #[cfg(not(feature = "std"))]
75        {
76            if self <= 0.0 {
77                return 0.0;
78            }
79            if !self.is_finite() {
80                return self;
81            }
82            let mut curr = self;
83            for _ in 0..24 {
84                let prev = curr;
85                curr = 0.5 * (curr + self / curr);
86                if (curr - prev).abs_val() <= 1e-15 * curr {
87                    break;
88                }
89            }
90            curr
91        }
92    }
93}