use std::ops::{Add, AddAssign};
use arika::epoch::Epoch;
#[cfg(test)]
use arika::frame::SimpleEci;
use arika::frame::{self, Body, Vec3};
use nalgebra::Vector3;
use crate::OrbitalState;
use crate::attitude::AttitudeState;
pub trait HasAttitude {
fn attitude(&self) -> &AttitudeState;
}
pub trait HasOrbit {
type Frame: arika::frame::Eci;
fn orbit(&self) -> &OrbitalState<Self::Frame>;
}
pub trait HasMass {
fn mass(&self) -> f64;
}
pub struct ExternalLoads<F: frame::Eci = frame::SimpleEci> {
pub acceleration_inertial: Vec3<F>,
pub torque_body: Vec3<Body>,
pub mass_rate: f64,
}
impl<F: frame::Eci> std::fmt::Debug for ExternalLoads<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ExternalLoads")
.field("acceleration_inertial", &self.acceleration_inertial)
.field("torque_body", &self.torque_body)
.field("mass_rate", &self.mass_rate)
.finish()
}
}
impl<F: frame::Eci> Clone for ExternalLoads<F> {
fn clone(&self) -> Self {
Self {
acceleration_inertial: Vec3::from_raw(*self.acceleration_inertial.inner()),
torque_body: Vec3::from_raw(*self.torque_body.inner()),
mass_rate: self.mass_rate,
}
}
}
impl<F: frame::Eci> PartialEq for ExternalLoads<F> {
fn eq(&self, other: &Self) -> bool {
self.acceleration_inertial.inner() == other.acceleration_inertial.inner()
&& self.torque_body.inner() == other.torque_body.inner()
&& self.mass_rate == other.mass_rate
}
}
impl<F: frame::Eci> ExternalLoads<F> {
pub fn zeros() -> Self {
Self {
acceleration_inertial: Vec3::zeros(),
torque_body: Vec3::zeros(),
mass_rate: 0.0,
}
}
pub fn torque(t: Vector3<f64>) -> Self {
Self {
acceleration_inertial: Vec3::zeros(),
torque_body: Vec3::from_raw(t),
mass_rate: 0.0,
}
}
pub fn acceleration(a: Vector3<f64>) -> Self {
Self {
acceleration_inertial: Vec3::from_raw(a),
torque_body: Vec3::zeros(),
mass_rate: 0.0,
}
}
}
impl<F: frame::Eci> Add for ExternalLoads<F> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self {
acceleration_inertial: self.acceleration_inertial + rhs.acceleration_inertial,
torque_body: self.torque_body + rhs.torque_body,
mass_rate: self.mass_rate + rhs.mass_rate,
}
}
}
impl<F: frame::Eci> AddAssign for ExternalLoads<F> {
fn add_assign(&mut self, rhs: Self) {
self.acceleration_inertial += rhs.acceleration_inertial;
self.torque_body += rhs.torque_body;
self.mass_rate += rhs.mass_rate;
}
}
pub trait Model<S, F: frame::Eci = frame::SimpleEci>: Send + Sync {
fn name(&self) -> &str;
fn eval(&self, t: f64, state: &S, epoch: Option<&Epoch>) -> ExternalLoads<F>;
}
impl<S, F: frame::Eci> Model<S, F> for Box<dyn Model<S, F>> {
fn name(&self) -> &str {
(**self).name()
}
fn eval(&self, t: f64, state: &S, epoch: Option<&Epoch>) -> ExternalLoads<F> {
(**self).eval(t, state, epoch)
}
}
#[cfg(test)]
mod external_loads_tests {
use super::*;
fn loads(ax: f64, ay: f64, az: f64, tx: f64, ty: f64, tz: f64, mr: f64) -> ExternalLoads {
ExternalLoads {
acceleration_inertial: Vec3::<SimpleEci>::new(ax, ay, az),
torque_body: Vec3::<Body>::new(tx, ty, tz),
mass_rate: mr,
}
}
#[test]
fn add_component_wise() {
let a = loads(1.0, 2.0, 3.0, 0.1, 0.2, 0.3, -0.5);
let b = loads(10.0, 20.0, 30.0, 1.0, 2.0, 3.0, -0.3);
let sum = a + b;
assert_eq!(
sum.acceleration_inertial,
Vec3::<SimpleEci>::new(11.0, 22.0, 33.0)
);
assert_eq!(sum.torque_body, Vec3::<Body>::new(1.1, 2.2, 3.3));
assert!((sum.mass_rate - (-0.8)).abs() < 1e-15);
}
#[test]
fn add_assign_component_wise() {
let mut a = loads(1.0, 2.0, 3.0, 0.1, 0.2, 0.3, -0.5);
let b = loads(10.0, 20.0, 30.0, 1.0, 2.0, 3.0, -0.3);
a += b;
assert_eq!(
a.acceleration_inertial,
Vec3::<SimpleEci>::new(11.0, 22.0, 33.0)
);
assert_eq!(a.torque_body, Vec3::<Body>::new(1.1, 2.2, 3.3));
assert!((a.mass_rate - (-0.8)).abs() < 1e-15);
}
#[test]
fn add_zeros_identity() {
let w = loads(1.0, 2.0, 3.0, 0.1, 0.2, 0.3, -0.1);
let sum = w.clone() + ExternalLoads::zeros();
assert_eq!(sum, w);
}
}