use core::f64::consts::{SQRT_2, TAU};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Accelerometer {
Alvarez350,
Alvarez500,
Dalin,
SuperStar,
MicroStar,
AtomInterferometer {
white: f64,
},
}
impl Accelerometer {
pub fn asd(&self, f: f64) -> f64 {
match *self {
Accelerometer::Alvarez350 => {
3e-8 * f * f + 4e-13 * (1.0 + 7e-4 / f + (3e-4 / f).powi(2)).sqrt()
}
Accelerometer::Alvarez500 => 3e-8 * f * f + 5e-13 * (1.0 + (1.0 / f).powf(2.0 / 3.0)).sqrt(),
Accelerometer::Dalin => 2.4e-13 * f.powf(-0.25) + 1e-18 / (f * f) + 8e-11 * f * f,
Accelerometer::SuperStar => 1e-10 * (1.0 + 0.005 / f).sqrt(),
Accelerometer::MicroStar => {
1e-11 / SQRT_2
* ((1e-3 / f).powi(4) / ((1e-5 / f).powi(4) + 1.0) + 1.0 + (f / 0.1).powi(4)).sqrt()
}
Accelerometer::AtomInterferometer { white } => white,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Ranging {
GraceFo,
Lri2030,
Lri2033,
}
impl Ranging {
pub fn asd(&self, f: f64) -> f64 {
let (white, drift) = match self {
Ranging::GraceFo => (2.2e-10, 1e-12),
Ranging::Lri2030 => (2.2e-10, 1e-13),
Ranging::Lri2033 => (1.1e-10, 5e-14),
};
white / f.sqrt() + drift / (f * f)
}
pub fn as_acceleration(&self, f: f64) -> f64 {
(TAU * f).powi(2) * self.asd(f)
}
}
pub fn pair_noise(accelerometer: Accelerometer, ranging: Ranging, f: f64) -> f64 {
(SQRT_2 * accelerometer.asd(f)).hypot(ranging.as_acceleration(f))
}
pub fn hybrid_asd(a: Accelerometer, b: Accelerometer, f: f64) -> f64 {
let (x, y) = (a.asd(f), b.asd(f));
x * y / x.hypot(y)
}
pub fn degree_at_frequency(f: f64, orbit_period: f64) -> f64 {
f * orbit_period
}
pub fn orbit_period(gm: f64, radius: f64, h: f64) -> f64 {
TAU * ((radius + h).powi(3) / gm).sqrt()
}
#[cfg(test)]
mod tests {
use super::*;
fn close(a: f64, b: f64, rel: f64) -> bool {
(a - b).abs() <= rel * b.abs()
}
#[test]
fn zingerle_figure_1_checkpoints() {
assert!(close(Ranging::GraceFo.as_acceleration(0.1), 3.1e-10, 0.02));
assert!(close(Ranging::GraceFo.as_acceleration(1e-3), 4.0e-11, 0.02));
assert!(close(pair_noise(Accelerometer::SuperStar, Ranging::GraceFo, 1e-3), 3.5e-10, 0.02));
let cai12 = Accelerometer::AtomInterferometer { white: 1e-12 };
assert!(close(pair_noise(cai12, Ranging::Lri2033, 1e-3), 2.5e-12, 0.03));
}
#[test]
fn white_and_limiting_behaviour() {
let cai = Accelerometer::AtomInterferometer { white: 1e-11 };
assert_eq!(cai.asd(1e-4), cai.asd(1e-1));
assert!(close(Accelerometer::SuperStar.asd(10.0), 1e-10, 1e-3));
assert!(close(Accelerometer::MicroStar.asd(1e-2), 1e-11 / SQRT_2, 0.02));
}
#[test]
fn hybrid_takes_the_better_sensor_per_frequency() {
let cai = Accelerometer::AtomInterferometer { white: 1e-11 };
assert!(close(hybrid_asd(cai, cai, 1e-3), 1e-11 / SQRT_2, 1e-12));
assert!(close(hybrid_asd(Accelerometer::SuperStar, cai, 1e-4), 1e-11, 0.01));
assert!(hybrid_asd(Accelerometer::MicroStar, cai, 1e-2) < Accelerometer::MicroStar.asd(1e-2));
}
#[test]
fn grace_orbit_maps_revolution_frequency_to_degree_one() {
let t = orbit_period(3.986004418e14, 6_371_000.0, 463e3);
assert!(close(t, 5620.0, 0.002), "{t}");
assert!(close(degree_at_frequency(1.0 / t, t), 1.0, 1e-12));
}
}