#![allow(non_snake_case)]
use super::traits::{FilterState, KalmanFilter, StepResult, UpdateResult};
use nalgebra::{DMatrix, DVector};
pub struct LinearKF {
n: usize,
m: usize,
F: DMatrix<f64>,
H: DMatrix<f64>,
Q: DMatrix<f64>,
R: DMatrix<f64>,
x: DVector<f64>,
P: DMatrix<f64>,
I: DMatrix<f64>,
}
impl LinearKF {
pub fn new(
F: DMatrix<f64>,
H: DMatrix<f64>,
Q: DMatrix<f64>,
R: DMatrix<f64>,
x0: &[f64],
P0: DMatrix<f64>,
) -> Self {
let n = F.nrows();
let m = H.nrows();
let x = DVector::from_row_slice(x0);
let I = DMatrix::identity(n, n);
LinearKF {
n,
m,
F,
H,
Q,
R,
x,
P: P0,
I,
}
}
}
impl KalmanFilter for LinearKF {
fn predict(&mut self, _dt: f64) -> FilterState {
self.x = &self.F * &self.x;
self.P = &self.F * &self.P * self.F.transpose() + &self.Q;
FilterState {
x: self.x.as_slice().to_vec(),
P: self.P.clone(),
}
}
fn update(&mut self, z: &[f64]) -> UpdateResult {
let z_vec = DVector::from_row_slice(z);
let y = &z_vec - &self.H * &self.x;
let S = &self.H * &self.P * self.H.transpose() + &self.R;
let S_inv = S
.clone()
.try_inverse()
.expect("innovation covariance matrix is not invertible");
let K = &self.P * self.H.transpose() * &S_inv;
self.x = &self.x + &K * &y;
self.P = (&self.I - &K * &self.H) * &self.P;
let kalman_gain: Vec<Vec<f64>> = (0..self.n)
.map(|i| (0..self.m).map(|j| K[(i, j)]).collect())
.collect();
let innovation_cov: Vec<Vec<f64>> = (0..self.m)
.map(|i| (0..self.m).map(|j| S[(i, j)]).collect())
.collect();
UpdateResult {
updated: FilterState {
x: self.x.as_slice().to_vec(),
P: self.P.clone(),
},
residual: y.as_slice().to_vec(),
kalman_gain,
innovation_cov,
}
}
fn step(&mut self, dt: f64, z: &[f64]) -> StepResult {
let predicted = self.predict(dt);
let update = self.update(z);
StepResult { predicted, update }
}
fn predict_only(&mut self, dt: f64) -> FilterState {
self.predict(dt)
}
fn state(&self) -> &[f64] {
self.x.as_slice()
}
fn covariance(&self) -> &DMatrix<f64> {
&self.P
}
fn jacobian(&self, _x: &[f64], _dt: f64) -> DMatrix<f64> {
self.F.clone()
}
}