Skip to content

Commit d05f980

Browse files
authored
Move timeoffset to gnss_traits (#8)
Signed-off-by: Guillaume W. Bres <[email protected]>
1 parent a465293 commit d05f980

File tree

3 files changed

+265
-1
lines changed

3 files changed

+265
-1
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod processing;
1313
pub use processing::{
1414
Decimate, DecimationError, DecimationFilter, DecimationFilterType, Filter, FilterItem,
1515
MaskError, MaskFilter, MaskOperand, Masking, Preprocessing, Repair, RepairTrait, Split,
16+
TimeOffset, TimeShift,
1617
};
1718

1819
#[cfg(feature = "html")]

src/processing/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ pub use decim::{Decimate, DecimationFilter, DecimationFilterType, Error as Decim
1414
mod split;
1515
pub use split::Split;
1616

17+
mod time;
18+
pub use time::{TimeOffset, TimeShift};
19+
1720
/// Preprocessing Trait is usually implemented by GNSS data
1821
/// to preprocess prior further analysis.
19-
pub trait Preprocessing: Masking + Decimate + Split {
22+
pub trait Preprocessing: Masking + Decimate + Split + TimeShift {
2023
/// Apply [Filter] algorithm on immutable dataset.
2124
fn filter(&self, filter: &Filter) -> Self
2225
where

src/processing/time.rs

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
use hifitime::{Epoch, TimeScale, Unit};
2+
3+
/// Errors during [TimeOffset] exploitation
4+
#[derive(Debug)]
5+
pub enum TimeOffsetError {
6+
/// Timescales should be identical when defining a new [TimeOffset]!
7+
IdenticalTimescales,
8+
/// Epoch is not expressed in one of the two supported [TimeScale]s!
9+
NotSupportedTimescale,
10+
/// Epoch should be expressed in the left-hand side [TimeScale].
11+
InvalidTimescale,
12+
/// [TimeOffset] is outdated and should not apply (difference is too large): should be updated weekly at least!
13+
OutdatedTimeOffset,
14+
}
15+
16+
/// [TimeOffset] used in [TimeShift]ing operations
17+
#[derive(Copy, Clone, PartialEq)]
18+
pub struct TimeOffset {
19+
/// Left hand side (compared) [TimeScale]
20+
lhs: TimeScale,
21+
/// Right hand side (reference) [TimeScale]
22+
rhs: TimeScale,
23+
/// Weekly reference time (counter, nanoseconds)
24+
t_ref_nanos: (u32, u64),
25+
/// Polynomials (s, s.s⁻¹, s.s⁻²)
26+
polynomials: (f64, f64, f64),
27+
}
28+
29+
impl TimeOffset {
30+
/// Define a new [TimeOffset] from this reference [Epoch] expressed
31+
/// in left-hand side [TimeScale] to rhs [TimeScale]
32+
pub fn from_reference_epoch(
33+
t_ref: Epoch,
34+
rhs: TimeScale,
35+
polynomials: (f64, f64, f64),
36+
) -> Result<Self, TimeOffsetError> {
37+
if t_ref.time_scale == rhs {
38+
return Err(TimeOffsetError::IdenticalTimescales);
39+
}
40+
41+
let t_ref_nanos = t_ref.to_time_of_week();
42+
43+
Ok(Self {
44+
rhs,
45+
t_ref_nanos,
46+
polynomials,
47+
lhs: t_ref.time_scale,
48+
})
49+
}
50+
51+
/// Define a new [TimeOffset] from reference time of week and other components
52+
pub fn from_reference_time_of_week(
53+
t_ref_nanos: (u32, u64),
54+
lhs: TimeScale,
55+
rhs: TimeScale,
56+
polynomials: (f64, f64, f64),
57+
) -> Result<Self, TimeOffsetError> {
58+
if lhs == rhs {
59+
return Err(TimeOffsetError::IdenticalTimescales);
60+
}
61+
62+
Ok(Self {
63+
lhs,
64+
rhs,
65+
t_ref_nanos,
66+
polynomials,
67+
})
68+
}
69+
70+
/// Define a new |[TimeScale::GPST] - [TimeScale::UTC]| [TimeOffset] from time of week components
71+
pub fn from_gpst_utc_time_of_week(
72+
t_ref_nanos: (u32, u64),
73+
polynomials: (f64, f64, f64),
74+
) -> Self {
75+
Self::from_reference_time_of_week(t_ref_nanos, TimeScale::GPST, TimeScale::UTC, polynomials)
76+
.unwrap()
77+
}
78+
79+
/// Define a new |[TimeScale::GST] - [TimeScale::UTC]| [TimeOffset] from time of week components
80+
pub fn from_gst_utc_time_of_week(
81+
t_ref_nanos: (u32, u64),
82+
polynomials: (f64, f64, f64),
83+
) -> Self {
84+
Self::from_reference_time_of_week(t_ref_nanos, TimeScale::GST, TimeScale::UTC, polynomials)
85+
.unwrap()
86+
}
87+
88+
/// Define a new |[TimeScale::GST] - [TimeScale::GPST]| [TimeOffset] from time of week components
89+
pub fn from_gst_gpst_time_of_week(
90+
t_ref_nanos: (u32, u64),
91+
polynomials: (f64, f64, f64),
92+
) -> Self {
93+
Self::from_reference_time_of_week(t_ref_nanos, TimeScale::GST, TimeScale::GPST, polynomials)
94+
.unwrap()
95+
}
96+
97+
/// Define a new |[TimeScale::BDT] - [TimeScale::UTC]| [TimeOffset] from time of week components
98+
pub fn from_bdt_utc_time_of_week(
99+
t_ref_nanos: (u32, u64),
100+
polynomials: (f64, f64, f64),
101+
) -> Self {
102+
Self::from_reference_time_of_week(t_ref_nanos, TimeScale::BDT, TimeScale::UTC, polynomials)
103+
.unwrap()
104+
}
105+
106+
/// Define a new |[TimeScale::BDT] - [TimeScale::GST]| [TimeOffset] from time of week components
107+
pub fn from_bdt_gst_time_of_week(
108+
t_ref_nanos: (u32, u64),
109+
polynomials: (f64, f64, f64),
110+
) -> Self {
111+
Self::from_reference_time_of_week(t_ref_nanos, TimeScale::BDT, TimeScale::GST, polynomials)
112+
.unwrap()
113+
}
114+
115+
/// Define a new |[TimeScale::QZSST] - [TimeScale::GPST]| [TimeOffset] from time of week components
116+
pub fn from_qzsst_gpst_time_of_week(
117+
t_ref_nanos: (u32, u64),
118+
polynomials: (f64, f64, f64),
119+
) -> Self {
120+
Self::from_reference_time_of_week(
121+
t_ref_nanos,
122+
TimeScale::QZSST,
123+
TimeScale::GPST,
124+
polynomials,
125+
)
126+
.unwrap()
127+
}
128+
129+
/// Define a new |[TimeScale::QZSST] - [TimeScale::UTC]| [TimeOffset] from time of week components
130+
pub fn from_qzsst_utc_time_of_week(
131+
t_ref_nanos: (u32, u64),
132+
polynomials: (f64, f64, f64),
133+
) -> Self {
134+
Self::from_reference_time_of_week(
135+
t_ref_nanos,
136+
TimeScale::QZSST,
137+
TimeScale::UTC,
138+
polynomials,
139+
)
140+
.unwrap()
141+
}
142+
143+
/// Returns both [TimeScale]s this [TimeOffset] allows converting to.
144+
pub fn supported_timescales(&self) -> (TimeScale, TimeScale) {
145+
(self.lhs, self.rhs)
146+
}
147+
148+
/// Update this [TimeOffset] with new reference epoch and polynomials.
149+
/// NB: this should be expressed in the left-hand side [TimeScale] and we have no means
150+
/// to verify that.
151+
pub fn update_mut(&mut self, t_ref_nanos: (u32, u64), polynomials: (f64, f64, f64)) {
152+
self.t_ref_nanos = t_ref_nanos;
153+
self.polynomials = polynomials;
154+
}
155+
156+
/// Define a new [TimeOffset] with new reference [TimeScale], while preserving other components.
157+
/// NB: this should be expressed in the left-hand side [TimeScale] and we do not verify it!
158+
pub fn with_reference_timescale(&self, ts: TimeScale) -> Self {
159+
let mut s = self.clone();
160+
s.rhs = ts;
161+
s
162+
}
163+
164+
/// Define a new [TimeOffset] with new left-hand side [TimeScale], while preserving other components.
165+
/// This needs to be coupled to either [Self::with_reference_time_of_week_nanos] or
166+
/// [Self::with_reference_epoch] to remain correct.
167+
pub fn with_lhs_timescale(&self, ts: TimeScale) -> Self {
168+
let mut s = self.clone();
169+
s.lhs = ts;
170+
s
171+
}
172+
173+
/// Define a new [TimeOffset] with new reference time of week (in nanoseconds), while preserving other components.
174+
pub fn with_reference_time_of_week_nanos(&self, t_ref_nanos: (u32, u64)) -> Self {
175+
let mut s = self.clone();
176+
s.t_ref_nanos = t_ref_nanos;
177+
s
178+
}
179+
180+
/// Define a new [TimeOffset] with new reference [Epoch] with 1 ns precision.
181+
pub fn with_reference_epoch(&self, t_ref: Epoch) -> Result<Self, TimeOffsetError> {
182+
let mut s = self.clone();
183+
184+
if t_ref.time_scale != self.lhs {
185+
return Err(TimeOffsetError::InvalidTimescale);
186+
}
187+
188+
s.t_ref_nanos = t_ref.to_time_of_week();
189+
Ok(s)
190+
}
191+
192+
/// Define a new [TimeOffset] with new polynomials, while preserving other components.
193+
pub fn with_polynomials(&self, polynomials: (f64, f64, f64)) -> Self {
194+
let mut s = self.clone();
195+
s.polynomials = polynomials;
196+
s
197+
}
198+
199+
/// Returns the total number of nanoseconds to apply to convert this [Epoch]
200+
/// into either of [Self::supported_timescales].
201+
/// NB:
202+
/// - `t` must be expressed in either of [Self::supported_timescales].
203+
/// - `t` should fall within the reference week, otherwise this will give invalid results.
204+
pub fn time_correction_nanos(&self, t: Epoch) -> Result<f64, TimeOffsetError> {
205+
if t.time_scale != self.lhs && t.time_scale != self.rhs {
206+
return Err(TimeOffsetError::NotSupportedTimescale);
207+
}
208+
209+
let (t_week, t_nanos) = t.to_time_of_week();
210+
let (ref_week, ref_nanos) = self.t_ref_nanos;
211+
212+
// make sure this falls within a week duration (at most)
213+
if t_week > ref_week + 1 || ref_week > t_week + 1 {
214+
return Err(TimeOffsetError::OutdatedTimeOffset);
215+
}
216+
217+
let (a0, a1, a2) = self.polynomials;
218+
let dt_s = (t_nanos as f64 - ref_nanos as f64) * 1.0E-9;
219+
let dt_s = a0 + a1 * dt_s + a2 * dt_s.powi(2);
220+
221+
// support back & forth conversion
222+
if t.time_scale == self.rhs {
223+
Ok(-dt_s)
224+
} else {
225+
Ok(dt_s)
226+
}
227+
}
228+
229+
/// Returns the total number of nanoseconds to apply to convert this [Epoch]
230+
/// into either of [Self::supported_timescales].
231+
/// NB:
232+
/// - `t` must be expressed in either of [Self::supported_timescales].
233+
/// - `t` should fall within the reference week, otherwise this will give invalid results.
234+
pub fn time_correction_seconds(&self, t: Epoch) -> Result<f64, TimeOffsetError> {
235+
let correction_nanos = self.time_correction_nanos(t)?;
236+
Ok(correction_nanos * 1.0E-9)
237+
}
238+
239+
/// Convert this [Epoch] to desired [TimeScale], with 1 nanosecond precision,
240+
/// using this [TimeOffset] definitions.
241+
/// NB:
242+
/// - `t` can be originally expressed in any supported [TimeScale]
243+
/// - `t` should fall within the reference week, otherwise this will give invalid results.
244+
pub fn epoch_time_correction(&self, t: Epoch) -> Result<Epoch, TimeOffsetError> {
245+
let correction_nanos = self.time_correction_nanos(t)?;
246+
let corrected = t + correction_nanos * Unit::Nanosecond;
247+
// perform the swap & return
248+
Ok(corrected.to_time_scale(self.rhs))
249+
}
250+
}
251+
252+
pub trait TimeShift {
253+
/// Convert and shift [TimeScale] using provided [TimeOffset] structure.
254+
fn time_offset(&self, timeoffset: TimeOffset) -> Self
255+
where
256+
Self: Sized;
257+
258+
/// Convert and shift [TimeScale] using provided [TimeOffset] structure.
259+
fn time_offset_mut(&mut self, timeoffset: TimeOffset);
260+
}

0 commit comments

Comments
 (0)