use nalgebra::{RealField, SMatrix, SVector};
pub trait System<T, const N: usize, const U: usize> {
fn covariance(&self) -> &SMatrix<T, N, N>;
fn state(&self) -> &SVector<T, N>;
fn state_mut(&mut self) -> &mut SVector<T, N>;
}
pub trait LinearisableSystem<T, const N: usize, const U: usize>: System<T, N, U> {
fn transition(&self) -> &SMatrix<T, N, N>;
fn transition_transpose(&self) -> &SMatrix<T, N, N>;
}
pub trait InputSystem<T, const N: usize, const U: usize>: System<T, N, U> {
fn step(&mut self, u: SVector<T, U>) -> &SVector<T, N> {
*self.state_mut() = self.predict(self.state(), &u);
self.state()
}
fn predict(&self, x: &SVector<T, N>, u: &SVector<T, U>) -> SVector<T, N>;
}
pub trait NoInputSystem<T, const N: usize>: System<T, N, 0> {
fn step(&mut self) -> &SVector<T, N> {
*self.state_mut() = self.predict(self.state());
self.state()
}
fn predict(&self, x: &SVector<T, N>) -> SVector<T, N>;
}
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LinearSystem<T: RealField, const N: usize, const U: usize> {
x: SVector<T, N>,
F: SMatrix<T, N, N>,
F_t: SMatrix<T, N, N>,
Q: SMatrix<T, N, N>,
B: SMatrix<T, N, U>,
}
#[allow(non_snake_case)]
impl<T: RealField + Copy, const N: usize, const U: usize> LinearSystem<T, N, U> {
pub fn new(
F: SMatrix<T, N, N>,
Q: SMatrix<T, N, N>,
B: SMatrix<T, N, U>,
x_initial: SVector<T, N>,
) -> Self {
LinearSystem {
x: x_initial,
F,
F_t: F.transpose(),
Q,
B,
}
}
pub fn set_transition(&mut self, transition: SMatrix<T, N, N>) {
self.F_t = transition.transpose();
self.F = transition;
}
pub fn covariance_mut(&mut self) -> &mut SMatrix<T, N, N> {
&mut self.Q
}
}
impl<T: RealField + Copy, const N: usize, const U: usize> System<T, N, U>
for LinearSystem<T, N, U>
{
fn covariance(&self) -> &SMatrix<T, N, N> {
&self.Q
}
fn state(&self) -> &SVector<T, N> {
&self.x
}
fn state_mut(&mut self) -> &mut SVector<T, N> {
&mut self.x
}
}
impl<T: RealField + Copy, const N: usize, const U: usize> LinearisableSystem<T, N, U>
for LinearSystem<T, N, U>
{
fn transition(&self) -> &SMatrix<T, N, N> {
&self.F
}
fn transition_transpose(&self) -> &SMatrix<T, N, N> {
&self.F_t
}
}
impl<T: RealField + Copy, const N: usize, const U: usize> InputSystem<T, N, U>
for LinearSystem<T, N, U>
{
fn predict(&self, x: &SVector<T, N>, u: &SVector<T, U>) -> SVector<T, N> {
self.F * x + self.B * u
}
}
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LinearNoInputSystem<T: RealField, const N: usize> {
x: SVector<T, N>,
F: SMatrix<T, N, N>,
F_t: SMatrix<T, N, N>,
Q: SMatrix<T, N, N>,
}
#[allow(non_snake_case)]
impl<T: RealField + Copy, const N: usize> LinearNoInputSystem<T, N> {
pub fn new(F: SMatrix<T, N, N>, Q: SMatrix<T, N, N>, x_initial: SVector<T, N>) -> Self {
LinearNoInputSystem {
x: x_initial,
F,
F_t: F.transpose(),
Q,
}
}
pub fn set_transition(&mut self, transition: SMatrix<T, N, N>) {
self.F_t = transition.transpose();
self.F = transition;
}
pub fn covariance_mut(&mut self) -> &mut SMatrix<T, N, N> {
&mut self.Q
}
}
impl<T: RealField + Copy, const N: usize> System<T, N, 0> for LinearNoInputSystem<T, N> {
fn covariance(&self) -> &SMatrix<T, N, N> {
&self.Q
}
fn state(&self) -> &SVector<T, N> {
&self.x
}
fn state_mut(&mut self) -> &mut SVector<T, N> {
&mut self.x
}
}
impl<T: RealField + Copy, const N: usize> LinearisableSystem<T, N, 0>
for LinearNoInputSystem<T, N>
{
fn transition(&self) -> &SMatrix<T, N, N> {
&self.F
}
fn transition_transpose(&self) -> &SMatrix<T, N, N> {
&self.F_t
}
}
impl<T: RealField + Copy, const N: usize> NoInputSystem<T, N> for LinearNoInputSystem<T, N> {
fn predict(&self, x: &SVector<T, N>) -> SVector<T, N> {
self.F * x
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StepReturn<T: RealField, const N: usize> {
pub state: SVector<T, N>,
pub jacobian: SMatrix<T, N, N>,
pub covariance: SMatrix<T, N, N>,
}
pub type StepFunction<T, const N: usize, const U: usize> =
fn(SVector<T, N>, SVector<T, U>) -> StepReturn<T, N>;
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NonLinearSystem<T: RealField, const N: usize, const U: usize> {
x: SVector<T, N>,
Q: SMatrix<T, N, N>,
F: SMatrix<T, N, N>,
F_t: SMatrix<T, N, N>,
step_fn: StepFunction<T, N, U>,
}
impl<T: RealField, const N: usize, const U: usize> NonLinearSystem<T, N, U> {
pub fn new(step_fn: StepFunction<T, N, U>, x_initial: SVector<T, N>) -> Self {
Self {
x: x_initial,
Q: SMatrix::zeros(),
F: SMatrix::zeros(),
F_t: SMatrix::zeros(),
step_fn,
}
}
}
impl<T: RealField + Copy, const N: usize, const U: usize> System<T, N, U>
for NonLinearSystem<T, N, U>
{
fn covariance(&self) -> &SMatrix<T, N, N> {
&self.Q
}
fn state(&self) -> &SVector<T, N> {
&self.x
}
fn state_mut(&mut self) -> &mut SVector<T, N> {
&mut self.x
}
}
impl<T: RealField + Copy, const N: usize, const U: usize> LinearisableSystem<T, N, U>
for NonLinearSystem<T, N, U>
{
fn transition(&self) -> &SMatrix<T, N, N> {
&self.F
}
fn transition_transpose(&self) -> &SMatrix<T, N, N> {
&self.F_t
}
}
impl<T: RealField + Copy, const N: usize, const U: usize> InputSystem<T, N, U>
for NonLinearSystem<T, N, U>
{
fn step(&mut self, u: SVector<T, U>) -> &SVector<T, N> {
let r = (self.step_fn)(self.x, u);
self.x = r.state;
self.F = r.jacobian;
self.F_t = self.F.transpose();
self.Q = r.covariance;
self.state()
}
fn predict(&self, x: &SVector<T, N>, u: &SVector<T, U>) -> SVector<T, N> {
let r = (self.step_fn)(*x, *u);
r.state
}
}
pub fn zero_order_hold<T: RealField, const N: usize>(
state_matrix: SMatrix<T, N, N>,
timestep: T,
) -> SMatrix<T, N, N> {
SMatrix::identity() + state_matrix * timestep
}