use astrodyn::Planet;
use bevy::prelude::*;
use crate::components::*;
use crate::SimulationTimeR;
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
pub fn planet_fixed_rotation_system<P: Planet>(
mut commands: Commands,
sim_time: Res<SimulationTimeR>,
polar: Option<Res<crate::PolarMotionR>>,
ephemeris: Option<Res<crate::EphemerisR>>,
mut query: Query<(
Entity,
&mut PlanetFixedRotationC<P>,
Option<&RotationModelC>,
Option<&PlanetOmegaC>,
Option<&mut PlanetAngularVelocityC<P>>,
Option<&PfixFrameEntityC>,
)>,
mut frame_rots: Query<&mut FrameRotC>,
mut frame_ang_vels: Query<&mut FrameAngVelC>,
) {
let polar_params = polar.map(|p| (p.xp, p.yp));
type PlanetRot<P> = astrodyn::FrameTransform<astrodyn::RootInertial, astrodyn::PlanetFixed<P>>;
let mut earth_rotation: Option<PlanetRot<P>> = Option::None;
let mut earth_rotation_raw: Option<glam::DMat3> = Option::None;
for (entity, mut rot, model, omega, ang_vel, pfix_frame_entity) in &mut query {
let default_model = astrodyn::RotationModel::EarthRNP;
let rotation_model = model.map_or(&default_model, |m| &m.0);
let rotated = !matches!(rotation_model, astrodyn::RotationModel::None);
let mut raw_matrix: Option<glam::DMat3> = None;
match rotation_model {
astrodyn::RotationModel::None => {}
astrodyn::RotationModel::EarthRNP => {
let mat = *earth_rotation_raw.get_or_insert_with(|| {
astrodyn::compute_t_parent_this_from_tjt_with_polar(
sim_time.gmst_seconds,
sim_time.tt_tjt(),
polar_params,
)
});
let rotation = *earth_rotation.get_or_insert_with(|| {
astrodyn::compute_t_parent_this_from_tjt_with_polar_typed::<P>(
sim_time.gmst_seconds,
sim_time.tt_tjt(),
polar_params,
)
});
rot.0 = rotation;
raw_matrix = Some(mat);
}
astrodyn::RotationModel::MarsIAU => {
let tt_s_since_j2000 =
(sim_time.tt_tjt() - astrodyn::J2000_TT_TJT) * astrodyn::SECONDS_PER_DAY;
let mat = astrodyn::rotation_mars::compute_mars_rotation(tt_s_since_j2000);
rot.0 = astrodyn::FrameTransform::from_matrix(mat);
raw_matrix = Some(mat);
}
astrodyn::RotationModel::MoonIAU => {
let tdb_jd = sim_time.tdb_julian_date();
let tdb_s_since_j2000 =
(tdb_jd - astrodyn::J2000_TT_JD) * astrodyn::SECONDS_PER_DAY;
let mat = astrodyn::rotation_moon::compute_moon_rotation(tdb_s_since_j2000);
rot.0 = astrodyn::FrameTransform::from_matrix(mat);
raw_matrix = Some(mat);
}
astrodyn::RotationModel::MoonDE421 => {
let eph = ephemeris.as_ref().expect(
"RotationModel::MoonDE421 requires the EphemerisR resource with a BPC \
loaded. Insert EphemerisR before stepping the simulation, or switch the \
body to RotationModel::MoonIAU.",
);
let tdb_jd = sim_time.tdb_julian_date();
let mat = eph
.get_body_rotation(astrodyn::EphemerisBody::Moon, tdb_jd)
.unwrap_or_else(|err| {
panic!(
"Moon DE421 BPC rotation query failed at TDB JD {tdb_jd}: {err:?}. \
The loaded BPC kernel does not cover this epoch; load a kernel \
whose coverage includes the simulation epoch."
)
});
rot.0 = astrodyn::FrameTransform::from_matrix(mat);
raw_matrix = Some(mat);
}
}
if rotated {
let default_omega = match rotation_model {
astrodyn::RotationModel::None => 0.0,
astrodyn::RotationModel::EarthRNP => astrodyn::EARTH.omega,
astrodyn::RotationModel::MarsIAU => astrodyn::MARS.omega,
astrodyn::RotationModel::MoonIAU | astrodyn::RotationModel::MoonDE421 => {
astrodyn::MOON.omega
}
};
let omega_value = omega.map(|o| o.0).unwrap_or(default_omega);
if let Some(mut ang_vel_c) = ang_vel {
let raw = glam::DVec3::new(0.0, 0.0, omega_value);
let typed = astrodyn::AngularVelocity::<astrodyn::PlanetFixed<P>>::from_raw_si(raw); ang_vel_c.0 = typed;
}
if let (Some(matrix), Some(pfix_fe)) = (raw_matrix, pfix_frame_entity) {
let mut frame_rot = frame_rots.get_mut(pfix_fe.0).unwrap_or_else(|err| {
panic!(
"planet_fixed_rotation_system: source {entity:?} has \
PfixFrameEntityC({:?}) but that entity has no \
FrameRotC ({err:?}). The pfix frame entity must be \
alive with FrameRotC / FrameAngVelC attached \
(spawned by register_pfix_frames_system). Either \
remove the stale PfixFrameEntityC marker before \
despawning the pfix frame entity, or ensure the \
pfix frame entity stays alive for as long as the \
source carries the handle.",
pfix_fe.0
)
});
frame_rot.q_parent_this =
astrodyn::JeodQuat::left_quat_from_transformation(&matrix);
frame_rot.t_parent_this = matrix;
let mut frame_av = frame_ang_vels.get_mut(pfix_fe.0).unwrap_or_else(|err| {
panic!(
"planet_fixed_rotation_system: source {entity:?} has \
PfixFrameEntityC({:?}) but that entity has no \
FrameAngVelC ({err:?}). The pfix frame entity must \
be alive with FrameRotC / FrameAngVelC attached \
(spawned by register_pfix_frames_system). Either \
remove the stale PfixFrameEntityC marker before \
despawning the pfix frame entity, or ensure the \
pfix frame entity stays alive for as long as the \
source carries the handle.",
pfix_fe.0
)
});
frame_av.0 = glam::DVec3::new(0.0, 0.0, omega_value);
}
} else {
rot.0 = astrodyn::FrameTransform::from_matrix(glam::DMat3::IDENTITY);
if let Some(mut ang_vel_c) = ang_vel {
ang_vel_c.0 = astrodyn::AngularVelocity::<astrodyn::PlanetFixed<P>>::from_raw_si(
glam::DVec3::ZERO,
);
}
if let Some(pfix_fe) = pfix_frame_entity {
let mut frame_rot = frame_rots.get_mut(pfix_fe.0).unwrap_or_else(|err| {
panic!(
"planet_fixed_rotation_system (RotationModel::None \
clear): source {entity:?} has PfixFrameEntityC({:?}) \
but that entity has no FrameRotC ({err:?}). The \
pfix frame entity must be alive with FrameRotC / \
FrameAngVelC attached (spawned by \
register_pfix_frames_system). Either remove the \
stale PfixFrameEntityC marker before despawning \
the pfix frame entity, or ensure the pfix frame \
entity stays alive for as long as the source \
carries the handle.",
pfix_fe.0
)
});
*frame_rot = FrameRotC::default();
let mut frame_av = frame_ang_vels.get_mut(pfix_fe.0).unwrap_or_else(|err| {
panic!(
"planet_fixed_rotation_system (RotationModel::None \
clear): source {entity:?} has PfixFrameEntityC({:?}) \
but that entity has no FrameAngVelC ({err:?}). The \
pfix frame entity must be alive with FrameRotC / \
FrameAngVelC attached (spawned by \
register_pfix_frames_system). Either remove the \
stale PfixFrameEntityC marker before despawning \
the pfix frame entity, or ensure the pfix frame \
entity stays alive for as long as the source \
carries the handle.",
pfix_fe.0
)
});
frame_av.0 = glam::DVec3::ZERO;
commands
.entity(pfix_fe.0)
.insert(Name::new(format!("pfix.retired:{:?}", pfix_fe.0)));
commands
.entity(entity)
.remove::<PfixFrameEntityC>()
.insert(RetiredPfixFrameEntityC(pfix_fe.0));
}
}
}
}
pub fn tidal_update_system<P: Planet>(
mut query: Query<(&TidalConfigC, &PlanetFixedRotationC<P>, &mut TidalDeltaC20C)>,
) {
for (config, rotation, mut delta) in &mut query {
delta.0 = astrodyn::compute_delta_c20_typed(&config.0, rotation.0.matrix_ref());
}
}
#[allow(clippy::type_complexity)]
pub fn ephemeris_update_system<P: Planet>(
ephemeris: Option<Res<crate::EphemerisR>>,
sim_time: Res<SimulationTimeR>,
mut query: Query<(
&EphemerisBodyC,
&mut SourceInertialPositionC,
Option<&mut SourceInertialVelocityC>,
Option<&mut TranslationalStateC<P>>,
)>,
) {
let Some(eph) = ephemeris else {
return;
};
let tdb_jd = sim_time.tdb_julian_date();
for (ephem_body, mut source_pos, source_vel, trans_state) in &mut query {
let (pos_typed, vel_typed) = eph
.get_state_typed(ephem_body.target, ephem_body.observer, tdb_jd)
.unwrap_or_else(|e| {
panic!(
"Ephemeris lookup failed for {:?} wrt {:?} at TDB JD {tdb_jd}: {e}",
ephem_body.target, ephem_body.observer,
)
});
source_pos.0 = pos_typed;
if let Some(mut sv) = source_vel {
sv.0 = vel_typed;
}
if let Some(mut ts) = trans_state {
let pos_si = pos_typed.raw_si();
let vel_si = vel_typed.raw_si();
ts.0.position = astrodyn::Position::<astrodyn::PlanetInertial<P>>::from_raw_si(pos_si);
ts.0.velocity = astrodyn::Velocity::<astrodyn::PlanetInertial<P>>::from_raw_si(vel_si);
}
}
}