base line cuc impl done

This commit is contained in:
Robin Müller 2022-12-08 14:59:12 +01:00
parent f7f500ea48
commit c4f8eee8da
No known key found for this signature in database
GPG Key ID: 71B58F8A3CDFA9AC

View File

@ -7,15 +7,14 @@ use core::fmt::{Display, Formatter};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use num_traits::float::FloatCore; use num_traits::float::FloatCore;
use crate::time::cuc::CucError;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error::Error; use std::error::Error;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::time::{SystemTime, SystemTimeError}; 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 DAYS_CCSDS_TO_UNIX: i32 = -4383;
pub const SECONDS_PER_DAY: u32 = 86400; 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) 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)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TimestampError { pub enum TimestampError {
@ -88,13 +57,20 @@ pub enum TimestampError {
/// value is the found raw value /// value is the found raw value
InvalidTimeCode(CcsdsTimeCodes, u8), InvalidTimeCode(CcsdsTimeCodes, u8),
ByteConversionError(ByteConversionError), ByteConversionError(ByteConversionError),
CdsError(CdsError), CdsError(cds::CdsError),
CucError(cuc::CucError),
CustomEpochNotSupported, CustomEpochNotSupported,
} }
impl From<CdsError> for TimestampError { impl From<cds::CdsError> for TimestampError {
fn from(v: CdsError) -> Self { fn from(e: cds::CdsError) -> Self {
TimestampError::CdsError(v) 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) => { TimestampError::CdsError(e) => {
write!(f, "cds error {}", e) write!(f, "cds error {}", e)
} }
TimestampError::CucError(e) => {
write!(f, "cuc error {}", e)
}
TimestampError::ByteConversionError(e) => { TimestampError::ByteConversionError(e) => {
write!(f, "byte conversion error {}", e) write!(f, "byte conversion error {}", e)
} }
@ -149,6 +128,7 @@ impl Error for TimestampError {
match self { match self {
TimestampError::ByteConversionError(e) => Some(e), TimestampError::ByteConversionError(e) => Some(e),
TimestampError::CdsError(e) => Some(e), TimestampError::CdsError(e) => Some(e),
TimestampError::CucError(e) => Some(e),
_ => None, _ => None,
} }
} }
@ -270,6 +250,35 @@ pub mod cds {
Reserved, 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 { 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;
@ -816,8 +825,41 @@ pub mod cds {
} }
pub mod cuc { pub mod cuc {
use core::fmt::Debug;
use super::*; 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)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -833,7 +875,7 @@ pub mod cuc {
pub struct TimeProviderCcsdsEpoch { pub struct TimeProviderCcsdsEpoch {
pfield: u8, pfield: u8,
counter: WidthCounterPair, counter: WidthCounterPair,
fractions: Option<WidthCounterPair> fractions: Option<WidthCounterPair>,
} }
#[inline] #[inline]
@ -841,53 +883,183 @@ pub mod cuc {
if ((pfield >> 7) & 0b1) == 1 { if ((pfield >> 7) & 0b1) == 1 {
return 2; return 2;
} }
return 1 1
} }
impl TimeProviderCcsdsEpoch { impl TimeProviderCcsdsEpoch {
fn build_p_field(counter_width: u8, fractions_width: Option<u8>) -> u8 { fn build_p_field(counter_width: u8, fractions_width: Option<u8>) -> u8 {
let mut pfield = (CcsdsTimeCodes::CucCcsdsEpoch as u8) << 4; 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); 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 let Some(fractions_width) = fractions_width {
if fractions_width < 1 || fractions_width > 3 { if !(1..=3).contains(&fractions_width) {
panic!("invalid fractions width {} for cuc timestamp", 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 |= fractions_width;
} }
pfield pfield
} }
pub fn len_packed(&self) -> usize { pub fn len_packed(&self) -> usize {
// TODO: Implement Self::len_packed_from_pfield(self.pfield)
todo!() }
#[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 { impl TimeProviderCcsdsEpoch {
pub fn new_default(counter: u32) -> Self { 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 { pub fn new(
let fractions_width = match fractions { counter: WidthCounterPair,
None => 0, fractions: Option<WidthCounterPair>,
Some(fractions) => fractions.0 ) -> Result<Self, CucError> {
}; Self::verify_counter_width(counter.0)?;
Self { if counter.1 > 2u32.pow(counter.0 as u32 * 8) - 1 {
pfield: Self::build_p_field(counter.0.into(), fractions_width.into()), return Err(CucError::InvalidCounter(counter.0, counter.1 as u64));
counter,
fractions
} }
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 { impl TimeReader for TimeProviderCcsdsEpoch {
fn from_bytes(_buf: &[u8]) -> Result<Self, TimestampError> where Self: Sized { fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError>
todo!() 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> { fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<usize, TimestampError> {
// Cross check the sizes of the counters against byte widths in the ctor // Cross check the sizes of the counters against byte widths in the ctor
if bytes.len() < self.len_packed() { if bytes.len() < self.len_packed() {
return Err(TimestampError::ByteConversionError(ByteConversionError::ToSliceTooSmall(SizeMissmatch { return Err(TimestampError::ByteConversionError(
found: bytes.len(), ByteConversionError::ToSliceTooSmall(SizeMissmatch {
expected: self.len_packed() found: bytes.len(),
}))) expected: self.len_packed(),
}),
));
} }
bytes[0] = self.pfield; bytes[0] = self.pfield;
let mut current_idx: usize = 1; let mut current_idx: usize = 1;
match self.counter.0 { match self.counter.0 {
1 => { 1 => {
bytes[1] = self.counter.1 as u8; bytes[current_idx] = self.counter.1 as u8;
}, }
2 => { 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 => { 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 => { 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 // Should never happen
_ => panic!("invalid counter width value") _ => panic!("invalid counter width value"),
} }
current_idx += self.counter.0 as usize; current_idx += self.counter.0 as usize;
if let Some(fractions) = self.fractions { if let Some(fractions) = self.fractions {
match fractions.0 { match fractions.0 {
1 => bytes[current_idx] = fractions.1 as u8, 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()), 2 => bytes[current_idx..current_idx + 2]
3 => bytes[current_idx..current_idx + 3].copy_from_slice(&fractions.1.to_be_bytes()[1..4]), .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 // Should also never happen
_ => panic!("invalid fractions value") _ => panic!("invalid fractions value"),
} }
current_idx += fractions.0 as usize; current_idx += fractions.0 as usize;
} }
Ok(current_idx) Ok(current_idx)
} }
} }
} }
/// Module to generate the ASCII timecodes specified in /// Module to generate the ASCII timecodes specified in
/// [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) section 3.5 . /// [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 /// See [chrono::DateTime::format] for a usage example of the generated
@ -1006,7 +1185,7 @@ pub mod ascii {
mod tests { mod tests {
use super::cds::TimeProvider; use super::cds::TimeProvider;
use super::*; 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::time::TimestampError::{ByteConversionError, InvalidTimeCode};
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall}; use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
use alloc::format; use alloc::format;
@ -1163,8 +1342,9 @@ mod tests {
let faulty_ctor = TimeProvider::<DaysLen16Bits>::from_bytes(&buf); let faulty_ctor = TimeProvider::<DaysLen16Bits>::from_bytes(&buf);
assert!(faulty_ctor.is_err()); assert!(faulty_ctor.is_err());
let error = faulty_ctor.unwrap_err(); let error = faulty_ctor.unwrap_err();
if let TimestampError::CdsError(CdsError::InvalidCtorForDaysOfLenInPreamble(len_of_day)) = if let TimestampError::CdsError(cds::CdsError::InvalidCtorForDaysOfLenInPreamble(
error len_of_day,
)) = error
{ {
assert_eq!(len_of_day, LengthOfDaySegment::Long24Bits); assert_eq!(len_of_day, LengthOfDaySegment::Long24Bits);
} else { } else {