base line cuc impl done
This commit is contained in:
parent
f7f500ea48
commit
c4f8eee8da
340
src/time.rs
340
src/time.rs
@ -7,15 +7,14 @@ use core::fmt::{Display, Formatter};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use num_traits::float::FloatCore;
|
||||
|
||||
use crate::time::cuc::CucError;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
#[cfg(feature = "std")]
|
||||
use std::time::{SystemTime, SystemTimeError};
|
||||
|
||||
use crate::time::cds::LengthOfDaySegment;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const DAYS_CCSDS_TO_UNIX: i32 = -4383;
|
||||
pub const SECONDS_PER_DAY: u32 = 86400;
|
||||
|
||||
@ -51,36 +50,6 @@ pub fn ccsds_time_code_from_p_field(pfield: u8) -> Result<CcsdsTimeCodes, u8> {
|
||||
CcsdsTimeCodes::try_from(raw_bits).map_err(|_| raw_bits)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum CdsError {
|
||||
/// CCSDS days value exceeds maximum allowed size or is negative
|
||||
InvalidCcsdsDays(i64),
|
||||
/// 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.
|
||||
InvalidCtorForDaysOfLenInPreamble(LengthOfDaySegment),
|
||||
}
|
||||
|
||||
impl Display for CdsError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
CdsError::InvalidCcsdsDays(days) => {
|
||||
write!(f, "invalid ccsds days {}", days)
|
||||
}
|
||||
CdsError::InvalidCtorForDaysOfLenInPreamble(length_of_day) => {
|
||||
write!(
|
||||
f,
|
||||
"wrong constructor for length of day {:?} detected in preamble",
|
||||
length_of_day
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for CdsError {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum TimestampError {
|
||||
@ -88,13 +57,20 @@ pub enum TimestampError {
|
||||
/// value is the found raw value
|
||||
InvalidTimeCode(CcsdsTimeCodes, u8),
|
||||
ByteConversionError(ByteConversionError),
|
||||
CdsError(CdsError),
|
||||
CdsError(cds::CdsError),
|
||||
CucError(cuc::CucError),
|
||||
CustomEpochNotSupported,
|
||||
}
|
||||
|
||||
impl From<CdsError> for TimestampError {
|
||||
fn from(v: CdsError) -> Self {
|
||||
TimestampError::CdsError(v)
|
||||
impl From<cds::CdsError> for TimestampError {
|
||||
fn from(e: cds::CdsError) -> Self {
|
||||
TimestampError::CdsError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cuc::CucError> for TimestampError {
|
||||
fn from(e: CucError) -> Self {
|
||||
TimestampError::CucError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +109,9 @@ impl Display for TimestampError {
|
||||
TimestampError::CdsError(e) => {
|
||||
write!(f, "cds error {}", e)
|
||||
}
|
||||
TimestampError::CucError(e) => {
|
||||
write!(f, "cuc error {}", e)
|
||||
}
|
||||
TimestampError::ByteConversionError(e) => {
|
||||
write!(f, "byte conversion error {}", e)
|
||||
}
|
||||
@ -149,6 +128,7 @@ impl Error for TimestampError {
|
||||
match self {
|
||||
TimestampError::ByteConversionError(e) => Some(e),
|
||||
TimestampError::CdsError(e) => Some(e),
|
||||
TimestampError::CucError(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -270,6 +250,35 @@ pub mod cds {
|
||||
Reserved,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum CdsError {
|
||||
/// CCSDS days value exceeds maximum allowed size or is negative
|
||||
InvalidCcsdsDays(i64),
|
||||
/// 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.
|
||||
InvalidCtorForDaysOfLenInPreamble(LengthOfDaySegment),
|
||||
}
|
||||
|
||||
impl Display for CdsError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
CdsError::InvalidCcsdsDays(days) => {
|
||||
write!(f, "invalid ccsds days {}", days)
|
||||
}
|
||||
CdsError::InvalidCtorForDaysOfLenInPreamble(length_of_day) => {
|
||||
write!(
|
||||
f,
|
||||
"wrong constructor for length of day {:?} detected in preamble",
|
||||
length_of_day
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for CdsError {}
|
||||
pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment {
|
||||
if (pfield >> 2) & 0b1 == 1 {
|
||||
return LengthOfDaySegment::Long24Bits;
|
||||
@ -816,8 +825,41 @@ pub mod cds {
|
||||
}
|
||||
|
||||
pub mod cuc {
|
||||
use core::fmt::Debug;
|
||||
use super::*;
|
||||
use core::fmt::Debug;
|
||||
|
||||
const MIN_CUC_LEN: usize = 2;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum CucError {
|
||||
InvalidCounterWidth(u8),
|
||||
InvalidFractionWidth(u8),
|
||||
InvalidCounter(u8, u64),
|
||||
InvalidFractions(u8, u64),
|
||||
}
|
||||
|
||||
impl Display for CucError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
CucError::InvalidCounterWidth(w) => {
|
||||
write!(f, "invalid cuc counter byte width {}", w)
|
||||
}
|
||||
CucError::InvalidFractionWidth(w) => {
|
||||
write!(f, "invalid cuc fractional part byte width {}", w)
|
||||
}
|
||||
CucError::InvalidCounter(w, c) => {
|
||||
write!(f, "invalid cuc counter {} for width {}", c, w)
|
||||
}
|
||||
CucError::InvalidFractions(w, c) => {
|
||||
write!(f, "invalid cuc fractional part {} for width {}", c, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for CucError {}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
@ -833,7 +875,7 @@ pub mod cuc {
|
||||
pub struct TimeProviderCcsdsEpoch {
|
||||
pfield: u8,
|
||||
counter: WidthCounterPair,
|
||||
fractions: Option<WidthCounterPair>
|
||||
fractions: Option<WidthCounterPair>,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -841,53 +883,183 @@ pub mod cuc {
|
||||
if ((pfield >> 7) & 0b1) == 1 {
|
||||
return 2;
|
||||
}
|
||||
return 1
|
||||
1
|
||||
}
|
||||
|
||||
impl TimeProviderCcsdsEpoch {
|
||||
fn build_p_field(counter_width: u8, fractions_width: Option<u8>) -> u8 {
|
||||
let mut pfield = (CcsdsTimeCodes::CucCcsdsEpoch as u8) << 4;
|
||||
if counter_width < 1 || counter_width > 4 {
|
||||
if !(1..=4).contains(&counter_width) {
|
||||
// Okay to panic here, this function is private and all input values should
|
||||
// have been sanitized
|
||||
panic!("invalid counter width {} for cuc timestamp", counter_width);
|
||||
}
|
||||
pfield |= counter_width << 3;
|
||||
pfield |= (counter_width - 1) << 3;
|
||||
if let Some(fractions_width) = fractions_width {
|
||||
if fractions_width < 1 || fractions_width > 3 {
|
||||
panic!("invalid fractions width {} for cuc timestamp", fractions_width);
|
||||
if !(1..=3).contains(&fractions_width) {
|
||||
// Okay to panic here, this function is private and all input values should
|
||||
// have been sanitized
|
||||
panic!(
|
||||
"invalid fractions width {} for cuc timestamp",
|
||||
fractions_width
|
||||
);
|
||||
}
|
||||
pfield |= fractions_width;
|
||||
}
|
||||
pfield
|
||||
}
|
||||
|
||||
|
||||
pub fn len_packed(&self) -> usize {
|
||||
// TODO: Implement
|
||||
todo!()
|
||||
Self::len_packed_from_pfield(self.pfield)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len_cntr_from_pfield(pfield: u8) -> u8 {
|
||||
((pfield >> 2) & 0b11) + 1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len_fractions_from_pfield(pfield: u8) -> u8 {
|
||||
pfield & 0b11
|
||||
}
|
||||
|
||||
/// This returns the length of the individual components of the CUC timestamp in addition
|
||||
/// to the total size.
|
||||
///
|
||||
/// This function will return a tuple where the first value is the byte width of the
|
||||
/// counter, the second value is the byte width of the fractional part, and the third
|
||||
/// components is the total size.
|
||||
pub fn len_components_and_total_from_pfield(pfield: u8) -> (u8, u8, usize) {
|
||||
let base_len: usize = 1;
|
||||
let cntr_len = Self::len_cntr_from_pfield(pfield);
|
||||
let fractions_len = Self::len_fractions_from_pfield(pfield);
|
||||
(
|
||||
cntr_len,
|
||||
fractions_len,
|
||||
base_len + cntr_len as usize + fractions_len as usize,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn len_packed_from_pfield(pfield: u8) -> usize {
|
||||
let mut base_len: usize = 1;
|
||||
base_len += Self::len_cntr_from_pfield(pfield) as usize;
|
||||
base_len += Self::len_fractions_from_pfield(pfield) as usize;
|
||||
base_len
|
||||
}
|
||||
|
||||
/// Verifies the raw width parameter and returns the actual length, which is the raw
|
||||
/// value plus 1.
|
||||
fn verify_counter_width(width: u8) -> Result<(), CucError> {
|
||||
if width == 0 || width > 4 {
|
||||
return Err(CucError::InvalidCounterWidth(width));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_fractions_width(width: u8) -> Result<(), CucError> {
|
||||
if width > 3 {
|
||||
return Err(CucError::InvalidFractionWidth(width));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeProviderCcsdsEpoch {
|
||||
pub fn new_default(counter: u32) -> Self {
|
||||
Self::new(WidthCounterPair(4, counter), None)
|
||||
// These values are definitely valid, so it is okay to unwrap here.
|
||||
Self::new(WidthCounterPair(4, counter), None).unwrap()
|
||||
}
|
||||
pub fn new_u16_counter(counter: u16) -> Self {
|
||||
// These values are definitely valid, so it is okay to unwrap here.
|
||||
Self::new(WidthCounterPair(2, counter as u32), None).unwrap()
|
||||
}
|
||||
|
||||
pub fn new(counter: WidthCounterPair, fractions: Option<WidthCounterPair>) -> Self {
|
||||
let fractions_width = match fractions {
|
||||
None => 0,
|
||||
Some(fractions) => fractions.0
|
||||
};
|
||||
Self {
|
||||
pfield: Self::build_p_field(counter.0.into(), fractions_width.into()),
|
||||
counter,
|
||||
fractions
|
||||
pub fn new(
|
||||
counter: WidthCounterPair,
|
||||
fractions: Option<WidthCounterPair>,
|
||||
) -> Result<Self, CucError> {
|
||||
Self::verify_counter_width(counter.0)?;
|
||||
if counter.1 > 2u32.pow(counter.0 as u32 * 8) - 1 {
|
||||
return Err(CucError::InvalidCounter(counter.0, counter.1 as u64));
|
||||
}
|
||||
if let Some(fractions) = fractions {
|
||||
Self::verify_fractions_width(fractions.0)?;
|
||||
if fractions.1 > 2u32.pow((fractions.0 as u32) * 8) - 1 {
|
||||
return Err(CucError::InvalidFractions(fractions.0, fractions.1 as u64));
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
pfield: Self::build_p_field(counter.0, fractions.map(|v| v.0)),
|
||||
counter,
|
||||
fractions,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeReader for TimeProviderCcsdsEpoch {
|
||||
fn from_bytes(_buf: &[u8]) -> Result<Self, TimestampError> where Self: Sized {
|
||||
todo!()
|
||||
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if buf.len() < MIN_CUC_LEN {
|
||||
return Err(TimestampError::ByteConversionError(
|
||||
ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||
expected: MIN_CUC_LEN,
|
||||
found: buf.len(),
|
||||
}),
|
||||
));
|
||||
}
|
||||
let (cntr_len, fractions_len, total_len) =
|
||||
Self::len_components_and_total_from_pfield(buf[0]);
|
||||
if buf.len() < total_len {
|
||||
return Err(TimestampError::ByteConversionError(
|
||||
ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||
expected: total_len,
|
||||
found: buf.len(),
|
||||
}),
|
||||
));
|
||||
}
|
||||
let mut current_idx = 1;
|
||||
let counter =
|
||||
match cntr_len {
|
||||
1 => buf[current_idx] as u32,
|
||||
2 => u16::from_be_bytes(buf[current_idx..current_idx + 2].try_into().unwrap())
|
||||
as u32,
|
||||
3 => {
|
||||
let mut tmp_buf: [u8; 4] = [0; 4];
|
||||
tmp_buf[1..4].copy_from_slice(&buf[current_idx..current_idx + 3]);
|
||||
u32::from_be_bytes(tmp_buf) as u32
|
||||
}
|
||||
4 => u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap())
|
||||
as u32,
|
||||
_ => panic!("unreachable match arm"),
|
||||
};
|
||||
current_idx += cntr_len as usize;
|
||||
let mut fractions = None;
|
||||
if fractions_len > 0 {
|
||||
match fractions_len {
|
||||
1 => fractions = Some(WidthCounterPair(fractions_len, buf[current_idx] as u32)),
|
||||
2 => {
|
||||
fractions = Some(WidthCounterPair(
|
||||
fractions_len,
|
||||
u16::from_be_bytes(
|
||||
buf[current_idx..current_idx + 2].try_into().unwrap(),
|
||||
) as u32,
|
||||
))
|
||||
}
|
||||
3 => {
|
||||
let mut tmp_buf: [u8; 4] = [0; 4];
|
||||
tmp_buf[1..4].copy_from_slice(&buf[current_idx..current_idx + 3]);
|
||||
fractions = Some(WidthCounterPair(
|
||||
fractions_len,
|
||||
u32::from_be_bytes(tmp_buf) as u32,
|
||||
))
|
||||
}
|
||||
_ => panic!("unreachable match arm"),
|
||||
}
|
||||
}
|
||||
let provider = Self::new(WidthCounterPair(cntr_len, counter), fractions)?;
|
||||
Ok(provider)
|
||||
}
|
||||
}
|
||||
|
||||
@ -895,45 +1067,52 @@ pub mod cuc {
|
||||
fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<usize, TimestampError> {
|
||||
// Cross check the sizes of the counters against byte widths in the ctor
|
||||
if bytes.len() < self.len_packed() {
|
||||
return Err(TimestampError::ByteConversionError(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||
found: bytes.len(),
|
||||
expected: self.len_packed()
|
||||
})))
|
||||
return Err(TimestampError::ByteConversionError(
|
||||
ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||
found: bytes.len(),
|
||||
expected: self.len_packed(),
|
||||
}),
|
||||
));
|
||||
}
|
||||
bytes[0] = self.pfield;
|
||||
let mut current_idx: usize = 1;
|
||||
match self.counter.0 {
|
||||
1 => {
|
||||
bytes[1] = self.counter.1 as u8;
|
||||
},
|
||||
bytes[current_idx] = self.counter.1 as u8;
|
||||
}
|
||||
2 => {
|
||||
bytes[1..3].copy_from_slice(&(self.counter.1 as u16).to_be_bytes());
|
||||
},
|
||||
bytes[current_idx..current_idx + 2]
|
||||
.copy_from_slice(&(self.counter.1 as u16).to_be_bytes());
|
||||
}
|
||||
3 => {
|
||||
bytes[1..4].copy_from_slice(&self.counter.1.to_be_bytes()[1..4]);
|
||||
},
|
||||
bytes[current_idx..current_idx + 3]
|
||||
.copy_from_slice(&self.counter.1.to_be_bytes()[1..4]);
|
||||
}
|
||||
4 => {
|
||||
bytes[1..5].copy_from_slice(&self.counter.1.to_be_bytes());
|
||||
},
|
||||
bytes[current_idx..current_idx + 4]
|
||||
.copy_from_slice(&self.counter.1.to_be_bytes());
|
||||
}
|
||||
// Should never happen
|
||||
_ => panic!("invalid counter width value")
|
||||
_ => panic!("invalid counter width value"),
|
||||
}
|
||||
current_idx += self.counter.0 as usize;
|
||||
if let Some(fractions) = self.fractions {
|
||||
match fractions.0 {
|
||||
1 => bytes[current_idx] = fractions.1 as u8,
|
||||
2 => bytes[current_idx..current_idx + 2].copy_from_slice(&(fractions.1 as u16).to_be_bytes()),
|
||||
3 => bytes[current_idx..current_idx + 3].copy_from_slice(&fractions.1.to_be_bytes()[1..4]),
|
||||
2 => bytes[current_idx..current_idx + 2]
|
||||
.copy_from_slice(&(fractions.1 as u16).to_be_bytes()),
|
||||
3 => bytes[current_idx..current_idx + 3]
|
||||
.copy_from_slice(&fractions.1.to_be_bytes()[1..4]),
|
||||
// Should also never happen
|
||||
_ => panic!("invalid fractions value")
|
||||
_ => panic!("invalid fractions value"),
|
||||
}
|
||||
current_idx += fractions.0 as usize;
|
||||
}
|
||||
Ok(current_idx)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Module to generate the ASCII timecodes specified in
|
||||
/// [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) section 3.5 .
|
||||
/// See [chrono::DateTime::format] for a usage example of the generated
|
||||
@ -1006,7 +1185,7 @@ pub mod ascii {
|
||||
mod tests {
|
||||
use super::cds::TimeProvider;
|
||||
use super::*;
|
||||
use crate::time::cds::{DaysLen16Bits, DaysLen24Bits, SubmillisPrecision};
|
||||
use crate::time::cds::{DaysLen16Bits, DaysLen24Bits, LengthOfDaySegment, SubmillisPrecision};
|
||||
use crate::time::TimestampError::{ByteConversionError, InvalidTimeCode};
|
||||
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
|
||||
use alloc::format;
|
||||
@ -1163,8 +1342,9 @@ mod tests {
|
||||
let faulty_ctor = TimeProvider::<DaysLen16Bits>::from_bytes(&buf);
|
||||
assert!(faulty_ctor.is_err());
|
||||
let error = faulty_ctor.unwrap_err();
|
||||
if let TimestampError::CdsError(CdsError::InvalidCtorForDaysOfLenInPreamble(len_of_day)) =
|
||||
error
|
||||
if let TimestampError::CdsError(cds::CdsError::InvalidCtorForDaysOfLenInPreamble(
|
||||
len_of_day,
|
||||
)) = error
|
||||
{
|
||||
assert_eq!(len_of_day, LengthOfDaySegment::Long24Bits);
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user