use crate::{Acceleration, Length, PhysicsError, Speed};
use deep_causality_algebra::RealField;
use deep_causality_num::FromPrimitive;
pub fn stopping_distance_kernel<R>(
speed: Speed<R>,
net_deceleration: Acceleration<R>,
) -> Result<Length<R>, PhysicsError>
where
R: RealField + FromPrimitive,
{
let a = net_deceleration.value();
if a <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Net deceleration must be positive; thrust-to-weight <= 1 cannot stop".into(),
));
}
let v = speed.value();
let two = R::from_f64(2.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(2.0) failed".into()))?;
Length::new(v * v / (two * a))
}
pub fn ignition_altitude_kernel<R>(
speed: Speed<R>,
thrust_acceleration: Acceleration<R>,
gravity: Acceleration<R>,
margin: Length<R>,
) -> Result<Length<R>, PhysicsError>
where
R: RealField + FromPrimitive,
{
let g = gravity.value();
if g <= R::zero() {
return Err(PhysicsError::Singularity(
"Gravitational acceleration must be positive".into(),
));
}
let a_net = thrust_acceleration.value() - g;
if a_net <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Thrust acceleration must exceed gravity; thrust-to-weight <= 1 cannot stop".into(),
));
}
let d = stopping_distance_kernel(speed, Acceleration::new(a_net)?)?;
Length::new(d.value() + margin.value())
}
pub fn suicide_burn_deceleration_kernel<R>(
speed: Speed<R>,
altitude: Length<R>,
gravity: Acceleration<R>,
) -> Result<Acceleration<R>, PhysicsError>
where
R: RealField + FromPrimitive,
{
let h = altitude.value();
if h <= R::zero() {
return Err(PhysicsError::Singularity(
"Altitude must be positive; the vehicle is at or below ground contact".into(),
));
}
let g = gravity.value();
if g <= R::zero() {
return Err(PhysicsError::Singularity(
"Gravitational acceleration must be positive".into(),
));
}
let v = speed.value();
let two = R::from_f64(2.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(2.0) failed".into()))?;
Acceleration::new(v * v / (two * h) + g)
}