use crate::error::EstimationError;
use crate::estimation::attitude_correction;
use crate::linear_algebra::Vector3D;
use crate::scalar::Numeric;
use crate::spatial::SO3;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MadgwickFilter<T: Numeric = f64> {
orientation: SO3<T>,
gyroscope_bias: Vector3D<T>,
correction_gain: T,
bias_gain: T,
upward_reference: Vector3D<T>,
north_reference: Vector3D<T>,
}
impl<T: Numeric> MadgwickFilter<T> {
pub fn new(initial_orientation: SO3<T>) -> Self {
MadgwickFilter {
orientation: initial_orientation,
gyroscope_bias: Vector3D::zeros(),
correction_gain: T::from_f64(0.1),
bias_gain: T::from_f64(0.01),
upward_reference: attitude_correction::upward_reference(),
north_reference: attitude_correction::north_reference(),
}
}
#[must_use]
pub fn with_correction_gain(mut self, correction_gain: T) -> Self {
self.correction_gain = correction_gain;
self
}
#[must_use]
pub fn with_bias_gain(mut self, bias_gain: T) -> Self {
self.bias_gain = bias_gain;
self
}
#[must_use]
pub fn with_reference_directions(
mut self,
upward_reference: Vector3D<T>,
north_reference: Vector3D<T>,
) -> Self {
let Some(up) = upward_reference.try_normalized() else {
return self;
};
let levelled = north_reference - up * north_reference.dot(up);
let Some(north) = levelled.try_normalized() else {
return self;
};
self.upward_reference = up;
self.north_reference = north;
self
}
pub fn set_orientation(&mut self, orientation: SO3<T>) {
self.orientation = orientation;
}
pub fn set_gyroscope_bias(&mut self, gyroscope_bias: Vector3D<T>) {
self.gyroscope_bias = gyroscope_bias;
}
pub fn step(
&mut self,
gyroscope_reading: Vector3D<T>,
accelerometer_reading: Vector3D<T>,
magnetometer_reading: Option<Vector3D<T>>,
timestep: T,
) -> Result<(), EstimationError> {
if !attitude_correction::readings_are_finite(
gyroscope_reading,
accelerometer_reading,
magnetometer_reading,
timestep,
) {
return Err(EstimationError::NonFinite);
}
let correction = attitude_correction::correction(
self.orientation,
accelerometer_reading,
magnetometer_reading,
self.upward_reference,
self.north_reference,
);
let direction = correction.try_normalized().unwrap_or_else(Vector3D::zeros);
let gyroscope_bias = self.gyroscope_bias - direction * self.bias_gain * timestep;
let corrected_rate = gyroscope_reading - gyroscope_bias + direction * self.correction_gain;
let orientation =
attitude_correction::stepped_orientation(self.orientation, corrected_rate, timestep);
if !gyroscope_bias.is_finite() || !attitude_correction::orientation_is_finite(orientation) {
return Err(EstimationError::NonFinite);
}
self.gyroscope_bias = gyroscope_bias;
self.orientation = orientation;
Ok(())
}
pub fn step_without_magnetometer(
&mut self,
gyroscope_reading: Vector3D<T>,
accelerometer_reading: Vector3D<T>,
timestep: T,
) -> Result<(), EstimationError> {
self.step(gyroscope_reading, accelerometer_reading, None, timestep)
}
#[inline]
#[must_use]
pub fn orientation(&self) -> SO3<T> {
self.orientation
}
#[inline]
pub fn gyroscope_bias(&self) -> Vector3D<T> {
self.gyroscope_bias
}
}