time library support #70
@ -8,9 +8,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- CUC timestamp was fixed to include leap second corrections because it is based on the TAI
|
||||||
|
time reference. Changed the `TimeReader` trait to allow specifying leap seconds which might
|
||||||
|
be necessary for some timestamps to enable other API.
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
- `From<$EcssEnum$TY> from $TY` for the ECSS enum type definitions.
|
- `From<$EcssEnum$TY> from $TY` for the ECSS enum type definitions.
|
||||||
|
- Added basic support conversions to the `time` library. Introduce new `chrono` and `timelib`
|
||||||
|
feature gate
|
||||||
|
|
||||||
# [v0.11.0-rc.0] 2024-03-04
|
# [v0.11.0-rc.0] 2024-03-04
|
||||||
|
|
||||||
|
@ -34,9 +34,15 @@ optional = true
|
|||||||
default-features = false
|
default-features = false
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
|
[dependencies.time]
|
||||||
|
version = "0.3"
|
||||||
|
default-features = false
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dependencies.chrono]
|
[dependencies.chrono]
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dependencies.num-traits]
|
[dependencies.num-traits]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
@ -50,6 +56,8 @@ default = ["std"]
|
|||||||
std = ["chrono/std", "chrono/clock", "alloc", "thiserror"]
|
std = ["chrono/std", "chrono/clock", "alloc", "thiserror"]
|
||||||
serde = ["dep:serde", "chrono/serde"]
|
serde = ["dep:serde", "chrono/serde"]
|
||||||
alloc = ["postcard/alloc", "chrono/alloc"]
|
alloc = ["postcard/alloc", "chrono/alloc"]
|
||||||
|
chrono = ["dep:chrono"]
|
||||||
|
timelib = ["dep:time"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
@ -43,6 +43,8 @@ deserializing them with an appropriate `serde` provider like
|
|||||||
## Optional Features
|
## Optional Features
|
||||||
|
|
||||||
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
|
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
|
||||||
|
- [`chrono`](https://crates.io/crates/chrono): Add basic support for the `chrono` time library.
|
||||||
|
- [`timelib`](https://crates.io/crates/time): Add basic support for the `time` time library.
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use super::*;
|
|||||||
use crate::private::Sealed;
|
use crate::private::Sealed;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
@ -588,6 +589,20 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
self.calc_unix_seconds(unix_days_seconds, ms_of_day);
|
self.calc_unix_seconds(unix_days_seconds, ms_of_day);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calc_ns_since_last_second(&self) -> u32 {
|
||||||
|
let mut ns_since_last_sec = (self.ms_of_day % 1000) * 10_u32.pow(6);
|
||||||
|
match self.submillis_precision() {
|
||||||
|
SubmillisPrecision::Microseconds => {
|
||||||
|
ns_since_last_sec += self.submillis() * 1000;
|
||||||
|
}
|
||||||
|
SubmillisPrecision::Picoseconds => {
|
||||||
|
ns_since_last_sec += self.submillis() / 1000;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
ns_since_last_sec
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn calc_unix_seconds(&mut self, mut unix_days_seconds: i64, ms_of_day: u32) {
|
fn calc_unix_seconds(&mut self, mut unix_days_seconds: i64, ms_of_day: u32) {
|
||||||
let seconds_of_day = (ms_of_day / 1000) as i64;
|
let seconds_of_day = (ms_of_day / 1000) as i64;
|
||||||
@ -599,7 +614,11 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
self.unix_stamp = UnixTimestamp::const_new(unix_days_seconds, (ms_of_day % 1000) as u16);
|
self.unix_stamp = UnixTimestamp::const_new(unix_days_seconds, (ms_of_day % 1000) as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_date_time(&self, ns_since_last_second: u32) -> Option<DateTime<Utc>> {
|
#[cfg(feature = "chrono")]
|
||||||
|
fn calc_chrono_date_time(
|
||||||
|
&self,
|
||||||
|
ns_since_last_second: u32,
|
||||||
|
) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||||
assert!(
|
assert!(
|
||||||
ns_since_last_second < 10_u32.pow(9),
|
ns_since_last_second < 10_u32.pow(9),
|
||||||
"Invalid MS since last second"
|
"Invalid MS since last second"
|
||||||
@ -612,6 +631,17 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timelib")]
|
||||||
|
fn calc_timelib_date_time(
|
||||||
|
&self,
|
||||||
|
ns_since_last_second: u32,
|
||||||
|
) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
|
||||||
|
Ok(
|
||||||
|
time::OffsetDateTime::from_unix_timestamp(self.unix_stamp.unix_seconds)?
|
||||||
|
+ time::Duration::nanoseconds(ns_since_last_second.into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn length_check(&self, buf: &[u8], len_as_bytes: usize) -> Result<(), TimestampError> {
|
fn length_check(&self, buf: &[u8], len_as_bytes: usize) -> Result<(), TimestampError> {
|
||||||
if buf.len() < len_as_bytes {
|
if buf.len() < len_as_bytes {
|
||||||
return Err(TimestampError::ByteConversion(
|
return Err(TimestampError::ByteConversion(
|
||||||
@ -1155,29 +1185,27 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CcsdsTimeProvider for TimeProvider<Pro
|
|||||||
self.unix_stamp
|
self.unix_stamp
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date_time(&self) -> Option<DateTime<Utc>> {
|
#[cfg(feature = "chrono")]
|
||||||
let mut ns_since_last_sec = (self.ms_of_day % 1000) * 10_u32.pow(6);
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
match self.submillis_precision() {
|
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||||
SubmillisPrecision::Microseconds => {
|
self.calc_chrono_date_time(self.calc_ns_since_last_second())
|
||||||
ns_since_last_sec += self.submillis() * 1000;
|
}
|
||||||
}
|
|
||||||
SubmillisPrecision::Picoseconds => {
|
#[cfg(feature = "timelib")]
|
||||||
ns_since_last_sec += self.submillis() / 1000;
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
|
||||||
}
|
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
|
||||||
_ => (),
|
self.calc_timelib_date_time(self.calc_ns_since_last_second())
|
||||||
}
|
|
||||||
self.calc_date_time(ns_since_last_sec)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeReader for TimeProvider<DaysLen16Bits> {
|
impl TimeReader for TimeProvider<DaysLen16Bits> {
|
||||||
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError> {
|
fn from_bytes_generic(buf: &[u8], _leap_seconds: Option<u32>) -> Result<Self, TimestampError> {
|
||||||
Self::from_bytes_with_u16_days(buf)
|
Self::from_bytes_with_u16_days(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeReader for TimeProvider<DaysLen24Bits> {
|
impl TimeReader for TimeProvider<DaysLen24Bits> {
|
||||||
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError> {
|
fn from_bytes_generic(buf: &[u8], _leap_seconds: Option<u32>) -> Result<Self, TimestampError> {
|
||||||
Self::from_bytes_with_u24_days(buf)
|
Self::from_bytes_with_u24_days(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1329,7 +1357,7 @@ mod tests {
|
|||||||
time_stamper.p_field(),
|
time_stamper.p_field(),
|
||||||
(1, [(CcsdsTimeCodes::Cds as u8) << 4, 0])
|
(1, [(CcsdsTimeCodes::Cds as u8) << 4, 0])
|
||||||
);
|
);
|
||||||
let date_time = time_stamper.date_time().unwrap();
|
let date_time = time_stamper.chrono_date_time().unwrap();
|
||||||
assert_eq!(date_time.year(), 1958);
|
assert_eq!(date_time.year(), 1958);
|
||||||
assert_eq!(date_time.month(), 1);
|
assert_eq!(date_time.month(), 1);
|
||||||
assert_eq!(date_time.day(), 1);
|
assert_eq!(date_time.day(), 1);
|
||||||
@ -1346,7 +1374,7 @@ mod tests {
|
|||||||
time_stamper.submillis_precision(),
|
time_stamper.submillis_precision(),
|
||||||
SubmillisPrecision::Absent
|
SubmillisPrecision::Absent
|
||||||
);
|
);
|
||||||
let date_time = time_stamper.date_time().unwrap();
|
let date_time = time_stamper.chrono_date_time().unwrap();
|
||||||
assert_eq!(date_time.year(), 1970);
|
assert_eq!(date_time.year(), 1970);
|
||||||
assert_eq!(date_time.month(), 1);
|
assert_eq!(date_time.month(), 1);
|
||||||
assert_eq!(date_time.day(), 1);
|
assert_eq!(date_time.day(), 1);
|
||||||
@ -1539,7 +1567,7 @@ mod tests {
|
|||||||
timestamp_now: TimeProvider<T>,
|
timestamp_now: TimeProvider<T>,
|
||||||
compare_stamp: DateTime<Utc>,
|
compare_stamp: DateTime<Utc>,
|
||||||
) {
|
) {
|
||||||
let dt = timestamp_now.date_time().unwrap();
|
let dt = timestamp_now.chrono_date_time().unwrap();
|
||||||
if compare_stamp.year() > dt.year() {
|
if compare_stamp.year() > dt.year() {
|
||||||
assert_eq!(compare_stamp.year() - dt.year(), 1);
|
assert_eq!(compare_stamp.year() - dt.year(), 1);
|
||||||
} else {
|
} else {
|
||||||
@ -1736,7 +1764,7 @@ mod tests {
|
|||||||
time_provider.ms_of_day,
|
time_provider.ms_of_day,
|
||||||
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
||||||
);
|
);
|
||||||
assert_eq!(time_provider.date_time().unwrap(), datetime_utc);
|
assert_eq!(time_provider.chrono_date_time().unwrap(), datetime_utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1790,7 +1818,7 @@ mod tests {
|
|||||||
SubmillisPrecision::Microseconds
|
SubmillisPrecision::Microseconds
|
||||||
);
|
);
|
||||||
assert_eq!(time_provider.submillis(), 500);
|
assert_eq!(time_provider.submillis(), 500);
|
||||||
assert_eq!(time_provider.date_time().unwrap(), datetime_utc);
|
assert_eq!(time_provider.chrono_date_time().unwrap(), datetime_utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1844,7 +1872,7 @@ mod tests {
|
|||||||
SubmillisPrecision::Picoseconds
|
SubmillisPrecision::Picoseconds
|
||||||
);
|
);
|
||||||
assert_eq!(time_provider.submillis(), submilli_nanos * 1000);
|
assert_eq!(time_provider.submillis(), submilli_nanos * 1000);
|
||||||
assert_eq!(time_provider.date_time().unwrap(), datetime_utc);
|
assert_eq!(time_provider.chrono_date_time().unwrap(), datetime_utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1917,7 +1945,7 @@ mod tests {
|
|||||||
time_provider.ms_of_day,
|
time_provider.ms_of_day,
|
||||||
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
||||||
);
|
);
|
||||||
let dt_back = time_provider.date_time().unwrap();
|
let dt_back = time_provider.chrono_date_time().unwrap();
|
||||||
assert_eq!(datetime_utc, dt_back);
|
assert_eq!(datetime_utc, dt_back);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
308
src/time/cuc.rs
308
src/time/cuc.rs
@ -3,7 +3,10 @@
|
|||||||
//!
|
//!
|
||||||
//! The core data structure to do this is the [TimeProviderCcsdsEpoch] struct.
|
//! The core data structure to do this is the [TimeProviderCcsdsEpoch] struct.
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::ops::{Add, AddAssign};
|
use core::ops::{Add, AddAssign};
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
@ -103,6 +106,7 @@ pub enum CucError {
|
|||||||
resolution: FractionalResolution,
|
resolution: FractionalResolution,
|
||||||
value: u64,
|
value: u64,
|
||||||
},
|
},
|
||||||
|
LeapSecondCorrectionError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CucError {
|
impl Display for CucError {
|
||||||
@ -120,6 +124,9 @@ impl Display for CucError {
|
|||||||
"invalid cuc fractional part {value} for resolution {resolution:?}"
|
"invalid cuc fractional part {value} for resolution {resolution:?}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
CucError::LeapSecondCorrectionError => {
|
||||||
|
write!(f, "error while correcting for leap seconds")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,8 +160,10 @@ pub struct FractionalPart(FractionalResolution, u32);
|
|||||||
/// use spacepackets::time::cuc::{FractionalResolution, TimeProviderCcsdsEpoch};
|
/// use spacepackets::time::cuc::{FractionalResolution, TimeProviderCcsdsEpoch};
|
||||||
/// use spacepackets::time::{TimeWriter, CcsdsTimeCodes, TimeReader, CcsdsTimeProvider};
|
/// use spacepackets::time::{TimeWriter, CcsdsTimeCodes, TimeReader, CcsdsTimeProvider};
|
||||||
///
|
///
|
||||||
|
/// const LEAP_SECONDS: u32 = 37;
|
||||||
/// // Highest fractional resolution
|
/// // Highest fractional resolution
|
||||||
/// let timestamp_now = TimeProviderCcsdsEpoch::from_now(FractionalResolution::SixtyNs).expect("creating cuc stamp failed");
|
/// let timestamp_now = TimeProviderCcsdsEpoch::from_now(FractionalResolution::SixtyNs, LEAP_SECONDS)
|
||||||
|
/// .expect("creating cuc stamp failed");
|
||||||
/// let mut raw_stamp = [0; 16];
|
/// let mut raw_stamp = [0; 16];
|
||||||
/// {
|
/// {
|
||||||
/// let written = timestamp_now.write_to_bytes(&mut raw_stamp).expect("writing timestamp failed");
|
/// let written = timestamp_now.write_to_bytes(&mut raw_stamp).expect("writing timestamp failed");
|
||||||
@ -163,7 +172,7 @@ pub struct FractionalPart(FractionalResolution, u32);
|
|||||||
/// assert_eq!(written, 8);
|
/// assert_eq!(written, 8);
|
||||||
/// }
|
/// }
|
||||||
/// {
|
/// {
|
||||||
/// let read_result = TimeProviderCcsdsEpoch::from_bytes(&raw_stamp);
|
/// let read_result = TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&raw_stamp, LEAP_SECONDS);
|
||||||
/// assert!(read_result.is_ok());
|
/// assert!(read_result.is_ok());
|
||||||
/// let stamp_deserialized = read_result.unwrap();
|
/// let stamp_deserialized = read_result.unwrap();
|
||||||
/// assert_eq!(stamp_deserialized, timestamp_now);
|
/// assert_eq!(stamp_deserialized, timestamp_now);
|
||||||
@ -175,6 +184,7 @@ pub struct TimeProviderCcsdsEpoch {
|
|||||||
pfield: u8,
|
pfield: u8,
|
||||||
counter: WidthCounterPair,
|
counter: WidthCounterPair,
|
||||||
fractions: Option<FractionalPart>,
|
fractions: Option<FractionalPart>,
|
||||||
|
leap_seconds: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -187,18 +197,26 @@ pub fn pfield_len(pfield: u8) -> usize {
|
|||||||
|
|
||||||
impl TimeProviderCcsdsEpoch {
|
impl TimeProviderCcsdsEpoch {
|
||||||
/// Create a time provider with a four byte counter and no fractional part.
|
/// Create a time provider with a four byte counter and no fractional part.
|
||||||
pub fn new(counter: u32) -> Self {
|
pub fn new(counter: u32, leap_seconds: u32) -> Self {
|
||||||
// These values are definitely valid, so it is okay to unwrap here.
|
// These values are definitely valid, so it is okay to unwrap here.
|
||||||
Self::new_generic(WidthCounterPair(4, counter), None).unwrap()
|
Self::new_generic(WidthCounterPair(4, counter), None, leap_seconds).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [TimeProviderCcsdsEpoch::new] but allow to supply a fractional part as well.
|
/// Like [TimeProviderCcsdsEpoch::new] but allow to supply a fractional part as well.
|
||||||
pub fn new_with_fractions(counter: u32, fractions: FractionalPart) -> Result<Self, CucError> {
|
pub fn new_with_fractions(
|
||||||
Self::new_generic(WidthCounterPair(4, counter), Some(fractions))
|
counter: u32,
|
||||||
|
fractions: FractionalPart,
|
||||||
|
leap_seconds: u32,
|
||||||
|
) -> Result<Self, CucError> {
|
||||||
|
Self::new_generic(WidthCounterPair(4, counter), Some(fractions), leap_seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fractions with a resolution of ~ 4 ms
|
/// Fractions with a resolution of ~ 4 ms
|
||||||
pub fn new_with_coarse_fractions(counter: u32, subsec_fractions: u8) -> Self {
|
pub fn new_with_coarse_fractions(
|
||||||
|
counter: u32,
|
||||||
|
subsec_fractions: u8,
|
||||||
|
leap_seconds: u32,
|
||||||
|
) -> Self {
|
||||||
// These values are definitely valid, so it is okay to unwrap here.
|
// These values are definitely valid, so it is okay to unwrap here.
|
||||||
Self::new_generic(
|
Self::new_generic(
|
||||||
WidthCounterPair(4, counter),
|
WidthCounterPair(4, counter),
|
||||||
@ -206,12 +224,17 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
FractionalResolution::FourMs,
|
FractionalResolution::FourMs,
|
||||||
subsec_fractions as u32,
|
subsec_fractions as u32,
|
||||||
)),
|
)),
|
||||||
|
leap_seconds,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fractions with a resolution of ~ 16 us
|
/// Fractions with a resolution of ~ 16 us
|
||||||
pub fn new_with_medium_fractions(counter: u32, subsec_fractions: u16) -> Self {
|
pub fn new_with_medium_fractions(
|
||||||
|
counter: u32,
|
||||||
|
subsec_fractions: u16,
|
||||||
|
leap_seconds: u32,
|
||||||
|
) -> Self {
|
||||||
// These values are definitely valid, so it is okay to unwrap here.
|
// These values are definitely valid, so it is okay to unwrap here.
|
||||||
Self::new_generic(
|
Self::new_generic(
|
||||||
WidthCounterPair(4, counter),
|
WidthCounterPair(4, counter),
|
||||||
@ -219,6 +242,7 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
FractionalResolution::FifteenUs,
|
FractionalResolution::FifteenUs,
|
||||||
subsec_fractions as u32,
|
subsec_fractions as u32,
|
||||||
)),
|
)),
|
||||||
|
leap_seconds,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
@ -226,40 +250,63 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
/// Fractions with a resolution of ~ 60 ns. The fractional part value is limited by the
|
/// Fractions with a resolution of ~ 60 ns. The fractional part value is limited by the
|
||||||
/// 24 bits of the fractional field, so this function will fail with
|
/// 24 bits of the fractional field, so this function will fail with
|
||||||
/// [CucError::InvalidFractions] if the fractional value exceeds the value.
|
/// [CucError::InvalidFractions] if the fractional value exceeds the value.
|
||||||
pub fn new_with_fine_fractions(counter: u32, subsec_fractions: u32) -> Result<Self, CucError> {
|
pub fn new_with_fine_fractions(
|
||||||
|
counter: u32,
|
||||||
|
subsec_fractions: u32,
|
||||||
|
leap_seconds: u32,
|
||||||
|
) -> Result<Self, CucError> {
|
||||||
Self::new_generic(
|
Self::new_generic(
|
||||||
WidthCounterPair(4, counter),
|
WidthCounterPair(4, counter),
|
||||||
Some(FractionalPart(
|
Some(FractionalPart(
|
||||||
FractionalResolution::SixtyNs,
|
FractionalResolution::SixtyNs,
|
||||||
subsec_fractions,
|
subsec_fractions,
|
||||||
)),
|
)),
|
||||||
|
leap_seconds,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function will return the current time as a CUC timestamp.
|
/// This function will return the current time as a CUC timestamp.
|
||||||
/// The counter width will always be set to 4 bytes because the normal CCSDS epoch will overflow
|
/// The counter width will always be set to 4 bytes because the normal CCSDS epoch will overflow
|
||||||
/// when using less than that.
|
/// when using less than that.
|
||||||
|
///
|
||||||
|
/// The CUC timestamp uses TAI as the reference time system. Therefore, leap second corrections
|
||||||
|
/// must be applied on top of the UTC based time retrieved from the system in addition to the
|
||||||
|
/// conversion to the CCSDS epoch.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||||
pub fn from_now(fraction_resolution: FractionalResolution) -> Result<Self, StdTimestampError> {
|
pub fn from_now(
|
||||||
|
fraction_resolution: FractionalResolution,
|
||||||
|
leap_seconds: u32,
|
||||||
|
) -> Result<Self, StdTimestampError> {
|
||||||
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||||
let ccsds_epoch = unix_epoch_to_ccsds_epoch(now.as_secs() as i64);
|
let ccsds_epoch = unix_epoch_to_ccsds_epoch(now.as_secs() as i64);
|
||||||
|
ccsds_epoch
|
||||||
|
.checked_add(i64::from(leap_seconds))
|
||||||
|
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
|
||||||
if fraction_resolution == FractionalResolution::Seconds {
|
if fraction_resolution == FractionalResolution::Seconds {
|
||||||
return Ok(Self::new(ccsds_epoch as u32));
|
return Ok(Self::new(ccsds_epoch as u32, leap_seconds));
|
||||||
}
|
}
|
||||||
let fractions =
|
let fractions =
|
||||||
fractional_part_from_subsec_ns(fraction_resolution, now.subsec_nanos() as u64);
|
fractional_part_from_subsec_ns(fraction_resolution, now.subsec_nanos() as u64);
|
||||||
Self::new_with_fractions(ccsds_epoch as u32, fractions.unwrap())
|
Self::new_with_fractions(ccsds_epoch as u32, fractions.unwrap(), leap_seconds)
|
||||||
.map_err(|e| StdTimestampError::Timestamp(e.into()))
|
.map_err(|e| StdTimestampError::Timestamp(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the current time stamp from the current time. The fractional field width remains
|
/// Updates the current time stamp from the current time. The fractional field width remains
|
||||||
/// the same and will be updated accordingly.
|
/// the same and will be updated accordingly.
|
||||||
|
///
|
||||||
|
/// The CUC timestamp uses TAI as the reference time system. Therefore, leap second corrections
|
||||||
|
/// must be applied on top of the UTC based time retrieved from the system in addition to the
|
||||||
|
/// conversion to the CCSDS epoch.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||||
pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> {
|
pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> {
|
||||||
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||||
self.counter.1 = unix_epoch_to_ccsds_epoch(now.as_secs() as i64) as u32;
|
self.counter.1 = unix_epoch_to_ccsds_epoch(now.as_secs() as i64) as u32;
|
||||||
|
self.counter
|
||||||
|
.1
|
||||||
|
.checked_add(self.leap_seconds)
|
||||||
|
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
|
||||||
if self.fractions.is_some() {
|
if self.fractions.is_some() {
|
||||||
self.fractions = fractional_part_from_subsec_ns(
|
self.fractions = fractional_part_from_subsec_ns(
|
||||||
self.fractions.unwrap().0,
|
self.fractions.unwrap().0,
|
||||||
@ -269,17 +316,25 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_date_time(
|
#[cfg(feature = "chrono")]
|
||||||
dt: &DateTime<Utc>,
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
|
pub fn from_chrono_date_time(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
res: FractionalResolution,
|
res: FractionalResolution,
|
||||||
|
leap_seconds: u32,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
// Year before CCSDS epoch is invalid.
|
// Year before CCSDS epoch is invalid.
|
||||||
if dt.year() < 1958 {
|
if dt.year() < 1958 {
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(*dt));
|
return Err(TimestampError::DateBeforeCcsdsEpoch(*dt));
|
||||||
}
|
}
|
||||||
|
let counter = dt
|
||||||
|
.timestamp()
|
||||||
|
.checked_add(i64::from(leap_seconds))
|
||||||
|
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
|
||||||
Self::new_generic(
|
Self::new_generic(
|
||||||
WidthCounterPair(4, dt.timestamp() as u32),
|
WidthCounterPair(4, counter as u32),
|
||||||
fractional_part_from_subsec_ns(res, dt.timestamp_subsec_nanos() as u64),
|
fractional_part_from_subsec_ns(res, dt.timestamp_subsec_nanos() as u64),
|
||||||
|
leap_seconds,
|
||||||
)
|
)
|
||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
@ -289,6 +344,7 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
pub fn from_unix_stamp(
|
pub fn from_unix_stamp(
|
||||||
unix_stamp: &UnixTimestamp,
|
unix_stamp: &UnixTimestamp,
|
||||||
res: FractionalResolution,
|
res: FractionalResolution,
|
||||||
|
leap_seconds: u32,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
let ccsds_epoch = unix_epoch_to_ccsds_epoch(unix_stamp.unix_seconds);
|
let ccsds_epoch = unix_epoch_to_ccsds_epoch(unix_stamp.unix_seconds);
|
||||||
// Negative CCSDS epoch is invalid.
|
// Negative CCSDS epoch is invalid.
|
||||||
@ -297,16 +353,24 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
unix_stamp.as_date_time().unwrap(),
|
unix_stamp.as_date_time().unwrap(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
ccsds_epoch
|
||||||
|
.checked_add(i64::from(leap_seconds))
|
||||||
|
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
|
||||||
let fractions = fractional_part_from_subsec_ns(
|
let fractions = fractional_part_from_subsec_ns(
|
||||||
res,
|
res,
|
||||||
unix_stamp.subsecond_millis() as u64 * 10_u64.pow(6),
|
unix_stamp.subsecond_millis() as u64 * 10_u64.pow(6),
|
||||||
);
|
);
|
||||||
Self::new_generic(WidthCounterPair(4, ccsds_epoch as u32), fractions).map_err(|e| e.into())
|
Self::new_generic(
|
||||||
|
WidthCounterPair(4, ccsds_epoch as u32),
|
||||||
|
fractions,
|
||||||
|
leap_seconds,
|
||||||
|
)
|
||||||
|
.map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_u16_counter(counter: u16) -> Self {
|
pub fn new_u16_counter(counter: u16, leap_seconds: u32) -> Self {
|
||||||
// These values are definitely valid, so it is okay to unwrap here.
|
// These values are definitely valid, so it is okay to unwrap here.
|
||||||
Self::new_generic(WidthCounterPair(2, counter as u32), None).unwrap()
|
Self::new_generic(WidthCounterPair(2, counter as u32), None, leap_seconds).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width_counter_pair(&self) -> WidthCounterPair {
|
pub fn width_counter_pair(&self) -> WidthCounterPair {
|
||||||
@ -352,6 +416,7 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
pub fn new_generic(
|
pub fn new_generic(
|
||||||
counter: WidthCounterPair,
|
counter: WidthCounterPair,
|
||||||
fractions: Option<FractionalPart>,
|
fractions: Option<FractionalPart>,
|
||||||
|
leap_seconds: u32,
|
||||||
) -> Result<Self, CucError> {
|
) -> Result<Self, CucError> {
|
||||||
Self::verify_counter_width(counter.0)?;
|
Self::verify_counter_width(counter.0)?;
|
||||||
if counter.1 > (2u64.pow(counter.0 as u32 * 8) - 1) as u32 {
|
if counter.1 > (2u64.pow(counter.0 as u32 * 8) - 1) as u32 {
|
||||||
@ -367,6 +432,7 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
pfield: Self::build_p_field(counter.0, fractions.map(|v| v.0)),
|
pfield: Self::build_p_field(counter.0, fractions.map(|v| v.0)),
|
||||||
counter,
|
counter,
|
||||||
fractions,
|
fractions,
|
||||||
|
leap_seconds,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,6 +478,8 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn unix_seconds(&self) -> i64 {
|
fn unix_seconds(&self) -> i64 {
|
||||||
ccsds_epoch_to_unix_epoch(self.counter.1 as i64)
|
ccsds_epoch_to_unix_epoch(self.counter.1 as i64)
|
||||||
|
.checked_sub(self.leap_seconds as i64)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This returns the length of the individual components of the CUC timestamp in addition
|
/// This returns the length of the individual components of the CUC timestamp in addition
|
||||||
@ -458,10 +526,7 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TimeReader for TimeProviderCcsdsEpoch {
|
impl TimeReader for TimeProviderCcsdsEpoch {
|
||||||
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError>
|
fn from_bytes_generic(buf: &[u8], leap_seconds: Option<u32>) -> Result<Self, TimestampError> {
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
if buf.len() < MIN_CUC_LEN {
|
if buf.len() < MIN_CUC_LEN {
|
||||||
return Err(TimestampError::ByteConversion(
|
return Err(TimestampError::ByteConversion(
|
||||||
ByteConversionError::FromSliceTooSmall {
|
ByteConversionError::FromSliceTooSmall {
|
||||||
@ -470,6 +535,9 @@ impl TimeReader for TimeProviderCcsdsEpoch {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if leap_seconds.is_none() {
|
||||||
|
return Err(TimestampError::Cuc(CucError::LeapSecondCorrectionError));
|
||||||
|
}
|
||||||
match ccsds_time_code_from_p_field(buf[0]) {
|
match ccsds_time_code_from_p_field(buf[0]) {
|
||||||
Ok(code) => {
|
Ok(code) => {
|
||||||
if code != CcsdsTimeCodes::CucCcsdsEpoch {
|
if code != CcsdsTimeCodes::CucCcsdsEpoch {
|
||||||
@ -536,7 +604,11 @@ impl TimeReader for TimeProviderCcsdsEpoch {
|
|||||||
_ => panic!("unreachable match arm"),
|
_ => panic!("unreachable match arm"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let provider = Self::new_generic(WidthCounterPair(cntr_len, counter), fractions)?;
|
let provider = Self::new_generic(
|
||||||
|
WidthCounterPair(cntr_len, counter),
|
||||||
|
fractions,
|
||||||
|
leap_seconds.unwrap(),
|
||||||
|
)?;
|
||||||
Ok(provider)
|
Ok(provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -621,18 +693,32 @@ impl CcsdsTimeProvider for TimeProviderCcsdsEpoch {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date_time(&self) -> Option<DateTime<Utc>> {
|
#[cfg(feature = "chrono")]
|
||||||
let unix_seconds = self.unix_seconds();
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
let ns = if let Some(fractional_part) = self.fractions {
|
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||||
convert_fractional_part_to_ns(fractional_part)
|
let mut ns = 0;
|
||||||
} else {
|
if let Some(fractional_part) = self.fractions {
|
||||||
0
|
ns = convert_fractional_part_to_ns(fractional_part);
|
||||||
};
|
}
|
||||||
if let LocalResult::Single(res) = Utc.timestamp_opt(unix_seconds, ns as u32) {
|
if let LocalResult::Single(res) = chrono::Utc.timestamp_opt(self.unix_seconds(), ns as u32)
|
||||||
|
{
|
||||||
return Some(res);
|
return Some(res);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timelib")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
|
||||||
|
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
|
||||||
|
let mut ns = 0;
|
||||||
|
if let Some(fractional_part) = self.fractions {
|
||||||
|
ns = convert_fractional_part_to_ns(fractional_part);
|
||||||
|
}
|
||||||
|
Ok(
|
||||||
|
time::OffsetDateTime::from_unix_timestamp(self.unix_seconds())?
|
||||||
|
+ time::Duration::nanoseconds(ns as i64),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_provider_values_after_duration_addition(
|
fn get_provider_values_after_duration_addition(
|
||||||
@ -704,9 +790,10 @@ impl Add<Duration> for TimeProviderCcsdsEpoch {
|
|||||||
get_provider_values_after_duration_addition(&self, duration);
|
get_provider_values_after_duration_addition(&self, duration);
|
||||||
if let Some(fractional_part) = new_fractional_part {
|
if let Some(fractional_part) = new_fractional_part {
|
||||||
// The generated fractional part should always be valid, so its okay to unwrap here.
|
// The generated fractional part should always be valid, so its okay to unwrap here.
|
||||||
return Self::new_with_fractions(new_counter, fractional_part).unwrap();
|
return Self::new_with_fractions(new_counter, fractional_part, self.leap_seconds)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
Self::new(new_counter)
|
Self::new(new_counter, self.leap_seconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,9 +805,14 @@ impl Add<Duration> for &TimeProviderCcsdsEpoch {
|
|||||||
get_provider_values_after_duration_addition(self, duration);
|
get_provider_values_after_duration_addition(self, duration);
|
||||||
if let Some(fractional_part) = new_fractional_part {
|
if let Some(fractional_part) = new_fractional_part {
|
||||||
// The generated fractional part should always be valid, so its okay to unwrap here.
|
// The generated fractional part should always be valid, so its okay to unwrap here.
|
||||||
return Self::Output::new_with_fractions(new_counter, fractional_part).unwrap();
|
return Self::Output::new_with_fractions(
|
||||||
|
new_counter,
|
||||||
|
fractional_part,
|
||||||
|
self.leap_seconds,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
Self::Output::new(new_counter)
|
Self::Output::new(new_counter, self.leap_seconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,9 +824,12 @@ mod tests {
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std::println;
|
use std::println;
|
||||||
|
|
||||||
|
const LEAP_SECONDS: u32 = 37;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic_zero_epoch() {
|
fn test_basic_zero_epoch() {
|
||||||
let zero_cuc = TimeProviderCcsdsEpoch::new(0);
|
// Include leap second correction for this.
|
||||||
|
let zero_cuc = TimeProviderCcsdsEpoch::new(0, LEAP_SECONDS);
|
||||||
assert_eq!(zero_cuc.len_as_bytes(), 5);
|
assert_eq!(zero_cuc.len_as_bytes(), 5);
|
||||||
assert_eq!(zero_cuc.width(), zero_cuc.width_counter_pair().0);
|
assert_eq!(zero_cuc.width(), zero_cuc.width_counter_pair().0);
|
||||||
assert_eq!(zero_cuc.counter(), zero_cuc.width_counter_pair().1);
|
assert_eq!(zero_cuc.counter(), zero_cuc.width_counter_pair().1);
|
||||||
@ -744,21 +839,25 @@ mod tests {
|
|||||||
assert_eq!(counter.1, 0);
|
assert_eq!(counter.1, 0);
|
||||||
let fractions = zero_cuc.width_fractions_pair();
|
let fractions = zero_cuc.width_fractions_pair();
|
||||||
assert!(fractions.is_none());
|
assert!(fractions.is_none());
|
||||||
let dt = zero_cuc.date_time();
|
let dt = zero_cuc.chrono_date_time();
|
||||||
assert!(dt.is_some());
|
assert!(dt.is_some());
|
||||||
let dt = dt.unwrap();
|
let dt = dt.unwrap();
|
||||||
assert_eq!(dt.year(), 1958);
|
assert_eq!(dt.year(), 1957);
|
||||||
assert_eq!(dt.month(), 1);
|
assert_eq!(dt.month(), 12);
|
||||||
assert_eq!(dt.day(), 1);
|
assert_eq!(dt.day(), 31);
|
||||||
assert_eq!(dt.hour(), 0);
|
assert_eq!(dt.hour(), 23);
|
||||||
assert_eq!(dt.minute(), 0);
|
assert_eq!(dt.minute(), 59);
|
||||||
assert_eq!(dt.second(), 0);
|
assert_eq!(dt.second(), 23);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_no_fractions() {
|
fn test_write_no_fractions() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let zero_cuc = TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(4, 0x20102030), None);
|
let zero_cuc = TimeProviderCcsdsEpoch::new_generic(
|
||||||
|
WidthCounterPair(4, 0x20102030),
|
||||||
|
None,
|
||||||
|
LEAP_SECONDS,
|
||||||
|
);
|
||||||
assert!(zero_cuc.is_ok());
|
assert!(zero_cuc.is_ok());
|
||||||
let zero_cuc = zero_cuc.unwrap();
|
let zero_cuc = zero_cuc.unwrap();
|
||||||
let res = zero_cuc.write_to_bytes(&mut buf);
|
let res = zero_cuc.write_to_bytes(&mut buf);
|
||||||
@ -782,10 +881,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_datetime_now() {
|
fn test_datetime_now() {
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
let cuc_now = TimeProviderCcsdsEpoch::from_now(FractionalResolution::SixtyNs);
|
let cuc_now = TimeProviderCcsdsEpoch::from_now(FractionalResolution::SixtyNs, LEAP_SECONDS);
|
||||||
assert!(cuc_now.is_ok());
|
assert!(cuc_now.is_ok());
|
||||||
let cuc_now = cuc_now.unwrap();
|
let cuc_now = cuc_now.unwrap();
|
||||||
let dt_opt = cuc_now.date_time();
|
let dt_opt = cuc_now.chrono_date_time();
|
||||||
assert!(dt_opt.is_some());
|
assert!(dt_opt.is_some());
|
||||||
let dt = dt_opt.unwrap();
|
let dt = dt_opt.unwrap();
|
||||||
let diff = dt - now;
|
let diff = dt - now;
|
||||||
@ -797,11 +896,16 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_read_no_fractions() {
|
fn test_read_no_fractions() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let zero_cuc =
|
let zero_cuc = TimeProviderCcsdsEpoch::new_generic(
|
||||||
TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(4, 0x20102030), None).unwrap();
|
WidthCounterPair(4, 0x20102030),
|
||||||
|
None,
|
||||||
|
LEAP_SECONDS,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
zero_cuc.write_to_bytes(&mut buf).unwrap();
|
zero_cuc.write_to_bytes(&mut buf).unwrap();
|
||||||
let cuc_read_back =
|
let cuc_read_back =
|
||||||
TimeProviderCcsdsEpoch::from_bytes(&buf).expect("reading cuc timestamp failed");
|
TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf, LEAP_SECONDS)
|
||||||
|
.expect("reading cuc timestamp failed");
|
||||||
assert_eq!(cuc_read_back, zero_cuc);
|
assert_eq!(cuc_read_back, zero_cuc);
|
||||||
assert_eq!(cuc_read_back.width_counter_pair().1, 0x20102030);
|
assert_eq!(cuc_read_back.width_counter_pair().1, 0x20102030);
|
||||||
assert_eq!(cuc_read_back.width_fractions_pair(), None);
|
assert_eq!(cuc_read_back.width_fractions_pair(), None);
|
||||||
@ -811,7 +915,8 @@ mod tests {
|
|||||||
fn invalid_read_len() {
|
fn invalid_read_len() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
for i in 0..2 {
|
for i in 0..2 {
|
||||||
let res = TimeProviderCcsdsEpoch::from_bytes(&buf[0..i]);
|
let res =
|
||||||
|
TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf[0..i], LEAP_SECONDS);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let err = res.unwrap_err();
|
let err = res.unwrap_err();
|
||||||
if let TimestampError::ByteConversion(ByteConversionError::FromSliceTooSmall {
|
if let TimestampError::ByteConversion(ByteConversionError::FromSliceTooSmall {
|
||||||
@ -823,10 +928,12 @@ mod tests {
|
|||||||
assert_eq!(expected, 2);
|
assert_eq!(expected, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let large_stamp = TimeProviderCcsdsEpoch::new_with_fine_fractions(22, 300).unwrap();
|
let large_stamp =
|
||||||
|
TimeProviderCcsdsEpoch::new_with_fine_fractions(22, 300, LEAP_SECONDS).unwrap();
|
||||||
large_stamp.write_to_bytes(&mut buf).unwrap();
|
large_stamp.write_to_bytes(&mut buf).unwrap();
|
||||||
for i in 2..large_stamp.len_as_bytes() - 1 {
|
for i in 2..large_stamp.len_as_bytes() - 1 {
|
||||||
let res = TimeProviderCcsdsEpoch::from_bytes(&buf[0..i]);
|
let res =
|
||||||
|
TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf[0..i], LEAP_SECONDS);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let err = res.unwrap_err();
|
let err = res.unwrap_err();
|
||||||
if let TimestampError::ByteConversion(ByteConversionError::FromSliceTooSmall {
|
if let TimestampError::ByteConversion(ByteConversionError::FromSliceTooSmall {
|
||||||
@ -843,7 +950,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn write_and_read_tiny_stamp() {
|
fn write_and_read_tiny_stamp() {
|
||||||
let mut buf = [0; 2];
|
let mut buf = [0; 2];
|
||||||
let cuc = TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(1, 200), None);
|
let cuc = TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(1, 200), None, LEAP_SECONDS);
|
||||||
assert!(cuc.is_ok());
|
assert!(cuc.is_ok());
|
||||||
let cuc = cuc.unwrap();
|
let cuc = cuc.unwrap();
|
||||||
assert_eq!(cuc.len_as_bytes(), 2);
|
assert_eq!(cuc.len_as_bytes(), 2);
|
||||||
@ -852,7 +959,8 @@ mod tests {
|
|||||||
let written = res.unwrap();
|
let written = res.unwrap();
|
||||||
assert_eq!(written, 2);
|
assert_eq!(written, 2);
|
||||||
assert_eq!(buf[1], 200);
|
assert_eq!(buf[1], 200);
|
||||||
let cuc_read_back = TimeProviderCcsdsEpoch::from_bytes(&buf);
|
let cuc_read_back =
|
||||||
|
TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf, LEAP_SECONDS);
|
||||||
assert!(cuc_read_back.is_ok());
|
assert!(cuc_read_back.is_ok());
|
||||||
let cuc_read_back = cuc_read_back.unwrap();
|
let cuc_read_back = cuc_read_back.unwrap();
|
||||||
assert_eq!(cuc_read_back, cuc);
|
assert_eq!(cuc_read_back, cuc);
|
||||||
@ -861,7 +969,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn write_slightly_larger_stamp() {
|
fn write_slightly_larger_stamp() {
|
||||||
let mut buf = [0; 4];
|
let mut buf = [0; 4];
|
||||||
let cuc = TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(2, 40000), None);
|
let cuc =
|
||||||
|
TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(2, 40000), None, LEAP_SECONDS);
|
||||||
assert!(cuc.is_ok());
|
assert!(cuc.is_ok());
|
||||||
let cuc = cuc.unwrap();
|
let cuc = cuc.unwrap();
|
||||||
assert_eq!(cuc.len_as_bytes(), 3);
|
assert_eq!(cuc.len_as_bytes(), 3);
|
||||||
@ -870,7 +979,8 @@ mod tests {
|
|||||||
let written = res.unwrap();
|
let written = res.unwrap();
|
||||||
assert_eq!(written, 3);
|
assert_eq!(written, 3);
|
||||||
assert_eq!(u16::from_be_bytes(buf[1..3].try_into().unwrap()), 40000);
|
assert_eq!(u16::from_be_bytes(buf[1..3].try_into().unwrap()), 40000);
|
||||||
let cuc_read_back = TimeProviderCcsdsEpoch::from_bytes(&buf);
|
let cuc_read_back =
|
||||||
|
TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf, LEAP_SECONDS);
|
||||||
assert!(cuc_read_back.is_ok());
|
assert!(cuc_read_back.is_ok());
|
||||||
let cuc_read_back = cuc_read_back.unwrap();
|
let cuc_read_back = cuc_read_back.unwrap();
|
||||||
assert_eq!(cuc_read_back, cuc);
|
assert_eq!(cuc_read_back, cuc);
|
||||||
@ -882,7 +992,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn write_read_three_byte_cntr_stamp() {
|
fn write_read_three_byte_cntr_stamp() {
|
||||||
let mut buf = [0; 4];
|
let mut buf = [0; 4];
|
||||||
let cuc = TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(3, 2_u32.pow(24) - 2), None);
|
let cuc = TimeProviderCcsdsEpoch::new_generic(
|
||||||
|
WidthCounterPair(3, 2_u32.pow(24) - 2),
|
||||||
|
None,
|
||||||
|
LEAP_SECONDS,
|
||||||
|
);
|
||||||
assert!(cuc.is_ok());
|
assert!(cuc.is_ok());
|
||||||
let cuc = cuc.unwrap();
|
let cuc = cuc.unwrap();
|
||||||
assert_eq!(cuc.len_as_bytes(), 4);
|
assert_eq!(cuc.len_as_bytes(), 4);
|
||||||
@ -893,7 +1007,8 @@ mod tests {
|
|||||||
let mut temp_buf = [0; 4];
|
let mut temp_buf = [0; 4];
|
||||||
temp_buf[1..4].copy_from_slice(&buf[1..4]);
|
temp_buf[1..4].copy_from_slice(&buf[1..4]);
|
||||||
assert_eq!(u32::from_be_bytes(temp_buf), 2_u32.pow(24) - 2);
|
assert_eq!(u32::from_be_bytes(temp_buf), 2_u32.pow(24) - 2);
|
||||||
let cuc_read_back = TimeProviderCcsdsEpoch::from_bytes(&buf);
|
let cuc_read_back =
|
||||||
|
TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf, LEAP_SECONDS);
|
||||||
assert!(cuc_read_back.is_ok());
|
assert!(cuc_read_back.is_ok());
|
||||||
let cuc_read_back = cuc_read_back.unwrap();
|
let cuc_read_back = cuc_read_back.unwrap();
|
||||||
assert_eq!(cuc_read_back, cuc);
|
assert_eq!(cuc_read_back, cuc);
|
||||||
@ -902,7 +1017,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_write_invalid_buf() {
|
fn test_write_invalid_buf() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let res = TimeProviderCcsdsEpoch::new_with_fine_fractions(0, 0);
|
let res = TimeProviderCcsdsEpoch::new_with_fine_fractions(0, 0, LEAP_SECONDS);
|
||||||
let cuc = res.unwrap();
|
let cuc = res.unwrap();
|
||||||
for i in 0..cuc.len_as_bytes() - 1 {
|
for i in 0..cuc.len_as_bytes() - 1 {
|
||||||
let err = cuc.write_to_bytes(&mut buf[0..i]);
|
let err = cuc.write_to_bytes(&mut buf[0..i]);
|
||||||
@ -924,7 +1039,7 @@ mod tests {
|
|||||||
fn invalid_ccsds_stamp_type() {
|
fn invalid_ccsds_stamp_type() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
buf[0] |= (CcsdsTimeCodes::CucAgencyEpoch as u8) << 4;
|
buf[0] |= (CcsdsTimeCodes::CucAgencyEpoch as u8) << 4;
|
||||||
let res = TimeProviderCcsdsEpoch::from_bytes(&buf);
|
let res = TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf, LEAP_SECONDS);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let err = res.unwrap_err();
|
let err = res.unwrap_err();
|
||||||
if let TimestampError::InvalidTimeCode { expected, found } = err {
|
if let TimestampError::InvalidTimeCode { expected, found } = err {
|
||||||
@ -938,7 +1053,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_write_with_coarse_fractions() {
|
fn test_write_with_coarse_fractions() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let cuc = TimeProviderCcsdsEpoch::new_with_coarse_fractions(0x30201060, 120);
|
let cuc = TimeProviderCcsdsEpoch::new_with_coarse_fractions(0x30201060, 120, LEAP_SECONDS);
|
||||||
assert!(cuc.fractions.is_some());
|
assert!(cuc.fractions.is_some());
|
||||||
assert_eq!(cuc.fractions.unwrap().1, 120);
|
assert_eq!(cuc.fractions.unwrap().1, 120);
|
||||||
assert_eq!(cuc.fractions.unwrap().0, FractionalResolution::FourMs);
|
assert_eq!(cuc.fractions.unwrap().0, FractionalResolution::FourMs);
|
||||||
@ -957,10 +1072,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_read_with_coarse_fractions() {
|
fn test_read_with_coarse_fractions() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let cuc = TimeProviderCcsdsEpoch::new_with_coarse_fractions(0x30201060, 120);
|
let cuc = TimeProviderCcsdsEpoch::new_with_coarse_fractions(0x30201060, 120, LEAP_SECONDS);
|
||||||
let res = cuc.write_to_bytes(&mut buf);
|
let res = cuc.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let res = TimeProviderCcsdsEpoch::from_bytes(&buf);
|
let res = TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf, LEAP_SECONDS);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let read_back = res.unwrap();
|
let read_back = res.unwrap();
|
||||||
assert_eq!(read_back, cuc);
|
assert_eq!(read_back, cuc);
|
||||||
@ -969,7 +1084,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_write_with_medium_fractions() {
|
fn test_write_with_medium_fractions() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let cuc = TimeProviderCcsdsEpoch::new_with_medium_fractions(0x30303030, 30000);
|
let cuc =
|
||||||
|
TimeProviderCcsdsEpoch::new_with_medium_fractions(0x30303030, 30000, LEAP_SECONDS);
|
||||||
let res = cuc.write_to_bytes(&mut buf);
|
let res = cuc.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let written = res.unwrap();
|
let written = res.unwrap();
|
||||||
@ -981,10 +1097,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_read_with_medium_fractions() {
|
fn test_read_with_medium_fractions() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let cuc = TimeProviderCcsdsEpoch::new_with_medium_fractions(0x30303030, 30000);
|
let cuc =
|
||||||
|
TimeProviderCcsdsEpoch::new_with_medium_fractions(0x30303030, 30000, LEAP_SECONDS);
|
||||||
let res = cuc.write_to_bytes(&mut buf);
|
let res = cuc.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let res = TimeProviderCcsdsEpoch::from_bytes(&buf);
|
let res = TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf, LEAP_SECONDS);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let cuc_read_back = res.unwrap();
|
let cuc_read_back = res.unwrap();
|
||||||
assert_eq!(cuc_read_back, cuc);
|
assert_eq!(cuc_read_back, cuc);
|
||||||
@ -993,8 +1110,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_write_with_fine_fractions() {
|
fn test_write_with_fine_fractions() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let cuc =
|
let cuc = TimeProviderCcsdsEpoch::new_with_fine_fractions(
|
||||||
TimeProviderCcsdsEpoch::new_with_fine_fractions(0x30303030, u16::MAX as u32 + 60000);
|
0x30303030,
|
||||||
|
u16::MAX as u32 + 60000,
|
||||||
|
LEAP_SECONDS,
|
||||||
|
);
|
||||||
assert!(cuc.is_ok());
|
assert!(cuc.is_ok());
|
||||||
let cuc = cuc.unwrap();
|
let cuc = cuc.unwrap();
|
||||||
let res = cuc.write_to_bytes(&mut buf);
|
let res = cuc.write_to_bytes(&mut buf);
|
||||||
@ -1009,13 +1129,16 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_read_with_fine_fractions() {
|
fn test_read_with_fine_fractions() {
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let cuc =
|
let cuc = TimeProviderCcsdsEpoch::new_with_fine_fractions(
|
||||||
TimeProviderCcsdsEpoch::new_with_fine_fractions(0x30303030, u16::MAX as u32 + 60000);
|
0x30303030,
|
||||||
|
u16::MAX as u32 + 60000,
|
||||||
|
LEAP_SECONDS,
|
||||||
|
);
|
||||||
assert!(cuc.is_ok());
|
assert!(cuc.is_ok());
|
||||||
let cuc = cuc.unwrap();
|
let cuc = cuc.unwrap();
|
||||||
let res = cuc.write_to_bytes(&mut buf);
|
let res = cuc.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let res = TimeProviderCcsdsEpoch::from_bytes(&buf);
|
let res = TimeProviderCcsdsEpoch::from_bytes_with_leap_seconds(&buf, LEAP_SECONDS);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let cuc_read_back = res.unwrap();
|
let cuc_read_back = res.unwrap();
|
||||||
assert_eq!(cuc_read_back, cuc);
|
assert_eq!(cuc_read_back, cuc);
|
||||||
@ -1089,7 +1212,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn update_fractions() {
|
fn update_fractions() {
|
||||||
let mut stamp = TimeProviderCcsdsEpoch::new(2000);
|
let mut stamp = TimeProviderCcsdsEpoch::new(2000, LEAP_SECONDS);
|
||||||
let res = stamp.set_fractions(FractionalPart(FractionalResolution::SixtyNs, 5000));
|
let res = stamp.set_fractions(FractionalPart(FractionalResolution::SixtyNs, 5000));
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
assert!(stamp.fractions.is_some());
|
assert!(stamp.fractions.is_some());
|
||||||
@ -1100,7 +1223,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_fract_resolution() {
|
fn set_fract_resolution() {
|
||||||
let mut stamp = TimeProviderCcsdsEpoch::new(2000);
|
let mut stamp = TimeProviderCcsdsEpoch::new(2000, LEAP_SECONDS);
|
||||||
stamp.set_fractional_resolution(FractionalResolution::SixtyNs);
|
stamp.set_fractional_resolution(FractionalResolution::SixtyNs);
|
||||||
assert!(stamp.fractions.is_some());
|
assert!(stamp.fractions.is_some());
|
||||||
let fractions = stamp.fractions.unwrap();
|
let fractions = stamp.fractions.unwrap();
|
||||||
@ -1135,7 +1258,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_duration_basic() {
|
fn add_duration_basic() {
|
||||||
let mut cuc_stamp = TimeProviderCcsdsEpoch::new(200);
|
let mut cuc_stamp = TimeProviderCcsdsEpoch::new(200, LEAP_SECONDS);
|
||||||
cuc_stamp.set_fractional_resolution(FractionalResolution::FifteenUs);
|
cuc_stamp.set_fractional_resolution(FractionalResolution::FifteenUs);
|
||||||
let duration = Duration::from_millis(2500);
|
let duration = Duration::from_millis(2500);
|
||||||
cuc_stamp += duration;
|
cuc_stamp += duration;
|
||||||
@ -1144,7 +1267,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_duration_basic_on_ref() {
|
fn add_duration_basic_on_ref() {
|
||||||
let mut cuc_stamp = TimeProviderCcsdsEpoch::new(200);
|
let mut cuc_stamp = TimeProviderCcsdsEpoch::new(200, LEAP_SECONDS);
|
||||||
cuc_stamp.set_fractional_resolution(FractionalResolution::FifteenUs);
|
cuc_stamp.set_fractional_resolution(FractionalResolution::FifteenUs);
|
||||||
let duration = Duration::from_millis(2500);
|
let duration = Duration::from_millis(2500);
|
||||||
let new_stamp = cuc_stamp + duration;
|
let new_stamp = cuc_stamp + duration;
|
||||||
@ -1153,7 +1276,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_duration_basic_no_fractions() {
|
fn add_duration_basic_no_fractions() {
|
||||||
let mut cuc_stamp = TimeProviderCcsdsEpoch::new(200);
|
let mut cuc_stamp = TimeProviderCcsdsEpoch::new(200, LEAP_SECONDS);
|
||||||
let duration = Duration::from_millis(2000);
|
let duration = Duration::from_millis(2000);
|
||||||
cuc_stamp += duration;
|
cuc_stamp += duration;
|
||||||
assert_eq!(cuc_stamp.counter(), 202);
|
assert_eq!(cuc_stamp.counter(), 202);
|
||||||
@ -1162,7 +1285,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_duration_basic_on_ref_no_fractions() {
|
fn add_duration_basic_on_ref_no_fractions() {
|
||||||
let cuc_stamp = TimeProviderCcsdsEpoch::new(200);
|
let cuc_stamp = TimeProviderCcsdsEpoch::new(200, LEAP_SECONDS);
|
||||||
let duration = Duration::from_millis(2000);
|
let duration = Duration::from_millis(2000);
|
||||||
let new_stamp = cuc_stamp + duration;
|
let new_stamp = cuc_stamp + duration;
|
||||||
assert_eq!(new_stamp.counter(), 202);
|
assert_eq!(new_stamp.counter(), 202);
|
||||||
@ -1171,7 +1294,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn add_duration_overflow() {
|
fn add_duration_overflow() {
|
||||||
let mut cuc_stamp =
|
let mut cuc_stamp =
|
||||||
TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(1, 255), None).unwrap();
|
TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(1, 255), None, LEAP_SECONDS)
|
||||||
|
.unwrap();
|
||||||
let duration = Duration::from_secs(10);
|
let duration = Duration::from_secs(10);
|
||||||
cuc_stamp += duration;
|
cuc_stamp += duration;
|
||||||
assert_eq!(cuc_stamp.counter.1, 10);
|
assert_eq!(cuc_stamp.counter.1, 10);
|
||||||
@ -1179,7 +1303,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_width_param() {
|
fn test_invalid_width_param() {
|
||||||
let error = TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(8, 0), None);
|
let error = TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(8, 0), None, LEAP_SECONDS);
|
||||||
assert!(error.is_err());
|
assert!(error.is_err());
|
||||||
let error = error.unwrap_err();
|
let error = error.unwrap_err();
|
||||||
if let CucError::InvalidCounterWidth(width) = error {
|
if let CucError::InvalidCounterWidth(width) = error {
|
||||||
@ -1193,14 +1317,18 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_from_dt() {
|
fn test_from_dt() {
|
||||||
let dt = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
|
let dt = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
|
||||||
let cuc =
|
let cuc = TimeProviderCcsdsEpoch::from_chrono_date_time(
|
||||||
TimeProviderCcsdsEpoch::from_date_time(&dt, FractionalResolution::Seconds).unwrap();
|
&dt,
|
||||||
assert_eq!(cuc.counter(), dt.timestamp() as u32);
|
FractionalResolution::Seconds,
|
||||||
|
LEAP_SECONDS,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(cuc.counter(), dt.timestamp() as u32 + LEAP_SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_u16_width() {
|
fn test_new_u16_width() {
|
||||||
let cuc = TimeProviderCcsdsEpoch::new_u16_counter(0);
|
let cuc = TimeProviderCcsdsEpoch::new_u16_counter(0, LEAP_SECONDS);
|
||||||
assert_eq!(cuc.width(), 2);
|
assert_eq!(cuc.width(), 2);
|
||||||
assert_eq!(cuc.counter(), 0);
|
assert_eq!(cuc.counter(), 0);
|
||||||
}
|
}
|
||||||
@ -1208,9 +1336,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn from_unix_stamp() {
|
fn from_unix_stamp() {
|
||||||
let unix_stamp = UnixTimestamp::new(0, 0).unwrap();
|
let unix_stamp = UnixTimestamp::new(0, 0).unwrap();
|
||||||
let cuc =
|
let cuc = TimeProviderCcsdsEpoch::from_unix_stamp(
|
||||||
TimeProviderCcsdsEpoch::from_unix_stamp(&unix_stamp, FractionalResolution::Seconds)
|
&unix_stamp,
|
||||||
.expect("failed to create cuc from unix stamp");
|
FractionalResolution::Seconds,
|
||||||
|
LEAP_SECONDS,
|
||||||
|
)
|
||||||
|
.expect("failed to create cuc from unix stamp");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cuc.counter(),
|
cuc.counter(),
|
||||||
(-DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as u32
|
(-DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as u32
|
||||||
@ -1219,7 +1350,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_counter() {
|
fn test_invalid_counter() {
|
||||||
let cuc_error = TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(1, 256), None);
|
let cuc_error =
|
||||||
|
TimeProviderCcsdsEpoch::new_generic(WidthCounterPair(1, 256), None, LEAP_SECONDS);
|
||||||
assert!(cuc_error.is_err());
|
assert!(cuc_error.is_err());
|
||||||
let cuc_error = cuc_error.unwrap_err();
|
let cuc_error = cuc_error.unwrap_err();
|
||||||
if let CucError::InvalidCounter { width, counter } = cuc_error {
|
if let CucError::InvalidCounter { width, counter } = cuc_error {
|
||||||
@ -1233,7 +1365,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stamp_to_vec() {
|
fn test_stamp_to_vec() {
|
||||||
let stamp = TimeProviderCcsdsEpoch::new_u16_counter(100);
|
let stamp = TimeProviderCcsdsEpoch::new_u16_counter(100, LEAP_SECONDS);
|
||||||
let stamp_vec = stamp.to_vec().unwrap();
|
let stamp_vec = stamp.to_vec().unwrap();
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
stamp.write_to_bytes(&mut buf).unwrap();
|
stamp.write_to_bytes(&mut buf).unwrap();
|
||||||
|
@ -204,10 +204,16 @@ pub trait TimeWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TimeReader {
|
pub trait TimeReader: Sized {
|
||||||
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError>
|
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError> {
|
||||||
where
|
Self::from_bytes_generic(buf, None)
|
||||||
Self: Sized;
|
}
|
||||||
|
|
||||||
|
fn from_bytes_with_leap_seconds(buf: &[u8], leap_seconds: u32) -> Result<Self, TimestampError> {
|
||||||
|
Self::from_bytes_generic(buf, Some(leap_seconds))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes_generic(buf: &[u8], leap_seconds: Option<u32>) -> Result<Self, TimestampError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for generic CCSDS time providers.
|
/// Trait for generic CCSDS time providers.
|
||||||
@ -230,7 +236,13 @@ pub trait CcsdsTimeProvider {
|
|||||||
UnixTimestamp::const_new(self.unix_seconds(), self.subsecond_millis())
|
UnixTimestamp::const_new(self.unix_seconds(), self.subsecond_millis())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date_time(&self) -> Option<DateTime<Utc>>;
|
#[cfg(feature = "chrono")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
|
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>>;
|
||||||
|
|
||||||
|
#[cfg(feature = "timelib")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
|
||||||
|
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00.
|
/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00.
|
||||||
|
Loading…
Reference in New Issue
Block a user