use std::marker::PhantomData;
use crate::astro::eop::{EopError, EopProvider, EopValues, IersEop};
use crate::astro::nutation::NutationModel;
use crate::time::JulianDate;
#[cfg(not(any(feature = "de440", feature = "de441")))]
use crate::calculus::ephemeris::Vsop87Ephemeris;
#[cfg(not(any(feature = "de440", feature = "de441")))]
pub type DefaultEphemeris = Vsop87Ephemeris;
#[cfg(all(feature = "de441", not(siderust_mock_de441)))]
pub type DefaultEphemeris = crate::calculus::ephemeris::De441Ephemeris;
#[cfg(all(feature = "de440", not(feature = "de441"), not(siderust_mock_de440)))]
pub type DefaultEphemeris = crate::calculus::ephemeris::De440Ephemeris;
#[cfg(any(
all(feature = "de441", siderust_mock_de441),
all(feature = "de440", not(feature = "de441"), siderust_mock_de440)
))]
pub type DefaultEphemeris = crate::calculus::ephemeris::Vsop87Ephemeris;
pub type DefaultEop = IersEop;
pub type DefaultNutationModel = crate::astro::nutation::Iau2006A;
#[derive(Debug, Clone)]
pub struct AstroContext<Eph = DefaultEphemeris, Eop = DefaultEop> {
_ephemeris: PhantomData<Eph>,
eop: Eop,
}
impl<Eph, Eop: Default> Default for AstroContext<Eph, Eop> {
fn default() -> Self {
Self {
_ephemeris: PhantomData,
eop: Eop::default(),
}
}
}
impl AstroContext {
#[inline]
pub fn new() -> Self {
Self::default()
}
}
impl<Eph, Eop: Default> AstroContext<Eph, Eop> {
#[inline]
pub fn with_types() -> Self {
Self::default()
}
}
impl<Eph, Eop> AstroContext<Eph, Eop> {
#[inline]
pub fn with_model<Nut: NutationModel>(&self) -> ModelContext<'_, Eph, Eop, Nut> {
ModelContext {
ctx: self,
_nutation: PhantomData,
}
}
}
impl<Eph, Eop: EopProvider> AstroContext<Eph, Eop> {
#[inline]
pub fn try_eop_at(&self, jd_utc: JulianDate) -> Result<EopValues, EopError> {
self.eop.try_eop_at(jd_utc)
}
#[inline]
pub fn try_eop_at_tt(&self, jd_tt: JulianDate) -> Result<EopValues, EopError> {
let jd_utc = crate::astro::earth_rotation::jd_utc_from_tt(jd_tt);
self.try_eop_at(jd_utc)
}
#[inline]
pub fn eop_at_tt(&self, jd_tt: JulianDate) -> EopValues {
let jd_utc = crate::astro::earth_rotation::jd_utc_from_tt(jd_tt);
self.eop_at(jd_utc)
}
#[inline]
pub fn eop_at(&self, jd_utc: JulianDate) -> EopValues {
self.eop.eop_at(jd_utc)
}
#[inline]
pub fn eop(&self) -> &Eop {
&self.eop
}
}
impl<Eop> AstroContext<DefaultEphemeris, Eop> {
#[inline]
pub const fn uses_default_ephemeris(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy)]
pub struct ModelContext<'a, Eph = DefaultEphemeris, Eop = DefaultEop, Nut = DefaultNutationModel> {
ctx: &'a AstroContext<Eph, Eop>,
_nutation: PhantomData<Nut>,
}
impl<'a, Eph, Eop, Nut> ModelContext<'a, Eph, Eop, Nut> {
#[inline]
pub fn astro_context(&self) -> &'a AstroContext<Eph, Eop> {
self.ctx
}
}
pub trait TransformContext {
type Eph;
type Eop: EopProvider;
type Nut: NutationModel;
fn astro_context(&self) -> &AstroContext<Self::Eph, Self::Eop>;
}
impl<Eph, Eop: EopProvider> TransformContext for AstroContext<Eph, Eop> {
type Eph = Eph;
type Eop = Eop;
type Nut = DefaultNutationModel;
#[inline]
fn astro_context(&self) -> &AstroContext<Self::Eph, Self::Eop> {
self
}
}
impl<'a, Eph, Eop: EopProvider, Nut: NutationModel> TransformContext
for ModelContext<'a, Eph, Eop, Nut>
{
type Eph = Eph;
type Eop = Eop;
type Nut = Nut;
#[inline]
fn astro_context(&self) -> &AstroContext<Self::Eph, Self::Eop> {
self.ctx
}
}
use crate::calculus::ephemeris::DynEphemeris;
pub struct DynAstroContext<Eop = DefaultEop> {
ephemeris: Box<dyn DynEphemeris>,
eop: Eop,
}
impl DynAstroContext<DefaultEop> {
pub fn with_ephemeris(eph: Box<dyn DynEphemeris>) -> Self {
Self {
ephemeris: eph,
eop: DefaultEop::default(),
}
}
}
impl<Eop: Default> DynAstroContext<Eop> {
pub fn with_ephemeris_and_eop(eph: Box<dyn DynEphemeris>, eop: Eop) -> Self {
Self {
ephemeris: eph,
eop,
}
}
}
impl<Eop: EopProvider> DynAstroContext<Eop> {
#[inline]
pub fn ephemeris(&self) -> &dyn DynEphemeris {
&*self.ephemeris
}
#[inline]
pub fn eop_at(&self, jd_utc: JulianDate) -> EopValues {
self.eop.eop_at(jd_utc)
}
#[inline]
pub fn try_eop_at(&self, jd_utc: JulianDate) -> Result<EopValues, EopError> {
self.eop.try_eop_at(jd_utc)
}
#[inline]
pub fn try_eop_at_tt(&self, jd_tt: JulianDate) -> Result<EopValues, EopError> {
let jd_utc = crate::astro::earth_rotation::jd_utc_from_tt(jd_tt);
self.try_eop_at(jd_utc)
}
#[inline]
pub fn eop_at_tt(&self, jd_tt: JulianDate) -> EopValues {
let jd_utc = crate::astro::earth_rotation::jd_utc_from_tt(jd_tt);
self.eop_at(jd_utc)
}
#[inline]
pub fn eop(&self) -> &Eop {
&self.eop
}
}
impl<Eop> std::fmt::Debug for DynAstroContext<Eop>
where
Eop: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynAstroContext")
.field("ephemeris", &"<dyn DynEphemeris>")
.field("eop", &self.eop)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_context_creation() {
let ctx = AstroContext::new();
assert!(ctx.uses_default_ephemeris());
}
#[test]
fn test_context_default() {
let ctx: AstroContext = Default::default();
assert!(ctx.uses_default_ephemeris());
}
#[test]
fn test_context_with_types() {
let ctx: AstroContext<DefaultEphemeris, DefaultEop> = AstroContext::with_types();
assert!(ctx.uses_default_ephemeris());
}
#[test]
fn test_context_with_model() {
let ctx = AstroContext::new();
let model_ctx = ctx.with_model::<DefaultNutationModel>();
let _ = model_ctx.astro_context();
}
#[test]
fn test_eop_at_returns_values() {
let ctx = AstroContext::new();
let jd = JulianDate::J2000;
let eop = ctx.eop_at_tt(jd);
assert!(eop.dut1.is_finite());
}
#[test]
fn test_eop_ref() {
let ctx = AstroContext::new();
let _eop = ctx.eop();
}
#[test]
fn test_dyn_context_creation() {
use crate::calculus::ephemeris::Vsop87Ephemeris;
let dyn_ctx = DynAstroContext::with_ephemeris(Box::new(Vsop87Ephemeris));
let _eph = dyn_ctx.ephemeris();
let _eop = dyn_ctx.eop_at(JulianDate::J2000);
let _eop_ref = dyn_ctx.eop();
let debug_str = format!("{:?}", dyn_ctx);
assert!(debug_str.contains("DynAstroContext"));
}
#[test]
fn test_dyn_context_with_eop() {
use crate::calculus::ephemeris::Vsop87Ephemeris;
let dyn_ctx = DynAstroContext::with_ephemeris_and_eop(
Box::new(Vsop87Ephemeris),
DefaultEop::default(),
);
let eop = dyn_ctx.eop_at(JulianDate::J2000);
assert!(eop.dut1.is_finite());
}
}