Merge pull request 'More granular error handling' (#73) from more-granular-error-handling into main
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
Reviewed-on: #73
This commit is contained in:
commit
c1b32bca21
@ -43,6 +43,9 @@ to check all the API changes in the **Changed** chapter.
|
|||||||
- `CcsdsTimeProvider::date_time` renamed to `CcsdsTimeProvider::chrono_date_time`.
|
- `CcsdsTimeProvider::date_time` renamed to `CcsdsTimeProvider::chrono_date_time`.
|
||||||
- Added `UnixTime::MIN`, `UnixTime::MAX` and `UnixTime::EPOCH`.
|
- Added `UnixTime::MIN`, `UnixTime::MAX` and `UnixTime::EPOCH`.
|
||||||
- Added `UnixTime::timelib_date_time`.
|
- Added `UnixTime::timelib_date_time`.
|
||||||
|
- Error handling for ECSS and time module is more granular now, with a new
|
||||||
|
`DateBeforeCcsdsEpochError` error and a `DateBeforeCcsdsEpoch` enum variant for both
|
||||||
|
`CdsError` and `CucError`.
|
||||||
|
|
||||||
# [v0.11.0-rc.0] 2024-03-04
|
# [v0.11.0-rc.0] 2024-03-04
|
||||||
|
|
||||||
|
@ -205,13 +205,12 @@ pub trait PusPacket: CcsdsPacket {
|
|||||||
fn crc16(&self) -> Option<u16>;
|
fn crc16(&self) -> Option<u16>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, PusError> {
|
pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, ByteConversionError> {
|
||||||
if raw_data.len() < 2 {
|
if raw_data.len() < 2 {
|
||||||
return Err(ByteConversionError::FromSliceTooSmall {
|
return Err(ByteConversionError::FromSliceTooSmall {
|
||||||
found: raw_data.len(),
|
found: raw_data.len(),
|
||||||
expected: 2,
|
expected: 2,
|
||||||
}
|
});
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
Ok(u16::from_be_bytes(
|
Ok(u16::from_be_bytes(
|
||||||
raw_data[raw_data.len() - 2..raw_data.len()]
|
raw_data[raw_data.len() - 2..raw_data.len()]
|
||||||
@ -248,13 +247,12 @@ pub(crate) fn user_data_from_raw(
|
|||||||
current_idx: usize,
|
current_idx: usize,
|
||||||
total_len: usize,
|
total_len: usize,
|
||||||
slice: &[u8],
|
slice: &[u8],
|
||||||
) -> Result<&[u8], PusError> {
|
) -> Result<&[u8], ByteConversionError> {
|
||||||
match current_idx {
|
match current_idx {
|
||||||
_ if current_idx > total_len - 2 => Err(ByteConversionError::FromSliceTooSmall {
|
_ if current_idx > total_len - 2 => Err(ByteConversionError::FromSliceTooSmall {
|
||||||
found: total_len - 2,
|
found: total_len - 2,
|
||||||
expected: current_idx,
|
expected: current_idx,
|
||||||
}
|
}),
|
||||||
.into()),
|
|
||||||
_ => Ok(&slice[current_idx..total_len - 2]),
|
_ => Ok(&slice[current_idx..total_len - 2]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -653,7 +653,7 @@ impl<'raw_data> PusTcCreator<'raw_data> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
|
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize {
|
||||||
let sph_zc = crate::zc::SpHeader::from(self.sp_header);
|
let sph_zc = crate::zc::SpHeader::from(self.sp_header);
|
||||||
let mut appended_len = PUS_TC_MIN_LEN_WITHOUT_APP_DATA;
|
let mut appended_len = PUS_TC_MIN_LEN_WITHOUT_APP_DATA;
|
||||||
appended_len += self.app_data.len();
|
appended_len += self.app_data.len();
|
||||||
@ -666,7 +666,7 @@ impl<'raw_data> PusTcCreator<'raw_data> {
|
|||||||
let mut digest = CRC_CCITT_FALSE.digest();
|
let mut digest = CRC_CCITT_FALSE.digest();
|
||||||
digest.update(&vec[start_idx..start_idx + appended_len - 2]);
|
digest.update(&vec[start_idx..start_idx + appended_len - 2]);
|
||||||
vec.extend_from_slice(&digest.finalize().to_be_bytes());
|
vec.extend_from_slice(&digest.finalize().to_be_bytes());
|
||||||
Ok(appended_len)
|
appended_len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -761,7 +761,8 @@ pub struct PusTcReader<'raw_data> {
|
|||||||
|
|
||||||
impl<'raw_data> PusTcReader<'raw_data> {
|
impl<'raw_data> PusTcReader<'raw_data> {
|
||||||
/// Create a [PusTcReader] instance from a raw slice. On success, it returns a tuple containing
|
/// Create a [PusTcReader] instance from a raw slice. On success, it returns a tuple containing
|
||||||
/// the instance and the found byte length of the packet.
|
/// the instance and the found byte length of the packet. This function also performs a CRC
|
||||||
|
/// check and will return an appropriate [PusError] if the check fails.
|
||||||
pub fn new(slice: &'raw_data [u8]) -> Result<(Self, usize), PusError> {
|
pub fn new(slice: &'raw_data [u8]) -> Result<(Self, usize), PusError> {
|
||||||
let raw_data_len = slice.len();
|
let raw_data_len = slice.len();
|
||||||
if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
|
if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
|
||||||
@ -1008,9 +1009,7 @@ mod tests {
|
|||||||
fn test_vec_ser_deser() {
|
fn test_vec_ser_deser() {
|
||||||
let pus_tc = base_ping_tc_simple_ctor();
|
let pus_tc = base_ping_tc_simple_ctor();
|
||||||
let mut test_vec = Vec::new();
|
let mut test_vec = Vec::new();
|
||||||
let size = pus_tc
|
let size = pus_tc.append_to_vec(&mut test_vec);
|
||||||
.append_to_vec(&mut test_vec)
|
|
||||||
.expect("Error writing TC to vector");
|
|
||||||
assert_eq!(size, 13);
|
assert_eq!(size, 13);
|
||||||
verify_test_tc_raw(&test_vec.as_slice());
|
verify_test_tc_raw(&test_vec.as_slice());
|
||||||
verify_crc_no_app_data(&test_vec.as_slice());
|
verify_crc_no_app_data(&test_vec.as_slice());
|
||||||
@ -1056,9 +1055,7 @@ mod tests {
|
|||||||
let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]);
|
let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]);
|
||||||
verify_test_tc(&pus_tc, true, 16);
|
verify_test_tc(&pus_tc, true, 16);
|
||||||
let mut test_vec = Vec::new();
|
let mut test_vec = Vec::new();
|
||||||
let size = pus_tc
|
let size = pus_tc.append_to_vec(&mut test_vec);
|
||||||
.append_to_vec(&mut test_vec)
|
|
||||||
.expect("Error writing TC to vector");
|
|
||||||
assert_eq!(test_vec[11], 1);
|
assert_eq!(test_vec[11], 1);
|
||||||
assert_eq!(test_vec[12], 2);
|
assert_eq!(test_vec[12], 2);
|
||||||
assert_eq!(test_vec[13], 3);
|
assert_eq!(test_vec[13], 3);
|
||||||
|
@ -659,6 +659,37 @@ impl<'raw_data> PusTmCreator<'raw_data> {
|
|||||||
self.update_ccsds_data_len();
|
self.update_ccsds_data_len();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write the raw PUS byte representation to a provided buffer.
|
||||||
|
pub fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||||
|
let mut curr_idx = 0;
|
||||||
|
let total_size = self.len_written();
|
||||||
|
if total_size > slice.len() {
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall {
|
||||||
|
found: slice.len(),
|
||||||
|
expected: total_size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.sp_header
|
||||||
|
.write_to_be_bytes(&mut slice[0..CCSDS_HEADER_LEN])?;
|
||||||
|
curr_idx += CCSDS_HEADER_LEN;
|
||||||
|
let sec_header_len = size_of::<zc::PusTmSecHeaderWithoutTimestamp>();
|
||||||
|
let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap();
|
||||||
|
sec_header
|
||||||
|
.write_to_bytes(&mut slice[curr_idx..curr_idx + sec_header_len])
|
||||||
|
.ok_or(ByteConversionError::ZeroCopyToError)?;
|
||||||
|
curr_idx += sec_header_len;
|
||||||
|
slice[curr_idx..curr_idx + self.sec_header.timestamp.len()]
|
||||||
|
.copy_from_slice(self.sec_header.timestamp);
|
||||||
|
curr_idx += self.sec_header.timestamp.len();
|
||||||
|
slice[curr_idx..curr_idx + self.source_data.len()].copy_from_slice(self.source_data);
|
||||||
|
curr_idx += self.source_data.len();
|
||||||
|
let mut digest = CRC_CCITT_FALSE.digest();
|
||||||
|
digest.update(&slice[0..curr_idx]);
|
||||||
|
slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
|
||||||
|
curr_idx += 2;
|
||||||
|
Ok(curr_idx)
|
||||||
|
}
|
||||||
|
|
||||||
/// Append the raw PUS byte representation to a provided [alloc::vec::Vec]
|
/// Append the raw PUS byte representation to a provided [alloc::vec::Vec]
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
|
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
|
||||||
@ -687,34 +718,7 @@ impl WritablePusPacket for PusTmCreator<'_> {
|
|||||||
}
|
}
|
||||||
/// Write the raw PUS byte representation to a provided buffer.
|
/// Write the raw PUS byte representation to a provided buffer.
|
||||||
fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
|
fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
|
||||||
let mut curr_idx = 0;
|
Ok(Self::write_to_bytes(self, slice)?)
|
||||||
let total_size = self.len_written();
|
|
||||||
if total_size > slice.len() {
|
|
||||||
return Err(ByteConversionError::ToSliceTooSmall {
|
|
||||||
found: slice.len(),
|
|
||||||
expected: total_size,
|
|
||||||
}
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
self.sp_header
|
|
||||||
.write_to_be_bytes(&mut slice[0..CCSDS_HEADER_LEN])?;
|
|
||||||
curr_idx += CCSDS_HEADER_LEN;
|
|
||||||
let sec_header_len = size_of::<zc::PusTmSecHeaderWithoutTimestamp>();
|
|
||||||
let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap();
|
|
||||||
sec_header
|
|
||||||
.write_to_bytes(&mut slice[curr_idx..curr_idx + sec_header_len])
|
|
||||||
.ok_or(ByteConversionError::ZeroCopyToError)?;
|
|
||||||
curr_idx += sec_header_len;
|
|
||||||
slice[curr_idx..curr_idx + self.sec_header.timestamp.len()]
|
|
||||||
.copy_from_slice(self.sec_header.timestamp);
|
|
||||||
curr_idx += self.sec_header.timestamp.len();
|
|
||||||
slice[curr_idx..curr_idx + self.source_data.len()].copy_from_slice(self.source_data);
|
|
||||||
curr_idx += self.source_data.len();
|
|
||||||
let mut digest = CRC_CCITT_FALSE.digest();
|
|
||||||
digest.update(&slice[0..curr_idx]);
|
|
||||||
slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
|
|
||||||
curr_idx += 2;
|
|
||||||
Ok(curr_idx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -786,6 +790,9 @@ impl<'raw_data> PusTmReader<'raw_data> {
|
|||||||
/// Create a [PusTmReader] instance from a raw slice. On success, it returns a tuple containing
|
/// Create a [PusTmReader] instance from a raw slice. On success, it returns a tuple containing
|
||||||
/// the instance and the found byte length of the packet. The timestamp length needs to be
|
/// the instance and the found byte length of the packet. The timestamp length needs to be
|
||||||
/// known beforehand.
|
/// known beforehand.
|
||||||
|
///
|
||||||
|
/// This function will check the CRC-16 of the PUS packet and will return an appropriate
|
||||||
|
/// [PusError] if the check fails.
|
||||||
pub fn new(slice: &'raw_data [u8], timestamp_len: usize) -> Result<(Self, usize), PusError> {
|
pub fn new(slice: &'raw_data [u8], timestamp_len: usize) -> Result<(Self, usize), PusError> {
|
||||||
let raw_data_len = slice.len();
|
let raw_data_len = slice.len();
|
||||||
if raw_data_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA {
|
if raw_data_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA {
|
||||||
@ -1258,18 +1265,11 @@ mod tests {
|
|||||||
let res = pus_tm.write_to_bytes(&mut buf);
|
let res = pus_tm.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let error = res.unwrap_err();
|
let error = res.unwrap_err();
|
||||||
assert!(matches!(error, PusError::ByteConversion { .. }));
|
if let ByteConversionError::ToSliceTooSmall { found, expected } = error {
|
||||||
match error {
|
assert_eq!(expected, 22);
|
||||||
PusError::ByteConversion(err) => match err {
|
assert_eq!(found, 16);
|
||||||
ByteConversionError::ToSliceTooSmall { found, expected } => {
|
} else {
|
||||||
assert_eq!(expected, 22);
|
panic!("Invalid error {:?}", error);
|
||||||
assert_eq!(found, 16);
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid PUS error {:?}", err),
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!("Invalid error {:?}", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
124
src/time/cds.rs
124
src/time/cds.rs
@ -34,8 +34,9 @@ use core::any::Any;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ccsds_to_unix_days, unix_to_ccsds_days, CcsdsTimeCode, CcsdsTimeProvider, TimeReader,
|
ccsds_to_unix_days, unix_to_ccsds_days, CcsdsTimeCode, CcsdsTimeProvider,
|
||||||
TimeWriter, TimestampError, UnixTime, MS_PER_DAY, SECONDS_PER_DAY,
|
DateBeforeCcsdsEpochError, TimeReader, TimeWriter, TimestampError, UnixTime, MS_PER_DAY,
|
||||||
|
SECONDS_PER_DAY,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base value for the preamble field for a time field parser to determine the time field type.
|
/// Base value for the preamble field for a time field parser to determine the time field type.
|
||||||
@ -99,6 +100,7 @@ pub enum CdsError {
|
|||||||
/// There are distinct constructors depending on the days field width detected in the preamble
|
/// There are distinct constructors depending on the days field width detected in the preamble
|
||||||
/// field. This error will be returned if there is a missmatch.
|
/// field. This error will be returned if there is a missmatch.
|
||||||
InvalidCtorForDaysOfLenInPreamble(LengthOfDaySegment),
|
InvalidCtorForDaysOfLenInPreamble(LengthOfDaySegment),
|
||||||
|
DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CdsError {
|
impl Display for CdsError {
|
||||||
@ -113,12 +115,27 @@ impl Display for CdsError {
|
|||||||
"wrong constructor for length of day {length_of_day:?} detected in preamble",
|
"wrong constructor for length of day {length_of_day:?} detected in preamble",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
CdsError::DateBeforeCcsdsEpoch(e) => write!(f, "date before CCSDS epoch: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl Error for CdsError {}
|
impl Error for CdsError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
CdsError::DateBeforeCcsdsEpoch(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DateBeforeCcsdsEpochError> for CdsError {
|
||||||
|
fn from(value: DateBeforeCcsdsEpochError) -> Self {
|
||||||
|
Self::DateBeforeCcsdsEpoch(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment {
|
pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment {
|
||||||
if (pfield >> 2) & 0b1 == 1 {
|
if (pfield >> 2) & 0b1 == 1 {
|
||||||
return LengthOfDaySegment::Long24Bits;
|
return LengthOfDaySegment::Long24Bits;
|
||||||
@ -226,11 +243,11 @@ impl ConversionFromUnix {
|
|||||||
unix_seconds: i64,
|
unix_seconds: i64,
|
||||||
subsec_nanos: u32,
|
subsec_nanos: u32,
|
||||||
precision: SubmillisPrecision,
|
precision: SubmillisPrecision,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, DateBeforeCcsdsEpochError> {
|
||||||
let (unix_days, secs_of_day) = calc_unix_days_and_secs_of_day(unix_seconds);
|
let (unix_days, secs_of_day) = calc_unix_days_and_secs_of_day(unix_seconds);
|
||||||
let ccsds_days = unix_to_ccsds_days(unix_days);
|
let ccsds_days = unix_to_ccsds_days(unix_days);
|
||||||
if ccsds_days == 0 && (secs_of_day > 0 || subsec_nanos > 0) || ccsds_days < 0 {
|
if ccsds_days == 0 && (secs_of_day > 0 || subsec_nanos > 0) || ccsds_days < 0 {
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(
|
return Err(DateBeforeCcsdsEpochError(
|
||||||
UnixTime::new_checked(unix_seconds, subsec_nanos)
|
UnixTime::new_checked(unix_seconds, subsec_nanos)
|
||||||
.expect("unix timestamp creation failed"),
|
.expect("unix timestamp creation failed"),
|
||||||
));
|
));
|
||||||
@ -318,19 +335,19 @@ fn calc_unix_days_and_secs_of_day(unix_seconds: i64) -> (i64, u32) {
|
|||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
impl ConversionFromChronoDatetime {
|
impl ConversionFromChronoDatetime {
|
||||||
fn new(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, TimestampError> {
|
fn new(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, DateBeforeCcsdsEpochError> {
|
||||||
Self::new_generic(dt, SubmillisPrecision::Absent)
|
Self::new_generic(dt, SubmillisPrecision::Absent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_submillis_us_prec(
|
fn new_with_submillis_us_prec(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, DateBeforeCcsdsEpochError> {
|
||||||
Self::new_generic(dt, SubmillisPrecision::Microseconds)
|
Self::new_generic(dt, SubmillisPrecision::Microseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_submillis_ps_prec(
|
fn new_with_submillis_ps_prec(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, DateBeforeCcsdsEpochError> {
|
||||||
Self::new_generic(dt, SubmillisPrecision::Picoseconds)
|
Self::new_generic(dt, SubmillisPrecision::Picoseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,10 +355,10 @@ impl ConversionFromChronoDatetime {
|
|||||||
fn new_generic(
|
fn new_generic(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
prec: SubmillisPrecision,
|
prec: SubmillisPrecision,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, DateBeforeCcsdsEpochError> {
|
||||||
// The CDS timestamp does not support timestamps before the CCSDS epoch.
|
// The CDS timestamp does not support timestamps before the CCSDS epoch.
|
||||||
if dt.year() < 1958 {
|
if dt.year() < 1958 {
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTime::from(*dt)));
|
return Err(DateBeforeCcsdsEpochError(UnixTime::from(*dt)));
|
||||||
}
|
}
|
||||||
// The contained values in the conversion should be all positive now
|
// The contained values in the conversion should be all positive now
|
||||||
let unix_conversion =
|
let unix_conversion =
|
||||||
@ -676,7 +693,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
|
|||||||
fn from_dt_generic(
|
fn from_dt_generic(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
let conv_from_dt = ConversionFromChronoDatetime::new(dt)?;
|
let conv_from_dt = ConversionFromChronoDatetime::new(dt)?;
|
||||||
Self::generic_from_conversion(days_len, conv_from_dt)
|
Self::generic_from_conversion(days_len, conv_from_dt)
|
||||||
}
|
}
|
||||||
@ -685,7 +702,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
|
|||||||
fn from_dt_generic_us_prec(
|
fn from_dt_generic_us_prec(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_us_prec(dt)?;
|
let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_us_prec(dt)?;
|
||||||
Self::generic_from_conversion(days_len, conv_from_dt)
|
Self::generic_from_conversion(days_len, conv_from_dt)
|
||||||
}
|
}
|
||||||
@ -694,7 +711,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
|
|||||||
fn from_dt_generic_ps_prec(
|
fn from_dt_generic_ps_prec(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_ps_prec(dt)?;
|
let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_ps_prec(dt)?;
|
||||||
Self::generic_from_conversion(days_len, conv_from_dt)
|
Self::generic_from_conversion(days_len, conv_from_dt)
|
||||||
}
|
}
|
||||||
@ -703,7 +720,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
|
|||||||
unix_stamp: &UnixTime,
|
unix_stamp: &UnixTime,
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
submillis_prec: SubmillisPrecision,
|
submillis_prec: SubmillisPrecision,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
let conv_from_dt =
|
let conv_from_dt =
|
||||||
ConversionFromUnix::new(unix_stamp.secs, unix_stamp.subsec_nanos, submillis_prec)?;
|
ConversionFromUnix::new(unix_stamp.secs, unix_stamp.subsec_nanos, submillis_prec)?;
|
||||||
Self::generic_from_conversion(days_len, conv_from_dt)
|
Self::generic_from_conversion(days_len, conv_from_dt)
|
||||||
@ -713,33 +730,31 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
|
|||||||
fn from_now_generic(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
|
fn from_now_generic(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
|
||||||
let conversion_from_now = ConversionFromNow::new()?;
|
let conversion_from_now = ConversionFromNow::new()?;
|
||||||
Self::generic_from_conversion(days_len, conversion_from_now)
|
Self::generic_from_conversion(days_len, conversion_from_now)
|
||||||
.map_err(StdTimestampError::Timestamp)
|
.map_err(|e| StdTimestampError::Timestamp(TimestampError::from(e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
fn from_now_generic_us_prec(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
|
fn from_now_generic_us_prec(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
|
||||||
let conversion_from_now = ConversionFromNow::new_with_submillis_us_prec()?;
|
let conversion_from_now = ConversionFromNow::new_with_submillis_us_prec()?;
|
||||||
Self::generic_from_conversion(days_len, conversion_from_now)
|
Self::generic_from_conversion(days_len, conversion_from_now)
|
||||||
.map_err(StdTimestampError::Timestamp)
|
.map_err(|e| StdTimestampError::Timestamp(TimestampError::from(e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
fn from_now_generic_ps_prec(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
|
fn from_now_generic_ps_prec(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
|
||||||
let conversion_from_now = ConversionFromNow::new_with_submillis_ps_prec()?;
|
let conversion_from_now = ConversionFromNow::new_with_submillis_ps_prec()?;
|
||||||
Self::generic_from_conversion(days_len, conversion_from_now)
|
Self::generic_from_conversion(days_len, conversion_from_now)
|
||||||
.map_err(StdTimestampError::Timestamp)
|
.map_err(|e| StdTimestampError::Timestamp(TimestampError::from(e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_from_conversion<C: CdsConverter>(
|
fn generic_from_conversion<C: CdsConverter>(
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
converter: C,
|
converter: C,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
let ccsds_days: ProvidesDaysLen::FieldType =
|
let ccsds_days: ProvidesDaysLen::FieldType = converter
|
||||||
converter.ccsds_days_as_u32().try_into().map_err(|_| {
|
.ccsds_days_as_u32()
|
||||||
TimestampError::Cds(CdsError::InvalidCcsdsDays(
|
.try_into()
|
||||||
converter.ccsds_days_as_u32().into(),
|
.map_err(|_| CdsError::InvalidCcsdsDays(converter.ccsds_days_as_u32().into()))?;
|
||||||
))
|
|
||||||
})?;
|
|
||||||
let mut provider = Self {
|
let mut provider = Self {
|
||||||
pfield: Self::generate_p_field(days_len, converter.submillis_precision()),
|
pfield: Self::generate_p_field(days_len, converter.submillis_precision()),
|
||||||
ccsds_days,
|
ccsds_days,
|
||||||
@ -815,13 +830,11 @@ impl CdsTime<DaysLen24Bits> {
|
|||||||
///
|
///
|
||||||
/// ## Errors
|
/// ## Errors
|
||||||
///
|
///
|
||||||
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
/// This function will return [CdsError::DateBeforeCcsdsEpoch] if the time is before the CCSDS
|
||||||
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
/// epoch (1958-01-01T00:00:00+00:00) or the CCSDS days value exceeds the allowed bit width
|
||||||
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
/// (24 bits).
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
pub fn from_dt_with_u24_days(
|
pub fn from_dt_with_u24_days(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, CdsError> {
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
|
||||||
) -> Result<Self, TimestampError> {
|
|
||||||
Self::from_dt_generic(dt, LengthOfDaySegment::Long24Bits)
|
Self::from_dt_generic(dt, LengthOfDaySegment::Long24Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -829,13 +842,13 @@ impl CdsTime<DaysLen24Bits> {
|
|||||||
///
|
///
|
||||||
/// ## Errors
|
/// ## Errors
|
||||||
///
|
///
|
||||||
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
/// This function will return [CdsError::DateBeforeCcsdsEpoch] if the time is before the CCSDS
|
||||||
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
/// epoch (1958-01-01T00:00:00+00:00) or the CCSDS days value exceeds the allowed bit width
|
||||||
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
/// (24 bits).
|
||||||
pub fn from_unix_stamp_with_u24_days(
|
pub fn from_unix_stamp_with_u24_days(
|
||||||
unix_stamp: &UnixTime,
|
unix_stamp: &UnixTime,
|
||||||
submillis_prec: SubmillisPrecision,
|
submillis_prec: SubmillisPrecision,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Long24Bits, submillis_prec)
|
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Long24Bits, submillis_prec)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -843,7 +856,7 @@ impl CdsTime<DaysLen24Bits> {
|
|||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
pub fn from_dt_with_u24_days_us_precision(
|
pub fn from_dt_with_u24_days_us_precision(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Long24Bits)
|
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Long24Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -851,7 +864,7 @@ impl CdsTime<DaysLen24Bits> {
|
|||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
pub fn from_dt_with_u24_days_ps_precision(
|
pub fn from_dt_with_u24_days_ps_precision(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Long24Bits)
|
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Long24Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -903,13 +916,11 @@ impl CdsTime<DaysLen16Bits> {
|
|||||||
|
|
||||||
/// Create a provider from a [`chrono::DateTime<Utc>`] struct.
|
/// Create a provider from a [`chrono::DateTime<Utc>`] struct.
|
||||||
///
|
///
|
||||||
/// This function will return a [TimestampError::DateBeforeCcsdsEpoch] or a
|
/// This function will return a [CdsError::DateBeforeCcsdsEpoch] if the time is before the
|
||||||
/// [TimestampError::Cds] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or
|
/// CCSDS epoch (01-01-1958 00:00:00) or the CCSDS days value exceeds the allowed bit width
|
||||||
/// the CCSDS days value exceeds the allowed bit width (16 bits).
|
/// (16 bits).
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
pub fn from_dt_with_u16_days(
|
pub fn from_dt_with_u16_days(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, CdsError> {
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
|
||||||
) -> Result<Self, TimestampError> {
|
|
||||||
Self::from_dt_generic(dt, LengthOfDaySegment::Short16Bits)
|
Self::from_dt_generic(dt, LengthOfDaySegment::Short16Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -923,13 +934,13 @@ impl CdsTime<DaysLen16Bits> {
|
|||||||
///
|
///
|
||||||
/// ## Errors
|
/// ## Errors
|
||||||
///
|
///
|
||||||
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
/// This function will return [CdsError::DateBeforeCcsdsEpoch] if the time is before the CCSDS
|
||||||
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
/// epoch (1958-01-01T00:00:00+00:00) or the CCSDS days value exceeds the allowed bit width
|
||||||
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
/// (24 bits).
|
||||||
pub fn from_unix_stamp_with_u16_days(
|
pub fn from_unix_stamp_with_u16_days(
|
||||||
unix_stamp: &UnixTime,
|
unix_stamp: &UnixTime,
|
||||||
submillis_prec: SubmillisPrecision,
|
submillis_prec: SubmillisPrecision,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Short16Bits, submillis_prec)
|
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Short16Bits, submillis_prec)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -937,7 +948,7 @@ impl CdsTime<DaysLen16Bits> {
|
|||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
pub fn from_dt_with_u16_days_us_precision(
|
pub fn from_dt_with_u16_days_us_precision(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Short16Bits)
|
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Short16Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -945,7 +956,7 @@ impl CdsTime<DaysLen16Bits> {
|
|||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
pub fn from_dt_with_u16_days_ps_precision(
|
pub fn from_dt_with_u16_days_ps_precision(
|
||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CdsError> {
|
||||||
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Short16Bits)
|
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Short16Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1157,7 +1168,7 @@ impl AddAssign<Duration> for CdsTime<DaysLen24Bits> {
|
|||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen16Bits> {
|
impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen16Bits> {
|
||||||
type Error = TimestampError;
|
type Error = CdsError;
|
||||||
|
|
||||||
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
|
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
|
||||||
let conversion = ConversionFromChronoDatetime::new(&dt)?;
|
let conversion = ConversionFromChronoDatetime::new(&dt)?;
|
||||||
@ -1167,8 +1178,7 @@ impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen16Bits> {
|
|||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen24Bits> {
|
impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen24Bits> {
|
||||||
type Error = TimestampError;
|
type Error = CdsError;
|
||||||
|
|
||||||
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
|
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
|
||||||
let conversion = ConversionFromChronoDatetime::new(&dt)?;
|
let conversion = ConversionFromChronoDatetime::new(&dt)?;
|
||||||
Self::generic_from_conversion(LengthOfDaySegment::Long24Bits, conversion)
|
Self::generic_from_conversion(LengthOfDaySegment::Long24Bits, conversion)
|
||||||
@ -1980,12 +1990,12 @@ mod tests {
|
|||||||
panic!("creation should not succeed")
|
panic!("creation should not succeed")
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let TimestampError::Cds(CdsError::InvalidCcsdsDays(days)) = e {
|
if let CdsError::InvalidCcsdsDays(days) = e {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
days,
|
days,
|
||||||
unix_to_ccsds_days(invalid_unix_secs / SECONDS_PER_DAY as i64)
|
unix_to_ccsds_days(invalid_unix_secs / SECONDS_PER_DAY as i64)
|
||||||
);
|
);
|
||||||
assert_eq!(e.to_string(), "cds error: invalid ccsds days 69919");
|
assert_eq!(e.to_string(), "invalid ccsds days 69919");
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected error {}", e)
|
panic!("unexpected error {}", e)
|
||||||
}
|
}
|
||||||
@ -2007,8 +2017,8 @@ mod tests {
|
|||||||
panic!("creation should not succeed")
|
panic!("creation should not succeed")
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let TimestampError::DateBeforeCcsdsEpoch(dt) = e {
|
if let CdsError::DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError(unix_dt)) = e {
|
||||||
let dt = dt.chrono_date_time();
|
let dt = unix_dt.chrono_date_time();
|
||||||
if let chrono::LocalResult::Single(dt) = dt {
|
if let chrono::LocalResult::Single(dt) = dt {
|
||||||
assert_eq!(dt.year(), 1957);
|
assert_eq!(dt.year(), 1957);
|
||||||
assert_eq!(dt.month(), 12);
|
assert_eq!(dt.month(), 12);
|
||||||
@ -2238,7 +2248,9 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let time_provider = CdsTime::from_dt_with_u24_days(&datetime_utc);
|
let time_provider = CdsTime::from_dt_with_u24_days(&datetime_utc);
|
||||||
assert!(time_provider.is_err());
|
assert!(time_provider.is_err());
|
||||||
if let TimestampError::DateBeforeCcsdsEpoch(dt) = time_provider.unwrap_err() {
|
if let CdsError::DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError(dt)) =
|
||||||
|
time_provider.unwrap_err()
|
||||||
|
{
|
||||||
assert_eq!(dt, datetime_utc.into());
|
assert_eq!(dt, datetime_utc.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@ use crate::ByteConversionError;
|
|||||||
use super::StdTimestampError;
|
use super::StdTimestampError;
|
||||||
use super::{
|
use super::{
|
||||||
ccsds_epoch_to_unix_epoch, ccsds_time_code_from_p_field, unix_epoch_to_ccsds_epoch,
|
ccsds_epoch_to_unix_epoch, ccsds_time_code_from_p_field, unix_epoch_to_ccsds_epoch,
|
||||||
CcsdsTimeCode, CcsdsTimeProvider, TimeReader, TimeWriter, TimestampError, UnixTime,
|
CcsdsTimeCode, CcsdsTimeProvider, DateBeforeCcsdsEpochError, TimeReader, TimeWriter,
|
||||||
|
TimestampError, UnixTime,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@ -116,6 +117,7 @@ pub enum CucError {
|
|||||||
value: u64,
|
value: u64,
|
||||||
},
|
},
|
||||||
LeapSecondCorrectionError,
|
LeapSecondCorrectionError,
|
||||||
|
DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CucError {
|
impl Display for CucError {
|
||||||
@ -136,12 +138,28 @@ impl Display for CucError {
|
|||||||
CucError::LeapSecondCorrectionError => {
|
CucError::LeapSecondCorrectionError => {
|
||||||
write!(f, "error while correcting for leap seconds")
|
write!(f, "error while correcting for leap seconds")
|
||||||
}
|
}
|
||||||
|
CucError::DateBeforeCcsdsEpoch(e) => {
|
||||||
|
write!(f, "date before ccsds epoch: {e}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl Error for CucError {}
|
impl Error for CucError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
CucError::DateBeforeCcsdsEpoch(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DateBeforeCcsdsEpochError> for CucError {
|
||||||
|
fn from(e: DateBeforeCcsdsEpochError) -> Self {
|
||||||
|
Self::DateBeforeCcsdsEpoch(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tuple object where the first value is the width of the counter and the second value
|
/// Tuple object where the first value is the width of the counter and the second value
|
||||||
/// is the counter value.
|
/// is the counter value.
|
||||||
@ -395,20 +413,19 @@ impl CucTime {
|
|||||||
dt: &chrono::DateTime<chrono::Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
res: FractionalResolution,
|
res: FractionalResolution,
|
||||||
leap_seconds: u32,
|
leap_seconds: u32,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CucError> {
|
||||||
// Year before CCSDS epoch is invalid.
|
// Year before CCSDS epoch is invalid.
|
||||||
if dt.year() < 1958 {
|
if dt.year() < 1958 {
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTime::from(*dt)));
|
return Err(DateBeforeCcsdsEpochError(UnixTime::from(*dt)).into());
|
||||||
}
|
}
|
||||||
let counter = dt
|
let counter = dt
|
||||||
.timestamp()
|
.timestamp()
|
||||||
.checked_add(i64::from(leap_seconds))
|
.checked_add(i64::from(leap_seconds))
|
||||||
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
|
.ok_or(CucError::LeapSecondCorrectionError)?;
|
||||||
Self::new_generic(
|
Self::new_generic(
|
||||||
WidthCounterPair(4, counter 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),
|
||||||
)
|
)
|
||||||
.map_err(|e| e.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a CUC timestamp from a UNIX timestamp with a width of 4. This width is able
|
/// Generates a CUC timestamp from a UNIX timestamp with a width of 4. This width is able
|
||||||
@ -417,11 +434,11 @@ impl CucTime {
|
|||||||
unix_stamp: &UnixTime,
|
unix_stamp: &UnixTime,
|
||||||
res: FractionalResolution,
|
res: FractionalResolution,
|
||||||
leap_seconds: u32,
|
leap_seconds: u32,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, CucError> {
|
||||||
let counter = unix_epoch_to_ccsds_epoch(unix_stamp.secs);
|
let counter = unix_epoch_to_ccsds_epoch(unix_stamp.secs);
|
||||||
// Negative CCSDS epoch is invalid.
|
// Negative CCSDS epoch is invalid.
|
||||||
if counter < 0 {
|
if counter < 0 {
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(*unix_stamp));
|
return Err(DateBeforeCcsdsEpochError(*unix_stamp).into());
|
||||||
}
|
}
|
||||||
// We already excluded negative values, so the conversion to u64 should always work.
|
// We already excluded negative values, so the conversion to u64 should always work.
|
||||||
let mut counter = u32::try_from(counter).map_err(|_| CucError::InvalidCounter {
|
let mut counter = u32::try_from(counter).map_err(|_| CucError::InvalidCounter {
|
||||||
@ -430,10 +447,10 @@ impl CucTime {
|
|||||||
})?;
|
})?;
|
||||||
counter = counter
|
counter = counter
|
||||||
.checked_add(leap_seconds)
|
.checked_add(leap_seconds)
|
||||||
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
|
.ok_or(CucError::LeapSecondCorrectionError)?;
|
||||||
let fractions =
|
let fractions =
|
||||||
fractional_part_from_subsec_ns(res, unix_stamp.subsec_millis() as u64 * 10_u64.pow(6));
|
fractional_part_from_subsec_ns(res, unix_stamp.subsec_millis() as u64 * 10_u64.pow(6));
|
||||||
Self::new_generic(WidthCounterPair(4, counter as u32), fractions).map_err(|e| e.into())
|
Self::new_generic(WidthCounterPair(4, counter as u32), fractions)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Most generic constructor which allows full configurability for the counter and for the
|
/// Most generic constructor which allows full configurability for the counter and for the
|
||||||
|
@ -63,6 +63,19 @@ pub fn ccsds_time_code_from_p_field(pfield: u8) -> Result<CcsdsTimeCode, u8> {
|
|||||||
CcsdsTimeCode::try_from(raw_bits).map_err(|_| raw_bits)
|
CcsdsTimeCode::try_from(raw_bits).map_err(|_| raw_bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct DateBeforeCcsdsEpochError(UnixTime);
|
||||||
|
|
||||||
|
impl Display for DateBeforeCcsdsEpochError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "date before ccsds epoch: {:?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl Error for DateBeforeCcsdsEpochError {}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -71,7 +84,6 @@ pub enum TimestampError {
|
|||||||
ByteConversion(ByteConversionError),
|
ByteConversion(ByteConversionError),
|
||||||
Cds(cds::CdsError),
|
Cds(cds::CdsError),
|
||||||
Cuc(cuc::CucError),
|
Cuc(cuc::CucError),
|
||||||
DateBeforeCcsdsEpoch(UnixTime),
|
|
||||||
CustomEpochNotSupported,
|
CustomEpochNotSupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,9 +105,6 @@ impl Display for TimestampError {
|
|||||||
TimestampError::ByteConversion(e) => {
|
TimestampError::ByteConversion(e) => {
|
||||||
write!(f, "time stamp: {e}")
|
write!(f, "time stamp: {e}")
|
||||||
}
|
}
|
||||||
TimestampError::DateBeforeCcsdsEpoch(e) => {
|
|
||||||
write!(f, "datetime with date before ccsds epoch: {e:?}")
|
|
||||||
}
|
|
||||||
TimestampError::CustomEpochNotSupported => {
|
TimestampError::CustomEpochNotSupported => {
|
||||||
write!(f, "custom epochs are not supported")
|
write!(f, "custom epochs are not supported")
|
||||||
}
|
}
|
||||||
@ -114,6 +123,7 @@ impl Error for TimestampError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<cds::CdsError> for TimestampError {
|
impl From<cds::CdsError> for TimestampError {
|
||||||
fn from(e: cds::CdsError) -> Self {
|
fn from(e: cds::CdsError) -> Self {
|
||||||
TimestampError::Cds(e)
|
TimestampError::Cds(e)
|
||||||
|
Loading…
Reference in New Issue
Block a user