diff --git a/Cargo.toml b/Cargo.toml index 20017a4..1bf3231 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,6 @@ version = "0.7.3" features = ["use-std"] [features] - +std = ["postcard/use-std"] default = ["heapless", "alloc"] alloc = ["serde/alloc"] diff --git a/src/lib.rs b/src/lib.rs index cf9eb94..7be3cca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,17 +2,20 @@ #![no_std] extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + use crate::ecss::CCSDS_HEADER_LEN; use serde::{Deserialize, Serialize}; pub mod ecss; -pub mod time; pub mod tc; +pub mod time; pub mod tm; #[derive(Debug, Copy, Clone, PartialEq)] pub enum PacketError { - /// The passed slice is too small. Returns the required size of the failed size chgeck + /// The passed slice is too small. Returns the required size of the failed size check ToBytesSliceTooSmall(usize), /// The [zerocopy] library failed to write to bytes ToBytesZeroCopyError, diff --git a/src/time.rs b/src/time.rs index f8a17ec..1ede74d 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,39 +1,120 @@ -enum CcsdsTimeCodes { +use crate::PacketError; +#[cfg(feature = "std")] +use std::time::SystemTime; + +pub const CDS_SHORT_LEN: usize = 7; +pub const DAYS_CCSDS_TO_UNIX: i32 = -4383; +pub const SECONDS_PER_DAY: u32 = 86400; + +pub enum CcsdsTimeCodes { None = 0, CucCcsdsEpoch = 0b001, CucAgencyEpoch = 0b010, Cds = 0b100, - Ccs = 0b101 + Ccs = 0b101, +} + +#[cfg(feature = "std")] +pub fn seconds_since_epoch() -> f64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("System time generation failed") + .as_secs_f64() +} + +/// Convert UNIX days to CCSDS days +/// +/// - CCSDS epoch: 1958 January 1 +/// - UNIX Epoch: 1970 January 1 +pub const fn unix_to_ccsds_days(unix_days: i32) -> i32 { + unix_days - DAYS_CCSDS_TO_UNIX +} + +/// Convert CCSDS days to UNIX days +/// +/// - CCSDS epoch: 1958 January 1 +/// - UNIX Epoch: 1970 January 1 +pub const fn ccsds_to_unix_days(unix_days: i32) -> i32 { + unix_days + DAYS_CCSDS_TO_UNIX } /// Trait for generic CCSDS time providers trait CcsdsTimeProvider { - fn write_to_bytes(&self, bytes: impl AsMut); + fn len(&self) -> usize; + fn write_to_bytes(&self, bytes: &mut (impl AsMut<[u8]> + ?Sized)) -> Result<(), PacketError>; /// Returns the pfield of the time provider. The pfield can have one or two bytes depending /// on the extension bit (first bit). The time provider should returns a tuple where the first /// entry denotes the length of the pfield and the second entry is the value of the pfield /// in big endian format. - fn pfield(&self) -> (usize, u16); + fn p_field(&self) -> (usize, [u8; 2]); fn ccdsd_time_code(&self) -> CcsdsTimeCodes; fn as_unix_seconds(&self) -> u64; } -struct CdsShortTimeProvider {} +pub struct CdsShortTimeProvider { + pfield: u8, + ccsds_days: u16, + ms_of_day: u32, + unix_seconds: u64, +} -impl CcsdsTimeProvider for CdsShortTimeProvider { - fn write_to_bytes(&self, bytes: impl AsMut) { - todo!() +impl CdsShortTimeProvider { + pub fn new(ccsds_days: u16, ms_of_day: u32) -> Self { + let mut provider = Self { + pfield: (CcsdsTimeCodes::Cds as u8) << 4, + ccsds_days, + ms_of_day, + unix_seconds: 0, + }; + provider.calc_unix_seconds(); + provider } - fn pfield(&self) -> (usize, u16) { - todo!() + #[cfg(feature = "std")] + pub fn ms_of_day_using_sysclock() -> u32 { + Self::ms_of_day(seconds_since_epoch()) + } + + pub fn ms_of_day(seconds_since_epoch: f64) -> u32 { + let fraction_ms = seconds_since_epoch - seconds_since_epoch.floor(); + let ms_of_day: u32 = + (((seconds_since_epoch.floor() as u32 % SECONDS_PER_DAY) * 1000) as f64 + fraction_ms) + .floor() as u32; + ms_of_day + } + + fn calc_unix_seconds(&mut self) { + let unix_days = ccsds_to_unix_days(self.ccsds_days as i32); + self.unix_seconds = unix_days as u64 * (24 * 60 * 60); + let seconds_of_day = (self.ms_of_day as f32 / 1000.0).floor() as u64; + self.unix_seconds += seconds_of_day; + } +} +impl CcsdsTimeProvider for CdsShortTimeProvider { + fn len(&self) -> usize { + CDS_SHORT_LEN + } + + fn write_to_bytes(&self, bytes: &mut (impl AsMut<[u8]> + ?Sized)) -> Result<(), PacketError> { + let slice = bytes.as_mut(); + if slice.len() < self.len() { + return Err(PacketError::ToBytesSliceTooSmall(slice.len())); + } + slice[0] = self.pfield; + slice[1..3].copy_from_slice(self.ccsds_days.to_be_bytes().as_slice()); + slice[4..].copy_from_slice(self.ms_of_day.to_be_bytes().as_slice()); + Ok(()) + } + + fn p_field(&self) -> (usize, [u8; 2]) { + (1, [self.pfield, 0]) } fn ccdsd_time_code(&self) -> CcsdsTimeCodes { - todo!() + CcsdsTimeCodes::Cds } fn as_unix_seconds(&self) -> u64 { - todo!() + self.unix_seconds } }