use ::core::marker::PhantomData;
use ::core::ops::{Add, Mul, Neg, Sub};
use nalgebra::{RealField, SVector, Scalar};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StateSpace;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MeasurementSpace;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InnovationSpace;
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq)]
pub struct Vector<T: Scalar, const N: usize, Space> {
inner: SVector<T, N>,
_marker: PhantomData<Space>,
}
impl<T: Scalar, const N: usize, Space> Vector<T, N, Space> {
#[inline]
pub fn from_array(data: [T; N]) -> Self {
Self {
inner: SVector::from(data),
_marker: PhantomData,
}
}
#[inline]
pub fn from_svector(inner: SVector<T, N>) -> Self {
Self {
inner,
_marker: PhantomData,
}
}
#[inline]
pub fn as_svector(&self) -> &SVector<T, N> {
&self.inner
}
#[inline]
pub fn into_svector(self) -> SVector<T, N> {
self.inner
}
#[inline]
pub fn as_slice(&self) -> &[T] {
self.inner.as_slice()
}
#[inline]
pub fn get(&self, index: usize) -> Option<&T> {
self.inner.get(index)
}
#[inline]
#[allow(clippy::should_implement_trait)]
pub fn index(&self, index: usize) -> &T {
&self.inner[index]
}
}
impl<T: Scalar + Copy, const N: usize, Space: Clone> Copy for Vector<T, N, Space> {}
impl<T: RealField + Copy, const N: usize, Space> Vector<T, N, Space> {
#[inline]
pub fn zeros() -> Self {
Self {
inner: SVector::zeros(),
_marker: PhantomData,
}
}
#[inline]
pub fn norm_squared(&self) -> T {
self.inner.norm_squared()
}
#[inline]
pub fn norm(&self) -> T {
self.inner.norm()
}
#[inline]
pub fn scale(&self, s: T) -> Self {
Self {
inner: self.inner.scale(s),
_marker: PhantomData,
}
}
}
pub type StateVector<T, const N: usize> = Vector<T, N, StateSpace>;
pub type Measurement<T, const M: usize> = Vector<T, M, MeasurementSpace>;
pub type Innovation<T, const M: usize> = Vector<T, M, InnovationSpace>;
impl<T: RealField + Copy, const N: usize, Space> Add for Vector<T, N, Space> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
inner: self.inner + rhs.inner,
_marker: PhantomData,
}
}
}
impl<T: RealField + Copy, const N: usize, Space> Sub for Vector<T, N, Space> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self {
inner: self.inner - rhs.inner,
_marker: PhantomData,
}
}
}
impl<T: RealField + Copy, const N: usize, Space> Neg for Vector<T, N, Space> {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self {
inner: -self.inner,
_marker: PhantomData,
}
}
}
impl<T: RealField + Copy, const N: usize, Space> Mul<T> for Vector<T, N, Space> {
type Output = Self;
#[inline]
fn mul(self, rhs: T) -> Self::Output {
Self {
inner: self.inner * rhs,
_marker: PhantomData,
}
}
}
pub trait ComputeInnovation<T: RealField, const M: usize> {
fn innovation(self, predicted: Measurement<T, M>) -> Innovation<T, M>;
}
impl<T: RealField + Copy, const M: usize> ComputeInnovation<T, M> for Measurement<T, M> {
#[inline]
fn innovation(self, predicted: Measurement<T, M>) -> Innovation<T, M> {
Innovation {
inner: self.inner - predicted.inner,
_marker: PhantomData,
}
}
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq)]
pub struct Covariance<T: Scalar, const N: usize, Space> {
inner: nalgebra::SMatrix<T, N, N>,
_marker: PhantomData<Space>,
}
impl<T: Scalar, const N: usize, Space> Covariance<T, N, Space> {
#[inline]
pub fn from_matrix(inner: nalgebra::SMatrix<T, N, N>) -> Self {
Self {
inner,
_marker: PhantomData,
}
}
#[inline]
pub fn as_matrix(&self) -> &nalgebra::SMatrix<T, N, N> {
&self.inner
}
#[inline]
pub fn into_matrix(self) -> nalgebra::SMatrix<T, N, N> {
self.inner
}
}
impl<T: Scalar + Copy, const N: usize, Space: Clone> Copy for Covariance<T, N, Space> where
nalgebra::SMatrix<T, N, N>: Copy
{
}
impl<T: RealField + Copy, const N: usize, Space> Covariance<T, N, Space> {
#[inline]
pub fn zeros() -> Self {
Self {
inner: nalgebra::SMatrix::zeros(),
_marker: PhantomData,
}
}
#[inline]
pub fn identity() -> Self {
Self {
inner: nalgebra::SMatrix::identity(),
_marker: PhantomData,
}
}
#[inline]
pub fn from_diagonal(diag: &SVector<T, N>) -> Self {
Self {
inner: nalgebra::SMatrix::from_diagonal(diag),
_marker: PhantomData,
}
}
#[inline]
pub fn scale(&self, s: T) -> Self {
Self {
inner: self.inner.scale(s),
_marker: PhantomData,
}
}
#[inline]
pub fn add(&self, other: &Self) -> Self {
Self {
inner: self.inner + other.inner,
_marker: PhantomData,
}
}
#[inline]
pub fn trace(&self) -> T {
self.inner.trace()
}
#[inline]
pub fn determinant_cholesky(&self) -> Option<T> {
let chol = nalgebra::Cholesky::new(self.inner)?;
let l = chol.l();
let mut det_l = T::one();
for i in 0..N {
det_l *= l[(i, i)];
}
Some(det_l * det_l)
}
#[inline]
pub fn determinant(&self) -> Option<T> {
self.determinant_cholesky()
}
#[inline]
pub fn try_inverse(&self) -> Option<Self> {
self.inner.try_inverse().map(|inner| Self {
inner,
_marker: PhantomData,
})
}
#[inline]
pub fn cholesky(&self) -> Option<nalgebra::SMatrix<T, N, N>> {
nalgebra::Cholesky::new(self.inner).map(|c| c.l())
}
}
impl<T: RealField + Copy, const N: usize, Space> Add for Covariance<T, N, Space> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
inner: self.inner + rhs.inner,
_marker: PhantomData,
}
}
}
pub type StateCovariance<T, const N: usize> = Covariance<T, N, StateSpace>;
pub type MeasurementCovariance<T, const M: usize> = Covariance<T, M, MeasurementSpace>;
pub type InnovationCovariance<T, const M: usize> = Covariance<T, M, InnovationSpace>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_state_vector_operations() {
let v1: StateVector<f64, 4> = StateVector::from_array([1.0, 2.0, 3.0, 4.0]);
let v2: StateVector<f64, 4> = StateVector::from_array([0.5, 1.0, 1.5, 2.0]);
let sum = v1 + v2;
assert!((sum.index(0) - 1.5).abs() < 1e-10);
assert!((sum.index(1) - 3.0).abs() < 1e-10);
}
#[test]
fn test_measurement_to_innovation() {
let actual: Measurement<f64, 2> = Measurement::from_array([10.0, 20.0]);
let predicted: Measurement<f64, 2> = Measurement::from_array([9.5, 19.0]);
let innovation = actual.innovation(predicted);
assert!((innovation.index(0) - 0.5).abs() < 1e-10);
assert!((innovation.index(1) - 1.0).abs() < 1e-10);
}
#[test]
fn test_covariance_operations() {
let cov: StateCovariance<f64, 2> = StateCovariance::identity();
assert!((cov.trace() - 2.0).abs() < 1e-10);
assert!((cov.determinant().unwrap() - 1.0).abs() < 1e-10);
}
#[test]
fn test_singular_covariance_determinant() {
let singular: StateCovariance<f64, 2> =
StateCovariance::from_matrix(nalgebra::matrix![1.0, 1.0; 1.0, 1.0]);
assert!(singular.determinant().is_none());
}
}