use crate::linalg::allocator::Allocator;
use crate::linalg::{DefaultAllocator, DimName};
use crate::md::trajectory::{Interpolatable, Traj};
pub use crate::od::estimate::*;
pub use crate::od::*;
use indexmap::IndexSet;
use msr::sensitivity::TrackerSensitivity;
use nalgebra::OMatrix;
use std::collections::BTreeMap;
use std::iter::Zip;
use std::ops::Add;
use std::slice::Iter;
use self::msr::MeasurementType;
mod display;
mod export;
mod filter_data;
mod import;
mod smooth;
mod stats;
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct ODSolution<StateType, EstType, MsrSize, Trk>
where
StateType: Interpolatable + Add<OVector<f64, <StateType as State>::Size>, Output = StateType>,
EstType: Estimate<StateType>,
MsrSize: DimName,
Trk: TrackerSensitivity<StateType, StateType>,
<DefaultAllocator as Allocator<<StateType as State>::VecLength>>::Buffer<f64>: Send,
DefaultAllocator: Allocator<<StateType as State>::Size>
+ Allocator<<StateType as State>::VecLength>
+ Allocator<MsrSize>
+ Allocator<MsrSize, <StateType as State>::Size>
+ Allocator<MsrSize, MsrSize>
+ Allocator<<StateType as State>::Size, <StateType as State>::Size>
+ Allocator<<StateType as State>::Size, MsrSize>,
{
pub estimates: Vec<EstType>,
pub residuals: Vec<Option<Residual<MsrSize>>>,
pub gains: Vec<Option<OMatrix<f64, <StateType as State>::Size, MsrSize>>>,
pub filter_smoother_ratios: Vec<Option<OVector<f64, <StateType as State>::Size>>>,
pub devices: BTreeMap<String, Trk>,
pub measurement_types: IndexSet<MeasurementType>,
}
impl<StateType, EstType, MsrSize, Trk> ODSolution<StateType, EstType, MsrSize, Trk>
where
StateType: Interpolatable + Add<OVector<f64, <StateType as State>::Size>, Output = StateType>,
EstType: Estimate<StateType>,
MsrSize: DimName,
Trk: TrackerSensitivity<StateType, StateType>,
<DefaultAllocator as Allocator<<StateType as State>::VecLength>>::Buffer<f64>: Send,
DefaultAllocator: Allocator<<StateType as State>::Size>
+ Allocator<<StateType as State>::VecLength>
+ Allocator<MsrSize>
+ Allocator<MsrSize, <StateType as State>::Size>
+ Allocator<MsrSize, MsrSize>
+ Allocator<<StateType as State>::Size, <StateType as State>::Size>
+ Allocator<<StateType as State>::Size, MsrSize>,
{
pub fn new(
devices: BTreeMap<String, Trk>,
measurement_types: IndexSet<MeasurementType>,
) -> Self {
Self {
estimates: Vec::new(),
residuals: Vec::new(),
gains: Vec::new(),
filter_smoother_ratios: Vec::new(),
devices,
measurement_types,
}
}
pub(crate) fn push_measurement_update(
&mut self,
estimate: EstType,
residual: Residual<MsrSize>,
gain: Option<OMatrix<f64, <StateType as State>::Size, MsrSize>>,
) {
self.estimates.push(estimate);
self.residuals.push(Some(residual));
self.gains.push(gain);
self.filter_smoother_ratios.push(None);
}
pub(crate) fn push_time_update(&mut self, estimate: EstType) {
self.estimates.push(estimate);
self.residuals.push(None);
self.gains.push(None);
self.filter_smoother_ratios.push(None);
}
pub fn results(&self) -> Zip<Iter<'_, EstType>, Iter<'_, Option<Residual<MsrSize>>>> {
self.estimates.iter().zip(self.residuals.iter())
}
pub fn is_filter_run(&self) -> bool {
self.gains.iter().flatten().count() > 0
}
pub fn is_smoother_run(&self) -> bool {
self.filter_smoother_ratios.iter().flatten().count() > 0
}
pub fn to_traj(&self) -> Result<Traj<StateType>, NyxError>
where
DefaultAllocator: Allocator<StateType::VecLength>,
{
if self.estimates.is_empty() {
Err(NyxError::NoStateData {
msg: "No navigation trajectory to generate: run the OD process first".to_string(),
})
} else {
let mut traj = Traj {
states: self.estimates.iter().map(|est| est.state()).collect(),
name: None,
};
traj.finalize();
Ok(traj)
}
}
pub fn accepted_residuals(&self) -> Vec<Residual<MsrSize>> {
self.residuals
.iter()
.flatten()
.filter(|resid| !resid.rejected)
.cloned()
.collect::<Vec<Residual<MsrSize>>>()
}
pub fn rejected_residuals(&self) -> Vec<Residual<MsrSize>> {
self.residuals
.iter()
.flatten()
.filter(|resid| resid.rejected)
.cloned()
.collect::<Vec<Residual<MsrSize>>>()
}
}
impl<StateType, EstType, MsrSize, Trk> PartialEq for ODSolution<StateType, EstType, MsrSize, Trk>
where
StateType: Interpolatable + Add<OVector<f64, <StateType as State>::Size>, Output = StateType>,
EstType: Estimate<StateType>,
MsrSize: DimName,
Trk: TrackerSensitivity<StateType, StateType> + PartialEq,
<DefaultAllocator as Allocator<<StateType as State>::VecLength>>::Buffer<f64>: Send,
DefaultAllocator: Allocator<<StateType as State>::Size>
+ Allocator<<StateType as State>::VecLength>
+ Allocator<MsrSize>
+ Allocator<MsrSize, <StateType as State>::Size>
+ Allocator<MsrSize, MsrSize>
+ Allocator<<StateType as State>::Size, <StateType as State>::Size>
+ Allocator<<StateType as State>::Size, MsrSize>,
{
fn eq(&self, other: &Self) -> bool {
self.estimates.len() == other.estimates.len()
&& self.residuals.len() == other.residuals.len()
&& self.gains.len() == other.gains.len()
&& self.filter_smoother_ratios.len() == other.filter_smoother_ratios.len()
&& self.devices == other.devices
&& self.measurement_types.iter().all(|msr_type| other.measurement_types.contains(msr_type))
&& self.estimates.iter().zip(other.estimates.iter()).all(|(mine, theirs)| {
(mine.state().to_state_vector() - theirs.state().to_state_vector()).norm() < 1e-6 &&
(mine.covar() - theirs.covar()).norm() < 1e-8
})
&& self.residuals.iter().zip(other.residuals.iter()).all(|(mine, theirs)| {
if let Some(mine) = mine {
if let Some(theirs) = theirs {
(mine.ratio - theirs.ratio).abs() < 1e-4
} else {
false
}
} else {
theirs.is_none()
}
})
&& self.gains.iter().zip(other.gains.iter()).all(|(my_k, other_k)| {
if let Some(my_k) = my_k {
if let Some(other_k) = other_k {
(my_k - other_k).norm() < 1e-8
} else {
false
}
} else {
other_k.is_none()
}
})
&& self.filter_smoother_ratios.iter().zip(other.filter_smoother_ratios.iter()).all(|(my_fs, other_fs)| {
if let Some(my_fs) = my_fs {
if let Some(other_fs) = other_fs {
(my_fs - other_fs).norm() < 1e-8
} else {
false
}
} else {
other_fs.is_none()
}
})
}
}