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"))]
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 {