use crate::{
error::{Error, ErrorKind, Result},
std::time::Duration,
Coord, Vector, DEFAULT_HEIGHT_MIN,
};
#[derive(Debug, Copy, Clone)]
pub struct Config {
pub error_max: f64,
pub height_min: f64,
pub gravity_rho: f64,
pub ce: f64,
pub cc: f64,
}
impl Default for Config {
fn default() -> Self {
Self {
error_max: 1.5,
height_min: DEFAULT_HEIGHT_MIN,
gravity_rho: 150.0,
ce: 0.25,
cc: 0.25,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Node<V, A = crate::heapless::VecD<0>> {
coord: Coord<V>,
cfg: Config,
adjustments: A,
adj_idx: usize,
}
impl<V, A> Node<V, A>
where
V: Vector,
A: Vector,
{
pub fn new() -> Self { Self::default() }
pub fn with_config(cfg: Config) -> Self { Self::with_coord_and_cfg(Coord::default(), cfg) }
pub fn with_coord<U>(coord: U) -> Self
where
U: Into<Coord<V>>,
{
Self::with_coord_and_cfg(coord, Config::default())
}
pub fn with_coord_and_cfg<U>(coord: U, cfg: Config) -> Self
where
U: Into<Coord<V>>,
{
let mut coord = coord.into();
coord.height = f64::max(cfg.height_min, coord.height);
coord.error_estimate = f64::min(cfg.error_max, coord.error_estimate);
Self {
coord,
cfg,
adjustments: A::default(),
adj_idx: 0,
}
}
#[cfg(all(feature = "std", feature = "rand"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "std", feature = "rand"))))]
pub fn rand() -> Self { Self::rand_with_cfg(Config::default()) }
#[cfg(all(feature = "std", feature = "rand"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "std", feature = "rand"))))]
pub fn rand_with_cfg(cfg: Config) -> Self { Self::with_coord_and_cfg(Coord::rand(), cfg) }
pub fn coordinate(&self) -> &Coord<V> { &self.coord }
pub fn set_coordinate<U>(&mut self, coord: U)
where
U: Into<Coord<V>>,
{
let mut coord = coord.into();
coord.height = f64::max(self.cfg.height_min, coord.height);
coord.error_estimate = f64::min(self.cfg.error_max, coord.error_estimate);
self.coord = coord;
}
pub fn distance_to(&self, other: &Coord<V>) -> Duration {
Duration::from_secs_f64(self.coord.distance_to(other))
}
pub fn error_estimate(&self) -> f64 { self.coord.error_estimate() }
pub fn set_error_estimate(&mut self, err_est: f64) { self.coord.set_error_estimate(err_est); }
pub fn try_set_error_estimate(&mut self, err_est: f64) -> Result<()> {
self.coord.try_set_error_estimate(err_est)
}
pub fn try_update_until(
&mut self,
rtt: Duration,
other: &Coord<V>,
threshold: f64,
) -> Result<()> {
self.coord.try_update_until(
f64::max(f64::MIN_POSITIVE, rtt.as_secs_f64()),
other,
threshold,
&self.cfg,
)?;
Ok(())
}
pub fn update_until(&mut self, rtt: Duration, other: &Coord<V>, threshold: f64) {
self.coord.update_until(
f64::max(f64::MIN_POSITIVE, rtt.as_secs_f64()),
other,
threshold,
&self.cfg,
);
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn update_until_all(&mut self, others: &[(Duration, &Coord<V>)], threshold: f64) {
self.coord.update_until_all(
others
.iter()
.map(|(rtt, coord)| (f64::max(f64::MIN_POSITIVE, rtt.as_secs_f64()), *coord)),
threshold,
&self.cfg,
);
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn try_update_until_all(
&mut self,
others: &[(Duration, &Coord<V>)],
threshold: f64,
) -> Result<()> {
self.coord.try_update_until_all(
others
.iter()
.map(|(rtt, coord)| (f64::max(f64::MIN_POSITIVE, rtt.as_secs_f64()), *coord)),
threshold,
&self.cfg,
)?;
Ok(())
}
pub fn try_update(&mut self, rtt: Duration, other: &Coord<V>) -> Result<()> {
let rtt = f64::max(f64::MIN_POSITIVE, rtt.as_secs_f64());
self.coord.update(rtt, other, &self.cfg);
self.update_offset(rtt, other);
if self.coord.is_finite() {
return Ok(());
}
Err(Error {
kind: ErrorKind::InvalidCoordinate,
})
}
pub fn update_gravity(&mut self, origin: &Coord<V>) {
self.coord.apply_gravity(origin, &self.cfg);
}
fn update_offset(&mut self, rtt: f64, other: &Coord<V>) {
if A::LEN == 0 {
return;
}
self.adjustments.as_mut()[self.adj_idx] = rtt - self.coord.raw_distance_to(other);
self.adj_idx = (self.adj_idx + 1) % A::LEN;
let adj_sum = self.adjustments.as_ref().iter().fold(0.0, |acc, n| acc + n);
self.coord.offset = adj_sum / (2 * A::LEN) as f64;
}
}
impl<V, A> Node<V, A>
where
V: Vector + Clone,
A: Vector,
{
pub fn update(&mut self, rtt: Duration, other: &Coord<V>) -> bool {
let coord = self.coord.clone();
if let Err(Error {
kind: ErrorKind::InvalidCoordinate,
}) = self.try_update(rtt, other)
{
self.coord = coord;
return false;
}
true
}
}