use nalgebra::{SMatrix, SVector};
use crate::state::{ErrorState, STATES, State};
use crate::units::{Altitude, AltitudeNoise};
use crate::update::Observation;
pub(crate) fn altitude_jacobian() -> SMatrix<f32, 1, STATES> {
let mut h = SMatrix::<f32, 1, STATES>::zeros();
h[(0, ErrorState::PositionDown.index())] = 1.0;
h
}
pub(crate) fn altitude_observation(
state: &State,
altitude: Altitude,
reference: Altitude,
noise: AltitudeNoise,
) -> Observation<1> {
let z = -(altitude.as_meters() - reference.as_meters());
Observation {
y: SVector::<f32, 1>::new(z - state.position.vector()[2]),
h: altitude_jacobian(),
h_b: SVector::<f32, 1>::new(1.0),
r_m: SVector::<f32, 1>::new(noise.variance()),
r_gain: SVector::<f32, 1>::new(noise.variance()),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::units::Position;
use nalgebra::SVector;
#[test]
fn the_jacobian_selects_the_down_position_and_nothing_else() {
let dx = SVector::<f32, STATES>::from_fn(|i, _| i as f32 + 1.0);
assert_eq!((altitude_jacobian() * dx)[0], 3.0);
}
#[test]
fn an_altitude_at_the_reference_is_no_news_to_a_filter_at_the_origin() {
let observation = altitude_observation(
&State::default(),
Altitude::from_meters(112.0),
Altitude::from_meters(112.0),
AltitudeNoise::from_sigma(2.0),
);
assert_eq!(observation.y[0], 0.0);
assert_eq!(observation.r_m[0], 4.0);
}
#[test]
fn a_metre_above_the_reference_is_a_metre_up_and_therefore_minus_one_down() {
let observation = altitude_observation(
&State::default(),
Altitude::from_meters(113.0),
Altitude::from_meters(112.0),
AltitudeNoise::from_sigma(2.0),
);
assert_eq!(observation.y[0], -1.0);
}
#[test]
fn a_filter_already_at_that_height_has_nothing_to_correct() {
let state = State {
position: Position::ned(20.0, -5.0, -1.0),
..State::default()
};
let observation = altitude_observation(
&state,
Altitude::from_meters(113.0),
Altitude::from_meters(112.0),
AltitudeNoise::from_sigma(2.0),
);
assert_eq!(observation.y[0], 0.0);
}
}