implemented function to get timestamp from bytes
This commit is contained in:
parent
88af155490
commit
4fc281070f
11
src/lib.rs
11
src/lib.rs
@ -13,10 +13,17 @@ pub mod tc;
|
|||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod tm;
|
pub mod tm;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct SizeMissmatch {
|
||||||
|
found: usize,
|
||||||
|
expected: usize,
|
||||||
|
}
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum PacketError {
|
pub enum PacketError {
|
||||||
/// The passed slice is too small. Returns the required size of the failed size check
|
/// The passed slice is too small. Returns the found and expected minimum size
|
||||||
ToBytesSliceTooSmall(usize),
|
ToBytesSliceTooSmall(SizeMissmatch),
|
||||||
|
/// The provider buffer it soo small. Returns the found and expected minimum size
|
||||||
|
FromBytesSliceTooSmall(SizeMissmatch),
|
||||||
/// The [zerocopy] library failed to write to bytes
|
/// The [zerocopy] library failed to write to bytes
|
||||||
ToBytesZeroCopyError,
|
ToBytesZeroCopyError,
|
||||||
FromBytesZeroCopyError,
|
FromBytesZeroCopyError,
|
||||||
|
22
src/tc.rs
22
src/tc.rs
@ -1,6 +1,6 @@
|
|||||||
use crate::ecss::{PusError, PusPacket, PusVersion, CRC_CCITT_FALSE};
|
use crate::ecss::{PusError, PusPacket, PusVersion, CRC_CCITT_FALSE};
|
||||||
use crate::ser::SpHeader;
|
use crate::ser::SpHeader;
|
||||||
use crate::{CcsdsPacket, PacketError, PacketType, SequenceFlags, CCSDS_HEADER_LEN};
|
use crate::{CcsdsPacket, PacketError, PacketType, SequenceFlags, SizeMissmatch, CCSDS_HEADER_LEN};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
@ -303,8 +303,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
self.calc_own_crc16();
|
self.calc_own_crc16();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_to_buf(&self, slice: &mut (impl AsMut<[u8]> + ?Sized)) -> Result<usize, PusError> {
|
pub fn copy_to_buf(&self, slice: &mut [u8]) -> Result<usize, PusError> {
|
||||||
let mut_slice = slice.as_mut();
|
|
||||||
let mut curr_idx = 0;
|
let mut curr_idx = 0;
|
||||||
let sph_zc = crate::zc::SpHeader::from(self.sph);
|
let sph_zc = crate::zc::SpHeader::from(self.sph);
|
||||||
let tc_header_len = size_of::<zc::PusTcDataFieldHeader>();
|
let tc_header_len = size_of::<zc::PusTcDataFieldHeader>();
|
||||||
@ -312,13 +311,16 @@ impl<'slice> PusTc<'slice> {
|
|||||||
if let Some(app_data) = self.app_data {
|
if let Some(app_data) = self.app_data {
|
||||||
total_size += app_data.len();
|
total_size += app_data.len();
|
||||||
};
|
};
|
||||||
if total_size > mut_slice.len() {
|
if total_size > slice.len() {
|
||||||
return Err(PusError::OtherPacketError(
|
return Err(PusError::OtherPacketError(
|
||||||
PacketError::ToBytesSliceTooSmall(total_size),
|
PacketError::ToBytesSliceTooSmall(SizeMissmatch {
|
||||||
|
found: slice.len(),
|
||||||
|
expected: total_size,
|
||||||
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
sph_zc
|
sph_zc
|
||||||
.to_bytes(&mut mut_slice[curr_idx..curr_idx + 6])
|
.to_bytes(&mut slice[curr_idx..curr_idx + 6])
|
||||||
.ok_or(PusError::OtherPacketError(
|
.ok_or(PusError::OtherPacketError(
|
||||||
PacketError::ToBytesZeroCopyError,
|
PacketError::ToBytesZeroCopyError,
|
||||||
))?;
|
))?;
|
||||||
@ -327,24 +329,24 @@ impl<'slice> PusTc<'slice> {
|
|||||||
let pus_tc_header = zc::PusTcDataFieldHeader::try_from(self.data_field_header).unwrap();
|
let pus_tc_header = zc::PusTcDataFieldHeader::try_from(self.data_field_header).unwrap();
|
||||||
|
|
||||||
pus_tc_header
|
pus_tc_header
|
||||||
.to_bytes(&mut mut_slice[curr_idx..curr_idx + tc_header_len])
|
.to_bytes(&mut slice[curr_idx..curr_idx + tc_header_len])
|
||||||
.ok_or(PusError::OtherPacketError(
|
.ok_or(PusError::OtherPacketError(
|
||||||
PacketError::ToBytesZeroCopyError,
|
PacketError::ToBytesZeroCopyError,
|
||||||
))?;
|
))?;
|
||||||
curr_idx += tc_header_len;
|
curr_idx += tc_header_len;
|
||||||
if let Some(app_data) = self.app_data {
|
if let Some(app_data) = self.app_data {
|
||||||
mut_slice[curr_idx..curr_idx + app_data.len()].copy_from_slice(app_data);
|
slice[curr_idx..curr_idx + app_data.len()].copy_from_slice(app_data);
|
||||||
curr_idx += app_data.len();
|
curr_idx += app_data.len();
|
||||||
}
|
}
|
||||||
let crc16;
|
let crc16;
|
||||||
if self.calc_crc_on_serialization {
|
if self.calc_crc_on_serialization {
|
||||||
crc16 = Self::calc_crc16(&mut_slice[0..curr_idx])
|
crc16 = Self::calc_crc16(&slice[0..curr_idx])
|
||||||
} else if self.crc16.is_none() {
|
} else if self.crc16.is_none() {
|
||||||
return Err(PusError::CrcCalculationMissing);
|
return Err(PusError::CrcCalculationMissing);
|
||||||
} else {
|
} else {
|
||||||
crc16 = self.crc16.unwrap();
|
crc16 = self.crc16.unwrap();
|
||||||
}
|
}
|
||||||
mut_slice[curr_idx..curr_idx + 2].copy_from_slice(crc16.to_be_bytes().as_slice());
|
slice[curr_idx..curr_idx + 2].copy_from_slice(crc16.to_be_bytes().as_slice());
|
||||||
curr_idx += 2;
|
curr_idx += 2;
|
||||||
Ok(curr_idx)
|
Ok(curr_idx)
|
||||||
}
|
}
|
||||||
|
135
src/time.rs
135
src/time.rs
@ -1,8 +1,9 @@
|
|||||||
use crate::PacketError;
|
use crate::{PacketError, SizeMissmatch};
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
|
||||||
|
use crate::time::CcsdsTimeCodes::Cds;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::time::SystemTime;
|
use std::time::{SystemTime, SystemTimeError};
|
||||||
|
|
||||||
pub const CDS_SHORT_LEN: usize = 7;
|
pub const CDS_SHORT_LEN: usize = 7;
|
||||||
pub const DAYS_CCSDS_TO_UNIX: i32 = -4383;
|
pub const DAYS_CCSDS_TO_UNIX: i32 = -4383;
|
||||||
@ -16,6 +17,25 @@ pub enum CcsdsTimeCodes {
|
|||||||
Ccs = 0b101,
|
Ccs = 0b101,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for CcsdsTimeCodes {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
x if x == CcsdsTimeCodes::None as u8 => Ok(CcsdsTimeCodes::None),
|
||||||
|
x if x == CcsdsTimeCodes::CucCcsdsEpoch as u8 => Ok(CcsdsTimeCodes::CucCcsdsEpoch),
|
||||||
|
x if x == CcsdsTimeCodes::CucAgencyEpoch as u8 => Ok(CcsdsTimeCodes::CucAgencyEpoch),
|
||||||
|
x if x == CcsdsTimeCodes::Cds as u8 => Ok(CcsdsTimeCodes::Cds),
|
||||||
|
x if x == CcsdsTimeCodes::Ccs as u8 => Ok(CcsdsTimeCodes::Ccs),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub enum TimestampError {
|
||||||
|
InvalidTimeCode(CcsdsTimeCodes, u8),
|
||||||
|
PacketError(PacketError),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn seconds_since_epoch() -> f64 {
|
pub fn seconds_since_epoch() -> f64 {
|
||||||
SystemTime::now()
|
SystemTime::now()
|
||||||
@ -43,7 +63,7 @@ pub const fn ccsds_to_unix_days(ccsds_days: i32) -> i32 {
|
|||||||
/// Trait for generic CCSDS time providers
|
/// Trait for generic CCSDS time providers
|
||||||
trait CcsdsTimeProvider {
|
trait CcsdsTimeProvider {
|
||||||
fn len(&self) -> usize;
|
fn len(&self) -> usize;
|
||||||
fn write_to_bytes(&self, bytes: &mut (impl AsMut<[u8]> + ?Sized)) -> Result<(), PacketError>;
|
fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<(), PacketError>;
|
||||||
/// Returns the pfield of the time provider. The pfield can have one or two bytes depending
|
/// 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
|
/// 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
|
/// entry denotes the length of the pfield and the second entry is the value of the pfield
|
||||||
@ -72,32 +92,64 @@ impl CdsShortTimeProvider {
|
|||||||
unix_seconds: 0,
|
unix_seconds: 0,
|
||||||
date_time: None,
|
date_time: None,
|
||||||
};
|
};
|
||||||
let unix_days_seconds = ccsds_to_unix_days(ccsds_days as i32) as i64 * (24 * 60 * 60);
|
let unix_days_seconds =
|
||||||
|
ccsds_to_unix_days(ccsds_days as i32) as i64 * SECONDS_PER_DAY as i64;
|
||||||
provider.setup(unix_days_seconds as i64, ms_of_day.into())
|
provider.setup(unix_days_seconds as i64, ms_of_day.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn from_now() -> Self {
|
pub fn from_now() -> Result<Self, SystemTimeError> {
|
||||||
let now = SystemTime::now()
|
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
|
||||||
.expect("Error retrieving UNIX epoch");
|
|
||||||
let epoch = now.as_secs();
|
let epoch = now.as_secs();
|
||||||
let secs_of_day = epoch % SECONDS_PER_DAY as u64;
|
let secs_of_day = epoch % SECONDS_PER_DAY as u64;
|
||||||
let unix_days_seconds = epoch - secs_of_day;
|
let unix_days_seconds = epoch - secs_of_day;
|
||||||
let ms_of_day = secs_of_day * 1000 + now.subsec_millis() as u64;
|
let ms_of_day = secs_of_day * 1000 + now.subsec_millis() as u64;
|
||||||
let provider = Self {
|
let provider = Self {
|
||||||
pfield: (CcsdsTimeCodes::Cds as u8) << 4,
|
pfield: (CcsdsTimeCodes::Cds as u8) << 4,
|
||||||
ccsds_days: unix_to_ccsds_days((unix_days_seconds / SECONDS_PER_DAY as u64) as i32) as u16,
|
ccsds_days: unix_to_ccsds_days((unix_days_seconds / SECONDS_PER_DAY as u64) as i32)
|
||||||
|
as u16,
|
||||||
ms_of_day: ms_of_day as u32,
|
ms_of_day: ms_of_day as u32,
|
||||||
unix_seconds: 0,
|
unix_seconds: 0,
|
||||||
date_time: None,
|
date_time: None,
|
||||||
};
|
};
|
||||||
provider.setup(unix_days_seconds as i64, ms_of_day.into())
|
Ok(provider.setup(unix_days_seconds as i64, ms_of_day.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError> {
|
||||||
|
if buf.len() < CDS_SHORT_LEN {
|
||||||
|
return Err(TimestampError::PacketError(
|
||||||
|
PacketError::FromBytesSliceTooSmall(SizeMissmatch {
|
||||||
|
expected: CDS_SHORT_LEN,
|
||||||
|
found: buf.len(),
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let pfield = buf[0];
|
||||||
|
match CcsdsTimeCodes::try_from(pfield >> 4 & 0b111) {
|
||||||
|
Ok(cds_type) => match cds_type {
|
||||||
|
Cds => (),
|
||||||
|
_ => {
|
||||||
|
return Err(TimestampError::InvalidTimeCode(
|
||||||
|
CcsdsTimeCodes::Cds,
|
||||||
|
cds_type as u8,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(TimestampError::InvalidTimeCode(
|
||||||
|
CcsdsTimeCodes::Cds,
|
||||||
|
pfield >> 4 & 0b111,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let ccsds_days: u16 = u16::from_be_bytes(buf[1..3].try_into().unwrap());
|
||||||
|
let ms_of_day: u32 = u32::from_be_bytes(buf[4..].try_into().unwrap());
|
||||||
|
Ok(Self::new(ccsds_days, ms_of_day))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(mut self, unix_days_seconds: i64, ms_of_day: u64) -> Self {
|
fn setup(mut self, unix_days_seconds: i64, ms_of_day: u64) -> Self {
|
||||||
self.calc_unix_seconds(unix_days_seconds, ms_of_day);
|
self.calc_unix_seconds(unix_days_seconds, ms_of_day);
|
||||||
self.calc_date_time(unix_days_seconds, (ms_of_day % 1000) as u32);
|
self.calc_date_time((ms_of_day % 1000) as u32);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,10 +176,10 @@ impl CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_date_time(&mut self, unix_days_seconds: i64, ms_since_last_second: u32) {
|
fn calc_date_time(&mut self, ms_since_last_second: u32) {
|
||||||
assert!(ms_since_last_second < 1000, "Invalid MS since last second");
|
assert!(ms_since_last_second < 1000, "Invalid MS since last second");
|
||||||
let ns_since_last_sec = ms_since_last_second * 1e6 as u32;
|
let ns_since_last_sec = ms_since_last_second * 1e6 as u32;
|
||||||
self.date_time = Some(Utc.timestamp(unix_days_seconds, ns_since_last_sec));
|
self.date_time = Some(Utc.timestamp(self.unix_seconds, ns_since_last_sec));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,14 +188,16 @@ impl CcsdsTimeProvider for CdsShortTimeProvider {
|
|||||||
CDS_SHORT_LEN
|
CDS_SHORT_LEN
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_bytes(&self, bytes: &mut (impl AsMut<[u8]> + ?Sized)) -> Result<(), PacketError> {
|
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), PacketError> {
|
||||||
let slice = bytes.as_mut();
|
if buf.len() < self.len() {
|
||||||
if slice.len() < self.len() {
|
return Err(PacketError::ToBytesSliceTooSmall(SizeMissmatch {
|
||||||
return Err(PacketError::ToBytesSliceTooSmall(slice.len()));
|
expected: self.len(),
|
||||||
|
found: buf.len(),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
slice[0] = self.pfield;
|
buf[0] = self.pfield;
|
||||||
slice[1..3].copy_from_slice(self.ccsds_days.to_be_bytes().as_slice());
|
buf[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());
|
buf[4..].copy_from_slice(self.ms_of_day.to_be_bytes().as_slice());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,8 +222,6 @@ impl CcsdsTimeProvider for CdsShortTimeProvider {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, Timelike};
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use std::println;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_creation() {
|
fn test_creation() {
|
||||||
@ -189,7 +241,7 @@ mod tests {
|
|||||||
let time_stamper = CdsShortTimeProvider::new(0, 0);
|
let time_stamper = CdsShortTimeProvider::new(0, 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
time_stamper.unix_seconds(),
|
time_stamper.unix_seconds(),
|
||||||
(DAYS_CCSDS_TO_UNIX * 24 * 60 * 60) as i64
|
(DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as i64
|
||||||
);
|
);
|
||||||
let date_time = time_stamper.date_time();
|
let date_time = time_stamper.date_time();
|
||||||
assert_eq!(date_time.year(), 1958);
|
assert_eq!(date_time.year(), 1958);
|
||||||
@ -213,10 +265,43 @@ mod tests {
|
|||||||
assert_eq!(date_time.second(), 0);
|
assert_eq!(date_time.second(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_packing() {}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_now() {
|
fn test_time_now() {
|
||||||
let timestamp_now = CdsShortTimeProvider::from_now();
|
let timestamp_now = CdsShortTimeProvider::from_now().unwrap();
|
||||||
println!("{}", timestamp_now.date_time());
|
let compare_stamp = Utc::now();
|
||||||
|
let dt = timestamp_now.date_time();
|
||||||
|
if compare_stamp.year() > dt.year() {
|
||||||
|
assert_eq!(compare_stamp.year() - dt.year(), 1);
|
||||||
|
} else {
|
||||||
|
assert_eq!(dt.year(), compare_stamp.year());
|
||||||
|
}
|
||||||
|
generic_dt_property_equality_check(dt.month(), compare_stamp.month(), 1, 12);
|
||||||
|
|
||||||
|
assert_eq!(dt.day(), compare_stamp.day());
|
||||||
|
if compare_stamp.day() < dt.day() {
|
||||||
|
assert!(dt.day() >= 28);
|
||||||
|
assert_eq!(compare_stamp.day(), 1);
|
||||||
|
} else if compare_stamp.day() > dt.day() {
|
||||||
|
assert_eq!(compare_stamp.day() - dt.day(), 1);
|
||||||
|
} else {
|
||||||
|
assert_eq!(compare_stamp.day(), dt.day());
|
||||||
|
}
|
||||||
|
generic_dt_property_equality_check(dt.hour(), compare_stamp.hour(), 0, 23);
|
||||||
|
generic_dt_property_equality_check(dt.minute(), compare_stamp.minute(), 0, 59);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_dt_property_equality_check(first: u32, second: u32, start: u32, end: u32) {
|
||||||
|
if second < first {
|
||||||
|
assert_eq!(second, start);
|
||||||
|
assert_eq!(first, end);
|
||||||
|
} else if second > first {
|
||||||
|
assert_eq!(second - first, 1);
|
||||||
|
} else {
|
||||||
|
assert_eq!(first, second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user