use crate::{
ATTOS_PER_SEC_I128, C_SQUARED, Dt, PLANCK_LENGTH_4, Real, Scale, Spacetime, Velocity, sqrt,
};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Drift {
pub constant: Dt,
pub rate: Dt,
pub accel: Dt,
}
impl Drift {
#[inline]
pub const fn new(constant: Dt, rate: Dt, accel: Dt) -> Drift {
Self {
constant,
rate,
accel,
}
}
pub const ZERO: Self = Self::new(Dt::ZERO, Dt::ZERO, Dt::ZERO);
#[inline]
pub const fn from_constant(c: Dt) -> Drift {
Self::new(c, Dt::ZERO, Dt::ZERO)
}
#[inline]
pub const fn from_offset_and_rate(offset: Dt, rate: Dt) -> Drift {
Self::new(offset, rate, Dt::ZERO)
}
#[inline]
pub const fn proper_time_rate(&self) -> Real {
f!(1.0) + self.rate.to_sec_f()
}
pub const fn time_diff_after(&self, span: &Dt) -> Dt {
let dt_attos = span.to_attos();
let mut total_attos = self.constant.to_attos();
if !self.rate.is_zero() || !self.accel.is_zero() {
let rate_attos: i128 = self.rate.to_attos();
let rate_term = rate_attos.wrapping_mul(dt_attos) / ATTOS_PER_SEC_I128;
total_attos = total_attos.wrapping_add(rate_term);
let accel_attos: i128 = self.accel.to_attos();
let accel_dt = accel_attos.wrapping_mul(dt_attos) / ATTOS_PER_SEC_I128;
let accel_term = accel_dt.wrapping_mul(dt_attos) / ATTOS_PER_SEC_I128;
total_attos = total_attos.saturating_add(accel_term);
}
Dt::span(total_attos)
}
#[inline]
pub fn time_diff_after_with_noise(&self, span: &Dt, stochastic_offset_sec: Real) -> Dt {
self.time_diff_after(span)
.add(Dt::from_sec_f(stochastic_offset_sec, Scale::TAI))
}
pub const fn from_velocity_potential_and_scale(
velocity_m_s: Real,
grav_potential_m2_s2: Real,
characteristic_length_scale: Real,
) -> Drift {
let phi = grav_potential_m2_s2 / C_SQUARED;
let velocity = Velocity::from_speed(velocity_m_s);
let spacetime = Spacetime::from_potential_velocity_and_scale(
phi,
velocity,
characteristic_length_scale,
);
Self::from_spacetime(&spacetime)
}
pub const fn from_unified_proper_time_rate(u: Real, kretschmann: Real) -> Drift {
let delta = u.max(f!(0.0));
let x = PLANCK_LENGTH_4 * kretschmann.max(f!(0.0));
let one_minus_delta = f!(1.0) - delta;
let num = delta * (f!(1.0) + x) + x * (one_minus_delta * one_minus_delta);
let k_eff = num / (f!(1.0) + x);
let rate_factor = sqrt(k_eff).max(f!(0.0));
let rate_offset = rate_factor - f!(1.0);
Self::from_offset_and_rate(Dt::ZERO, Dt::from_sec_f(rate_offset, Scale::TAI))
}
#[inline]
pub const fn from_spacetime(spacetime: &Spacetime) -> Drift {
let u = spacetime.alpha * spacetime.alpha * (f!(1.0) - spacetime.beta * spacetime.beta);
Self::from_unified_proper_time_rate(u, spacetime.kretschmann)
}
}
impl Dt {
#[inline]
pub const fn to_drift_as_constant(self, rate: Dt, accel: Dt) -> Drift {
Drift::new(self, rate, accel)
}
#[inline]
pub const fn to_drift_as_rate(self, constant: Dt, accel: Dt) -> Drift {
Drift::new(constant, self, accel)
}
#[inline]
pub const fn to_drift_as_accel(self, constant: Dt, rate: Dt) -> Drift {
Drift::new(constant, rate, self)
}
#[inline]
pub const fn adjusted_advance(&mut self, elapsed: &Dt, spacetime: &Spacetime) {
let dtau = elapsed.add(Drift::from_spacetime(spacetime).time_diff_after(elapsed));
*self = self.add(dtau);
}
#[inline]
pub const fn adjusted_advance_using_drift(&mut self, elapsed: &Dt, drift: &Drift) {
let dtau = elapsed.add(drift.time_diff_after(elapsed));
*self = self.add(dtau);
}
pub const fn convert_using_drift(self, reference: Dt, drift: Drift) -> Dt {
let span = self.to_diff_raw(reference);
let correction = drift.time_diff_after(&span);
self.add(correction)
}
pub const fn convert_back_using_drift(self, reference: Dt, drift: Drift) -> Dt {
if drift.rate.is_zero() && drift.accel.is_zero() {
return self.sub(drift.constant);
}
let mut guess = self;
let mut i = 0u32;
while i < 16 {
let span = guess.to_diff_raw(reference);
let correction = drift.time_diff_after(&span);
guess = self.sub(correction);
i += 1;
}
guess
}
}
#[cfg(feature = "wire")]
impl Drift {
pub const WIRE_VERSION: u8 = 1;
pub const WIRE_SIZE: usize = 3 * Dt::WIRE_SIZE;
pub fn to_wire_bytes(&self) -> [u8; Self::WIRE_SIZE] {
let mut buf = [0u8; Self::WIRE_SIZE];
let c = self.constant.to_wire_bytes();
let r = self.rate.to_wire_bytes();
let a = self.accel.to_wire_bytes();
buf[0..Dt::WIRE_SIZE].copy_from_slice(&c);
buf[Dt::WIRE_SIZE..2 * Dt::WIRE_SIZE].copy_from_slice(&r);
buf[2 * Dt::WIRE_SIZE..].copy_from_slice(&a);
buf
}
pub fn from_wire_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != Self::WIRE_SIZE {
return None;
}
if bytes[0] != Self::WIRE_VERSION {
return None;
}
let constant = Dt::from_wire_bytes(&bytes[0..Dt::WIRE_SIZE])?;
let rate = Dt::from_wire_bytes(&bytes[Dt::WIRE_SIZE..2 * Dt::WIRE_SIZE])?;
let accel = Dt::from_wire_bytes(&bytes[2 * Dt::WIRE_SIZE..])?;
Some(Self::new(constant, rate, accel))
}
}