dw3000_ng/time.rs
1//! Time-related types based on the DW3000's system time
2
3use core::ops::{Add, Sub};
4
5use serde::{Deserialize, Serialize};
6
7#[cfg(feature = "defmt")]
8use defmt::Format;
9
10/// The maximum value of 40-bit system time stamps.
11pub const TIME_MAX: u64 = 0xffffffffff;
12
13/// Represents an instant in time
14///
15/// You can get the current DW3000 system time by calling [`DW3000::sys_time`].
16///
17/// Internally uses the same 40-bit timestamps that the DW3000 uses.
18///
19/// [`DW3000::sys_time`]: ../hl/struct.DW3000.html#method.sys_time
20#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
21#[cfg_attr(feature = "defmt", derive(Format))]
22#[repr(C)]
23pub struct Instant(u64);
24
25impl Instant {
26 /// Creates a new instance of `Instant`
27 ///
28 /// The given value must fit in a 40-bit timestamp, so:
29 /// 0 <= `value` <= 2^40 - 1
30 ///
31 /// Returns `Some(...)`, if `value` is within the valid range, `None` if it
32 /// isn't.
33 ///
34 /// # Example
35 ///
36 /// ``` rust
37 /// use dw3000_ng::time::{
38 /// TIME_MAX,
39 /// Instant,
40 /// };
41 ///
42 /// let valid_instant = Instant::new(TIME_MAX);
43 /// let invalid_instant = Instant::new(TIME_MAX + 1);
44 ///
45 /// assert!(valid_instant.is_some());
46 /// assert!(invalid_instant.is_none());
47 /// ```
48 pub fn new(value: u64) -> Option<Self> {
49 if value <= TIME_MAX {
50 Some(Instant(value))
51 } else {
52 None
53 }
54 }
55
56 /// Returns the raw 40-bit timestamp
57 ///
58 /// The returned value is guaranteed to be in the following range:
59 /// 0 <= `value` <= 2^40 - 1
60 pub fn value(&self) -> u64 {
61 self.0
62 }
63
64 /// Returns the amount of time passed between the two `Instant`s
65 ///
66 /// Assumes that `&self` represents a later time than the argument
67 /// `earlier`. Please make sure that this is the case, as this method has no
68 /// way of knowing (DW3000 timestamps can overflow, so comparing the
69 /// numerical value of the timestamp doesn't tell anything about order).
70 ///
71 /// # Example
72 ///
73 /// ``` rust
74 /// use dw3000_ng::time::{
75 /// TIME_MAX,
76 /// Instant,
77 /// };
78 ///
79 /// // `unwrap`ing here is okay, since we're passing constants that we know
80 /// // are in the valid range.
81 /// let instant_1 = Instant::new(TIME_MAX - 50).unwrap();
82 /// let instant_2 = Instant::new(TIME_MAX).unwrap();
83 /// let instant_3 = Instant::new(49).unwrap();
84 ///
85 /// // Works as expected, if the later timestamp is larger than the earlier
86 /// // one.
87 /// let duration = instant_2.duration_since(instant_1);
88 /// assert_eq!(duration.value(), 50);
89 ///
90 /// // Still works as expected, if the later timestamp is the numerically
91 /// // smaller value.
92 /// let duration = instant_3.duration_since(instant_2);
93 /// assert_eq!(duration.value(), 50);
94 /// ```
95 pub fn duration_since(&self, earlier: Instant) -> Duration {
96 if self.value() >= earlier.value() {
97 Duration(self.value() - earlier.value())
98 } else {
99 Duration(TIME_MAX - earlier.value() + self.value() + 1)
100 }
101 }
102}
103
104impl Add<Duration> for Instant {
105 type Output = Instant;
106
107 fn add(self, rhs: Duration) -> Self::Output {
108 // Both `Instant` and `Duration` are guaranteed to contain 40-bit
109 // numbers, so this addition will never overflow.
110 let value = (self.value() + rhs.value()) % (TIME_MAX + 1);
111
112 // We made sure to keep the result of the addition within `TIME_MAX`, so
113 // the following will never panic.
114 Instant::new(value).unwrap()
115 }
116}
117
118impl Sub<Duration> for Instant {
119 type Output = Instant;
120
121 fn sub(self, rhs: Duration) -> Self::Output {
122 // Both `Instant` and `Duration` are guaranteed to contain 40-bit
123 // numbers, so this addition will never overflow.
124 let value: u64 = if (self.value() as i64 - rhs.value() as i64) < 0 {
125 TIME_MAX + self.value() - rhs.value()
126 } else {
127 self.value() - rhs.value()
128 };
129
130 // We made sure to keep the result of the addition within `TIME_MAX`, so
131 // the following will never panic.
132 Instant::new(value).unwrap()
133 }
134}
135
136impl Sub<Instant> for Instant {
137 type Output = Instant;
138
139 fn sub(self, rhs: Instant) -> Self::Output {
140 // Both `Instant` and `Duration` are guaranteed to contain 40-bit
141 // numbers, so this addition will never overflow.
142 let value: u64 = if (self.value() as i64 - rhs.value() as i64) < 0 {
143 TIME_MAX + self.value() - rhs.value()
144 } else {
145 self.value() - rhs.value()
146 };
147
148 // We made sure to keep the result of the addition within `TIME_MAX`, so
149 // the following will never panic.
150 Instant::new(value).unwrap()
151 }
152}
153
154/// A duration between two instants in DW3000 system time
155///
156/// Internally uses the same 40-bit timestamps that the DW3000 uses.
157#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
158#[repr(C)]
159pub struct Duration(u64);
160
161impl Duration {
162 /// Creates a new instance of `Duration`
163 ///
164 /// The given value must fit in a 40-bit timestamp, so:
165 /// 0 <= `value` <= 2^40 - 1
166 ///
167 /// Returns `Some(...)`, if `value` is within the valid range, `None` if it
168 /// isn't.
169 ///
170 /// # Example
171 ///
172 /// ``` rust
173 /// use dw3000_ng::time::{
174 /// TIME_MAX,
175 /// Duration,
176 /// };
177 ///
178 /// let valid_duration = Duration::new(TIME_MAX);
179 /// let invalid_duration = Duration::new(TIME_MAX + 1);
180 ///
181 /// assert!(valid_duration.is_some());
182 /// assert!(invalid_duration.is_none());
183 /// ```
184 pub fn new(value: u64) -> Option<Self> {
185 if value <= TIME_MAX {
186 Some(Duration(value))
187 } else {
188 None
189 }
190 }
191
192 /// Creates an instance of `Duration` from a number of nanoseconds, rounding to the nearest
193 ///
194 /// On the DW3000, the TX_TIME/RX_TIME registers have a resolution of 40-bits
195 /// and a unit of 1/(128*499.2*10^6) seconds. This means that 1 nanosecond
196 /// is 63.8976 DW3000 time units.
197 ///
198 /// We do this with fixed point arithmetic, where
199 /// (dividend + (divisor / 2)) / divisor
200 pub fn from_nanos(nanos: u32) -> Self {
201 // `nanos` takes up at most 32 bits before it is cast to `u64`. That
202 // means the result of the multiplication fits within 38 bits, so the
203 // following should never panic.
204 Duration::new((nanos as u64 * 638976 + 5000) / 10000).unwrap()
205 }
206
207 /// Returns the raw 40-bit timestamp
208 ///
209 /// The returned value is guaranteed to be in the following range:
210 /// 0 <= `value` <= 2^40 - 1
211 pub fn value(&self) -> u64 {
212 self.0
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn instant_add_duration() {
222 let instant = Instant::new(0).unwrap();
223 let duration = Duration::new(1).unwrap();
224
225 let result = instant + duration;
226
227 assert_eq!(result.value(), 1);
228 }
229
230 #[test]
231 fn instant_sub_duration() {
232 let instant = Instant::new(1).unwrap();
233 let duration = Duration::new(1).unwrap();
234
235 let result = instant - duration;
236
237 assert_eq!(result.value(), 0);
238 }
239
240 #[test]
241 fn instant_sub_instant() {
242 let instant_1 = Instant::new(1).unwrap();
243 let instant_2 = Instant::new(0).unwrap();
244
245 let result = instant_1 - instant_2;
246
247 assert_eq!(result.value(), 1);
248 }
249
250 #[test]
251 fn instant_duration_since() {
252 let instant_1 = Instant::new(1).unwrap();
253 let instant_2 = Instant::new(0).unwrap();
254
255 let result = instant_1.duration_since(instant_2);
256
257 assert_eq!(result.value(), 1);
258 }
259
260 #[test]
261 fn duration_from_nanos() {
262 let duration = Duration::from_nanos(1);
263
264 assert_eq!(duration.value(), 64);
265
266 let duration = Duration::from_nanos(6);
267
268 assert_eq!(duration.value(), 383);
269 }
270}