use apex_manifolds::rn::Rn;
use nalgebra::DVector;
use crate::core::CoreResult;
use crate::core::noise::NoiseModel;
use crate::factors::pose::BetweenFactor;
use super::types::ImuParameters;
pub const BIAS_DIM: usize = 6;
pub fn bias_random_walk() -> BetweenFactor<Rn> {
BetweenFactor::new(Rn::new(DVector::zeros(BIAS_DIM)))
}
pub fn bias_random_walk_noise(params: &ImuParameters, dt: f64) -> CoreResult<NoiseModel> {
let sigmas = bias_random_walk_sigmas(params, dt)?;
NoiseModel::from_sigmas(&sigmas)
}
fn bias_random_walk_sigmas(params: &ImuParameters, dt: f64) -> CoreResult<[f64; BIAS_DIM]> {
use crate::core::CoreError;
if !dt.is_finite() || dt <= 0.0 {
return Err(CoreError::InvalidInput(format!(
"bias random walk needs a positive, finite interval, got dt={dt}"
)));
}
if params.sigma_gw_c <= 0.0 || params.sigma_aw_c <= 0.0 {
return Err(CoreError::InvalidInput(format!(
"bias random-walk densities must be positive, got sigma_gw_c={}, sigma_aw_c={}",
params.sigma_gw_c, params.sigma_aw_c
)));
}
let scale = dt.sqrt();
let sg = params.sigma_gw_c * scale;
let sa = params.sigma_aw_c * scale;
Ok([sg, sg, sg, sa, sa, sa])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::factors::Factor;
#[test]
fn random_walk_residual_is_zero_for_equal_biases() {
let factor = bias_random_walk();
let b = [0.01, -0.02, 0.03, 0.4, -0.5, 0.6];
let mut r = vec![0.0; factor.residual_dim()];
factor.linearize(&[&b, &b], &mut r, None);
assert!(
r.iter().all(|v| v.abs() < 1e-12),
"expected zero residual, got {r:?}"
);
}
#[test]
fn random_walk_residual_is_the_bias_difference() {
let factor = bias_random_walk();
let b_i = [0.0; 6];
let b_j = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6];
let mut r = vec![0.0; 6];
factor.linearize(&[&b_i, &b_j], &mut r, None);
for (k, expected) in b_j.iter().enumerate() {
assert!(
(r[k].abs() - expected.abs()).abs() < 1e-12,
"row {k}: {r:?}"
);
}
}
#[test]
fn sigmas_scale_with_sqrt_dt() {
let params = ImuParameters::default();
let one = bias_random_walk_sigmas(¶ms, 1.0).unwrap_or_else(|e| panic!("{e}"));
let four = bias_random_walk_sigmas(¶ms, 4.0).unwrap_or_else(|e| panic!("{e}"));
for k in 0..BIAS_DIM {
assert!(
(four[k] - 2.0 * one[k]).abs() < 1e-15,
"sigma[{k}] should double when dt quadruples"
);
}
}
#[test]
fn rejects_non_positive_interval() {
let params = ImuParameters::default();
assert!(bias_random_walk_noise(¶ms, 0.0).is_err());
assert!(bias_random_walk_noise(¶ms, -1.0).is_err());
assert!(bias_random_walk_noise(¶ms, f64::NAN).is_err());
}
#[test]
fn rejects_zero_random_walk_density() {
let params = ImuParameters {
sigma_gw_c: 0.0,
..ImuParameters::default()
};
assert!(bias_random_walk_noise(¶ms, 0.1).is_err());
}
}