Skip to main content

celestial_coords/eop/
mod.rs

1pub mod bundled;
2pub mod interpolate;
3pub mod parse;
4pub mod record;
5
6pub use record::{EopParameters, EopRecord};
7
8use interpolate::{EopInterpolator, InterpolationMethod};
9
10use crate::{CoordError, CoordResult};
11use std::path::Path;
12
13pub struct EopProvider {
14    interpolator: EopInterpolator,
15}
16
17impl EopProvider {
18    pub fn bundled() -> CoordResult<Self> {
19        let records = bundled::load_bundled_combined()?;
20        Self::from_records(records)
21    }
22
23    pub fn bundled_c04() -> CoordResult<Self> {
24        let records = bundled::load_bundled_c04()?;
25        Self::from_records(records)
26    }
27
28    pub fn from_records(records: Vec<EopRecord>) -> CoordResult<Self> {
29        if records.is_empty() {
30            return Err(CoordError::data_unavailable(
31                "Cannot create EopProvider with empty records",
32            ));
33        }
34        Ok(Self {
35            interpolator: EopInterpolator::new(records),
36        })
37    }
38
39    pub fn with_interpolation(mut self, method: InterpolationMethod) -> Self {
40        self.interpolator = self.interpolator.with_method(method);
41        self
42    }
43
44    pub fn get(&self, mjd: f64) -> CoordResult<EopParameters> {
45        self.interpolator.get(mjd)
46    }
47
48    pub fn time_span(&self) -> Option<(f64, f64)> {
49        self.interpolator.time_span()
50    }
51
52    pub fn record_count(&self) -> usize {
53        self.interpolator.record_count()
54    }
55
56    pub fn from_finals_str(content: &str) -> CoordResult<Self> {
57        let records = parse::parse_finals(content)?;
58        Self::from_records(records)
59    }
60
61    pub fn from_finals_file(path: impl AsRef<Path>) -> CoordResult<Self> {
62        let content = std::fs::read_to_string(path.as_ref()).map_err(|e| {
63            CoordError::external_library("reading finals2000A file", &e.to_string())
64        })?;
65        Self::from_finals_str(&content)
66    }
67
68    pub fn bundled_with_update(path: impl AsRef<Path>) -> CoordResult<Self> {
69        let mut provider = Self::bundled()?;
70        let update_content = std::fs::read_to_string(path.as_ref()).map_err(|e| {
71            CoordError::external_library("reading finals2000A update file", &e.to_string())
72        })?;
73        let update_records = parse::parse_finals(&update_content)?;
74        provider.interpolator.extend(update_records);
75        Ok(provider)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_bundled_provider() {
85        let provider = EopProvider::bundled().unwrap();
86        assert!(provider.record_count() > 0);
87        assert!(provider.time_span().is_some());
88    }
89
90    #[test]
91    fn test_bundled_lookup() {
92        let provider = EopProvider::bundled().unwrap();
93        let params = provider.get(59945.0).unwrap();
94        assert_eq!(params.mjd, 59945.0);
95        assert!(params.x_p.abs() < 1.0);
96        assert!(params.y_p.abs() < 1.0);
97        assert!(params.ut1_utc.abs() < 1.0);
98    }
99
100    #[test]
101    fn test_from_records() {
102        let records = vec![
103            EopRecord::new(60000.0, 0.1, 0.2, 0.01, 0.001).unwrap(),
104            EopRecord::new(60001.0, 0.101, 0.202, 0.011, 0.001).unwrap(),
105        ];
106        let provider = EopProvider::from_records(records).unwrap();
107        let params = provider.get(60000.5).unwrap();
108        assert!((params.x_p - 0.1005).abs() < 1e-7);
109    }
110
111    #[test]
112    fn test_empty_records_rejected() {
113        let result = EopProvider::from_records(vec![]);
114        assert!(result.is_err());
115    }
116
117    #[test]
118    fn test_out_of_range() {
119        let provider = EopProvider::bundled().unwrap();
120        assert!(provider.get(70000.0).is_err());
121    }
122
123    #[test]
124    fn test_immutable_get() {
125        let provider = EopProvider::bundled().unwrap();
126        let _p1 = provider.get(59945.0).unwrap();
127        let _p2 = provider.get(59945.0).unwrap();
128    }
129}