sidereon_core/sp3/
interpolant.rs1use std::collections::BTreeMap;
4
5use crate::astro::time::model::{Instant, TimeScale};
6use crate::id::GnssSatelliteId;
7use crate::observables::{
8 ObservableEphemerisSource, ObservableState, ObservableStateBatch, ObservablesError,
9};
10use crate::sp3::interp::{
11 gather_sp3_precise_series, instant_to_j2000_seconds, interpolate_precise_state,
12 PreciseSatSeries,
13};
14use crate::sp3::{
15 PreciseEphemerisSample, PreciseEphemerisSamples, PreciseSamplesError, Sp3, Sp3State,
16};
17use crate::{Error, Result};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum PreciseInterpolantError {
22 Samples(PreciseSamplesError),
24}
25
26impl core::fmt::Display for PreciseInterpolantError {
27 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
28 match self {
29 Self::Samples(err) => write!(f, "{err}"),
30 }
31 }
32}
33
34impl std::error::Error for PreciseInterpolantError {}
35
36impl From<PreciseSamplesError> for PreciseInterpolantError {
37 fn from(error: PreciseSamplesError) -> Self {
38 Self::Samples(error)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq)]
49pub struct PreciseEphemerisInterpolant {
50 time_scale: TimeScale,
51 nodes: BTreeMap<GnssSatelliteId, PreciseSatSeries>,
52}
53
54impl PreciseEphemerisInterpolant {
55 pub fn from_sp3(source: &Sp3) -> Self {
60 let mut nodes = BTreeMap::new();
61 for &sat in source.satellites() {
62 let series = gather_sp3_precise_series(source, sat);
63 if !series.x.is_empty() {
64 nodes.insert(sat, series);
65 }
66 }
67 Self {
68 time_scale: source.header.time_scale,
69 nodes,
70 }
71 }
72
73 pub fn from_samples(
78 samples: impl IntoIterator<Item = PreciseEphemerisSample>,
79 ) -> core::result::Result<Self, PreciseInterpolantError> {
80 let source = PreciseEphemerisSamples::from_samples(samples)?;
81 Ok(Self::from_precise_ephemeris_samples(&source))
82 }
83
84 pub fn from_precise_ephemeris_samples(source: &PreciseEphemerisSamples) -> Self {
86 Self {
87 time_scale: source.time_scale(),
88 nodes: source.node_series().clone(),
89 }
90 }
91
92 pub fn time_scale(&self) -> TimeScale {
94 self.time_scale
95 }
96
97 pub fn satellites(&self) -> impl Iterator<Item = GnssSatelliteId> + '_ {
99 self.nodes.keys().copied()
100 }
101
102 pub fn position_at_j2000_seconds(&self, sat: GnssSatelliteId, query: f64) -> Result<Sp3State> {
109 static EMPTY_F64: [f64; 0] = [];
110 static EMPTY_CLK: [(f64, f64, bool); 0] = [];
111 match self.nodes.get(&sat) {
112 Some(series) => interpolate_precise_state(
113 sat,
114 &series.x,
115 &series.kx,
116 &series.ky,
117 &series.kz,
118 &series.clk,
119 query,
120 ),
121 None => interpolate_precise_state(
122 sat, &EMPTY_F64, &EMPTY_F64, &EMPTY_F64, &EMPTY_F64, &EMPTY_CLK, query,
123 ),
124 }
125 }
126
127 pub fn position(&self, sat: GnssSatelliteId, epoch: Instant) -> Result<Sp3State> {
132 if epoch.scale != self.time_scale {
133 return Err(Error::InvalidInput(format!(
134 "precise-interpolant query time scale {} does not match source time scale {}",
135 epoch.scale.abbrev(),
136 self.time_scale.abbrev()
137 )));
138 }
139 let query = instant_to_j2000_seconds(&epoch).ok_or(Error::EpochOutOfRange)?;
140 self.position_at_j2000_seconds(sat, query)
141 }
142
143 pub fn observable_states_at_j2000_s(
148 &self,
149 satellites: &[GnssSatelliteId],
150 epochs_j2000_s: &[f64],
151 ) -> core::result::Result<ObservableStateBatch, ObservablesError> {
152 <Self as ObservableEphemerisSource>::observable_states_at_j2000_s(
153 self,
154 satellites,
155 epochs_j2000_s,
156 )
157 }
158
159 pub fn observable_states_at_shared_j2000_s(
164 &self,
165 satellites: &[GnssSatelliteId],
166 epoch_j2000_s: f64,
167 ) -> ObservableStateBatch {
168 <Self as ObservableEphemerisSource>::observable_states_at_shared_j2000_s(
169 self,
170 satellites,
171 epoch_j2000_s,
172 )
173 }
174}
175
176impl ObservableEphemerisSource for PreciseEphemerisInterpolant {
177 fn observable_state_at_j2000_s(
178 &self,
179 sat: GnssSatelliteId,
180 t_j2000_s: f64,
181 ) -> core::result::Result<ObservableState, ObservablesError> {
182 let state = self
183 .position_at_j2000_seconds(sat, t_j2000_s)
184 .map_err(ObservablesError::Ephemeris)?;
185 Ok(ObservableState {
186 position_ecef_m: state.position.as_array(),
187 clock_s: state.clock_s,
188 })
189 }
190}