sidereon_core/astro/
apparent.rs1use crate::astro::almanac::{AlmanacError, EphemerisSource};
4use crate::astro::bodies::observe::{
5 apparent_geocentric_analytic_true_of_date_m, apparent_geocentric_spk_true_of_date_m,
6 observe_with_time_scales, ObserveError, ObserveOptions, Target,
7};
8use crate::astro::frames::transforms::{FrameTransformError, GeodeticStationKm};
9use crate::astro::time::scales::TimeScales;
10
11const NAIF_SUN: i32 = 10;
12const NAIF_MOON: i32 = 301;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
16pub struct RaDec {
17 pub right_ascension_deg: f64,
19 pub declination_deg: f64,
21 pub distance_km: f64,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq)]
27pub struct TopocentricApparent {
28 pub radec: RaDec,
30 pub hour_angle_deg: f64,
32 pub altitude_deg: f64,
34}
35
36pub fn apparent_geocentric(
38 target_naif: i32,
39 ts: &TimeScales,
40 source: EphemerisSource<'_>,
41) -> Result<[f64; 3], AlmanacError> {
42 match source {
43 EphemerisSource::Spk(kernel) => {
44 apparent_geocentric_spk_true_of_date_m(target_naif, ts, kernel).map_err(map_observe)
45 }
46 EphemerisSource::Analytic => match target_naif {
47 NAIF_SUN | NAIF_MOON => {
48 apparent_geocentric_analytic_true_of_date_m(target_naif, ts).map_err(map_observe)
49 }
50 _ => Err(AlmanacError::EphemerisRequired),
51 },
52 }
53}
54
55pub fn topocentric_apparent(
57 target_naif: i32,
58 station: &GeodeticStationKm,
59 ts: &TimeScales,
60 source: EphemerisSource<'_>,
61) -> Result<TopocentricApparent, AlmanacError> {
62 let target = match source {
63 EphemerisSource::Spk(kernel) => Target::Spk {
64 kernel,
65 naif_id: target_naif,
66 },
67 EphemerisSource::Analytic => match target_naif {
68 NAIF_SUN => Target::Sun,
69 NAIF_MOON => Target::Moon,
70 _ => return Err(AlmanacError::EphemerisRequired),
71 },
72 };
73 let observation = observe_with_time_scales(station, ts, target, ObserveOptions::default())
74 .map_err(map_observe)?;
75 Ok(TopocentricApparent {
76 radec: RaDec {
77 right_ascension_deg: observation.apparent.right_ascension_deg,
78 declination_deg: observation.apparent.declination_deg,
79 distance_km: observation.apparent.distance_km,
80 },
81 hour_angle_deg: observation.hour_angle_deg,
82 altitude_deg: observation.horizontal.elevation_deg,
83 })
84}
85
86fn map_observe(error: ObserveError) -> AlmanacError {
87 match error {
88 ObserveError::Spk(error) => AlmanacError::Spk(error),
89 ObserveError::FrameTransform(FrameTransformError::InvalidInput { field, reason }) => {
90 AlmanacError::InvalidInput { field, reason }
91 }
92 ObserveError::SunMoon(_) => AlmanacError::Frame("sun_moon"),
93 ObserveError::Angle(_) => AlmanacError::Frame("angle"),
94 ObserveError::UnsupportedSpkFrame { .. } => AlmanacError::Frame("spk_frame"),
95 ObserveError::NonFinite => AlmanacError::InvalidInput {
96 field: "geometry",
97 reason: "must be finite",
98 },
99 ObserveError::DegenerateGeometry => AlmanacError::InvalidInput {
100 field: "geometry",
101 reason: "degenerate",
102 },
103 }
104}