use astrodyn::{Planet, RootInertial, SelfRef};
use bevy::prelude::*;
use glam::DVec3;
use crate::components::*;
use crate::frame_param::FrameOrigin;
use crate::IntegrationDtR;
use super::util::body_integ_origin_in_root;
#[allow(clippy::type_complexity)]
pub fn aero_drag_system<P: Planet>(
mut query: Query<
(
&DragConfigC,
&AtmosphericStateC<P>,
&TranslationalStateC<P>,
&RotationalStateC,
Option<&StructuralTransformC>,
&mut AerodynamicForceC,
),
Without<crate::DetachedSubtreeStateC>,
>,
) {
for (drag_config, atmos, state, rot, struct_xform, mut aero_force) in &mut query {
let t_struct_body = struct_xform.map_or(glam::DMat3::IDENTITY, |s| *s.0.matrix_ref());
let rot_untyped = astrodyn::typed_bridge::rot_typed_to_raw(&rot.0);
let t_inertial_body = rot_untyped.quaternion.left_quat_to_transformation();
let t_inertial_struct =
astrodyn::compute_t_inertial_struct(&t_struct_body, &t_inertial_body);
let result = astrodyn::compute_ballistic_drag_typed::<P, SelfRef>(
&drag_config.0,
&atmos.0,
state.velocity,
&t_inertial_struct,
);
aero_force.force = result.force.raw_si();
aero_force.torque = result.torque.raw_si();
}
}
pub fn gravity_torque_system(
mut query: Query<
(
&GravityAccelerationC,
&RotationalStateC,
&MassPropertiesC,
&mut GravityTorqueC,
),
Without<crate::DetachedSubtreeStateC>,
>,
) {
for (grav, rot, mass, mut torque) in &mut query {
let rot_untyped = astrodyn::typed_bridge::rot_typed_to_raw(&rot.0);
let t_parent_this = rot_untyped.quaternion.left_quat_to_transformation();
torque.0 = astrodyn::compute_gravity_torque_typed::<SelfRef>(
&grav.grav_grad,
&t_parent_this,
mass.0.inertia,
);
}
}
fn compute_illum_factor<P: Planet>(
vehicle_pos: DVec3,
sun_pos: DVec3,
shadow_bodies: &Query<(&TranslationalStateC<P>, &ShadowBodyC), Without<SunMarker>>,
) -> f64 {
let mut illum = 1.0_f64;
for (body_state, shadow) in shadow_bodies.iter() {
let factor = astrodyn::compute_shadow_fraction(
vehicle_pos,
sun_pos,
body_state.position.raw_si(),
shadow.radius,
astrodyn::SOLAR_RADIUS,
);
illum = illum.min(factor);
}
illum
}
#[allow(clippy::type_complexity)]
pub fn earth_lighting_system<P: Planet>(
frame_origin: FrameOrigin,
root_frame_entity: Res<crate::RootFrameEntityR>,
parents: Query<&ChildOf>,
mut query: Query<
(
&TranslationalStateC<P>,
Option<&FrameEntityC>,
&EarthLightingConfigC,
&mut EarthLightingStateC,
),
(Without<SunMarker>, Without<MoonMarker>),
>,
sun_query: Query<&TranslationalStateC<P>, With<SunMarker>>,
moon_query: Query<&TranslationalStateC<P>, With<MoonMarker>>,
) {
let sun_state = match sun_query.single() {
Ok(s) => s,
Err(bevy::ecs::query::QuerySingleError::NoEntities(_)) => {
for (_, _, _, mut lighting) in &mut query {
lighting.0 = Default::default();
}
return;
}
Err(bevy::ecs::query::QuerySingleError::MultipleEntities(_)) => {
panic!(
"Multiple entities with SunMarker found in earth_lighting_system. \
JEOD assumes exactly one Sun body."
);
}
};
let moon_state = match moon_query.single() {
Ok(s) => s,
Err(bevy::ecs::query::QuerySingleError::NoEntities(_)) => {
for (_, _, _, mut lighting) in &mut query {
lighting.0 = Default::default();
}
return;
}
Err(bevy::ecs::query::QuerySingleError::MultipleEntities(_)) => {
panic!(
"Multiple entities with MoonMarker found in earth_lighting_system. \
JEOD assumes exactly one Moon body."
);
}
};
for (state, body_frame, config, mut lighting) in &mut query {
let (integ_origin, _integ_origin_vel) =
body_integ_origin_in_root(body_frame, &parents, root_frame_entity.0, &frame_origin);
let body_pos_rel = state.position.relabel_to::<RootInertial>();
let body_pos = body_pos_rel + integ_origin;
let sun_pos = sun_state.position.relabel_to::<RootInertial>();
let moon_pos = moon_state.position.relabel_to::<RootInertial>();
lighting.0 = astrodyn::compute_earth_lighting_typed(
body_pos,
sun_pos,
moon_pos,
config.sun_radius,
config.earth_radius,
config.moon_radius,
);
}
}
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
pub fn flat_plate_srp_system<P: Planet>(
frame_origin: FrameOrigin,
root_frame_entity: Res<crate::RootFrameEntityR>,
parents: Query<&ChildOf>,
mut query: Query<
(
&mut FlatPlateConfigC,
&TranslationalStateC<P>,
Option<&RotationalStateC>,
Option<&MassPropertiesC>,
Option<&StructuralTransformC>,
Option<&FrameEntityC>,
&mut RadiationForceC,
),
(
Without<SunMarker>,
Without<CannonballSrpC>,
Without<crate::DetachedSubtreeStateC>,
Without<KinematicChildC>,
),
>,
mut kinematic_cleanup: Query<
(&mut FlatPlateConfigC, &mut RadiationForceC),
(
With<KinematicChildC>,
Without<SunMarker>,
Without<CannonballSrpC>,
),
>,
sun_query: Query<&TranslationalStateC<P>, With<SunMarker>>,
shadow_bodies: Query<(&TranslationalStateC<P>, &ShadowBodyC), Without<SunMarker>>,
dt: Res<IntegrationDtR>,
) {
for (mut flat_config, mut srp_force) in &mut kinematic_cleanup {
flat_config.stage_inputs = None;
srp_force.force = DVec3::ZERO;
srp_force.torque = DVec3::ZERO;
}
let sun_state = match sun_query.single() {
Ok(s) => Some(s),
Err(bevy::ecs::query::QuerySingleError::NoEntities(_)) => None,
Err(bevy::ecs::query::QuerySingleError::MultipleEntities(_)) => {
panic!(
"Multiple entities with SunMarker found. In JEOD, RadiationPressure \
has exactly one RadiationSource (value member). Ensure exactly one \
Sun entity exists."
);
}
};
let dt = dt.0;
for (mut flat_config, state, rot, mass, struct_xform, body_frame, mut srp_force) in &mut query {
flat_config.stage_inputs = None;
srp_force.force = DVec3::ZERO;
srp_force.torque = DVec3::ZERO;
let Some(sun_state) = sun_state else {
continue;
};
let (integ_origin, _integ_origin_vel) =
body_integ_origin_in_root(body_frame, &parents, root_frame_entity.0, &frame_origin);
let pos_raw = state.position.raw_si() + integ_origin.raw_si();
let sun_pos_raw = sun_state.position.raw_si();
let sun_to_vehicle = pos_raw - sun_pos_raw;
let distance = sun_to_vehicle.length();
if distance < 1.0 {
continue;
}
let flux_inertial_hat = sun_to_vehicle / distance;
let flux_mag = astrodyn::solar_flux_at_distance(distance);
let illum_factor = compute_illum_factor(pos_raw, sun_pos_raw, &shadow_bodies);
let center_grav_raw = mass.map_or(DVec3::ZERO, |m| m.0.center_of_mass.raw_si());
let center_grav = astrodyn::Vec3Ext::m_at::<astrodyn::StructuralFrame<astrodyn::SelfRef>>(
center_grav_raw,
);
match flat_config.integration_order {
astrodyn::ThermalIntegrationOrder::Scheduled => {
let t_inertial_body = rot.map_or(glam::DMat3::IDENTITY, |r| {
r.0.q_inertial_body
.as_witness()
.left_quat_to_transformation()
});
let t_struct_body =
struct_xform.map_or(glam::DMat3::IDENTITY, |s| *s.0.matrix_ref());
let t_inertial_struct =
astrodyn::compute_t_inertial_struct(&t_struct_body, &t_inertial_body);
let flux_struct_hat = t_inertial_struct * flux_inertial_hat;
let srp_result = astrodyn::compute_flat_plate_srp_thermal(
&flat_config.plates,
&flat_config.t_pow4_cached,
flux_struct_hat,
flux_mag,
center_grav.raw_si(),
illum_factor,
);
let force_inertial = t_inertial_struct.transpose() * srp_result.force;
srp_force.force = force_inertial;
srp_force.torque = srp_result.torque;
if dt > 0.0 {
flat_config.integrate_temperatures(&srp_result.temp_dots, dt);
}
}
astrodyn::ThermalIntegrationOrder::DerivativeFirstOrder
| astrodyn::ThermalIntegrationOrder::DerivativeRk4 => {
let sun_pos_root = sun_state.position.relabel_to::<astrodyn::RootInertial>();
flat_config.stage_inputs = Some(astrodyn::FlatPlateStageInputs {
sun_position: sun_pos_root,
illum_factor,
center_grav,
});
}
}
}
}
#[allow(clippy::type_complexity)]
pub fn cannonball_srp_system<P: Planet>(
frame_origin: FrameOrigin,
root_frame_entity: Res<crate::RootFrameEntityR>,
parents: Query<&ChildOf>,
mut query: Query<
(
&CannonballSrpC,
&TranslationalStateC<P>,
Option<&FrameEntityC>,
&mut RadiationForceC,
),
(
Without<SunMarker>,
Without<FlatPlateConfigC>,
Without<crate::DetachedSubtreeStateC>,
),
>,
sun_query: Query<&TranslationalStateC<P>, With<SunMarker>>,
shadow_bodies: Query<(&TranslationalStateC<P>, &ShadowBodyC), Without<SunMarker>>,
) {
let sun_state = match sun_query.single() {
Ok(s) => s,
Err(bevy::ecs::query::QuerySingleError::NoEntities(_)) => return,
Err(bevy::ecs::query::QuerySingleError::MultipleEntities(_)) => {
panic!(
"Multiple entities with SunMarker found. \
Ensure exactly one Sun entity exists."
);
}
};
for (config, state, body_frame, mut srp_force) in &mut query {
let (integ_origin, _integ_origin_vel) =
body_integ_origin_in_root(body_frame, &parents, root_frame_entity.0, &frame_origin);
let pos_raw = state.position.raw_si() + integ_origin.raw_si();
let sun_pos_raw = sun_state.position.raw_si();
let illum_factor = compute_illum_factor(pos_raw, sun_pos_raw, &shadow_bodies);
srp_force.force = astrodyn::compute_cannonball_srp(
pos_raw,
sun_pos_raw,
config.cx_area,
config.albedo,
config.diffuse,
illum_factor,
);
srp_force.torque = DVec3::ZERO;
}
}