use crate::assist_data::AssistData;
use crate::coordinates::{bary_to_helio, equatorial_to_ecliptic};
use libassist_sys::Ephemeris;
use libassist_sys::ffi;
use crate::observatory::ObservatoryTable;
use crate::origin::Origin;
use crate::{Error, Result};
#[derive(Debug, Clone)]
pub struct BodyState {
pub state: [f64; 6],
pub epoch: f64,
}
pub fn assist_get_state(
data: &AssistData,
origin: &Origin,
epochs: &[f64],
num_threads: Option<usize>,
) -> Result<Vec<BodyState>> {
let ephem = &data.ephem;
let obs_table = data.observatory.as_ref();
let op = |&epoch_mjd: &f64| -> Result<BodyState> {
let state = resolve_origin_state(ephem, origin, epoch_mjd, obs_table)?;
Ok(BodyState {
state,
epoch: epoch_mjd,
})
};
crate::propagate::map_with_threads(epochs, num_threads, op)
}
pub(crate) fn resolve_origin_state(
ephem: &Ephemeris,
origin: &Origin,
epoch_mjd: f64,
obs_table: Option<&ObservatoryTable>,
) -> Result<[f64; 6]> {
let t = ephem.mjd_to_assist_time(epoch_mjd);
if let Some(body_id) = origin.body_id() {
let bary_eq = ephem.get_body_state_array(body_id, t)?;
let sun = ephem.get_body_state_array(ffi::ASSIST_BODY_SUN, t)?;
Ok(equatorial_to_ecliptic(&bary_to_helio(&bary_eq, &sun)))
} else if let Origin::SolarSystemBarycenter = origin {
let sun = ephem.get_body_state_array(ffi::ASSIST_BODY_SUN, t)?;
let helio_eq = [-sun[0], -sun[1], -sun[2], -sun[3], -sun[4], -sun[5]];
Ok(equatorial_to_ecliptic(&helio_eq))
} else if let Origin::Observatory(code) = origin {
let table = obs_table.ok_or_else(|| {
Error::Other("Observatory table required for observatory codes".into())
})?;
compute_observatory_state(ephem, code, t, table)
} else {
unreachable!()
}
}
fn compute_observatory_state(
ephem: &Ephemeris,
code: &str,
t: f64,
obs_table: &ObservatoryTable,
) -> Result<[f64; 6]> {
let entry = obs_table
.get(code)
.ok_or_else(|| Error::InvalidObservatory(format!("Unknown MPC code: {code}")))?;
let earth = ephem.get_body_state_array(ffi::ASSIST_BODY_EARTH, t)?;
let sun = ephem.get_body_state_array(ffi::ASSIST_BODY_SUN, t)?;
if entry.is_space_based() || code == "500" {
return Ok(equatorial_to_ecliptic(&bary_to_helio(&earth, &sun)));
}
let eo = obs_table
.earth_orientation()
.ok_or_else(|| Error::MissingEarthOrientation(code.to_string()))?;
let earth_radius_au = ephem.earth_radius_au();
let lon_rad = entry.longitude_deg.to_radians();
let (sin_lon, cos_lon) = lon_rad.sin_cos();
let bf_x = earth_radius_au * entry.cos_lat * cos_lon;
let bf_y = earth_radius_au * entry.cos_lat * sin_lon;
let bf_z = earth_radius_au * entry.sin_lat;
let jd = t + ephem.jd_ref();
let et = (jd - 2_451_545.0) * 86_400.0;
let r = eo
.rotation_itrf_to_j2000_et(et)
.map_err(|e| Error::Other(format!("earth orientation lookup: {e}")))?;
let obs_x = r[0][0] * bf_x + r[0][1] * bf_y + r[0][2] * bf_z;
let obs_y = r[1][0] * bf_x + r[1][1] * bf_y + r[1][2] * bf_z;
let obs_z = r[2][0] * bf_x + r[2][1] * bf_y + r[2][2] * bf_z;
const OMEGA_EARTH_RAD_S: f64 = 7.292_115_0e-5; const SEC_PER_DAY: f64 = 86_400.0;
let omega = OMEGA_EARTH_RAD_S * SEC_PER_DAY; let vb_x = -omega * bf_y;
let vb_y = omega * bf_x;
let vb_z = 0.0;
let obs_vx = r[0][0] * vb_x + r[0][1] * vb_y + r[0][2] * vb_z;
let obs_vy = r[1][0] * vb_x + r[1][1] * vb_y + r[1][2] * vb_z;
let obs_vz = r[2][0] * vb_x + r[2][1] * vb_y + r[2][2] * vb_z;
let obs_bary = [
earth[0] + obs_x,
earth[1] + obs_y,
earth[2] + obs_z,
earth[3] + obs_vx,
earth[4] + obs_vy,
earth[5] + obs_vz,
];
Ok(equatorial_to_ecliptic(&bary_to_helio(&obs_bary, &sun)))
}