17 Commits

Author SHA1 Message Date
638e4cda62 bump version
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-13 10:34:31 +02:00
1cc4771a53 cross-ref docs for examples
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-13 10:31:47 +02:00
427b368057 cargo fmt
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-13 10:29:09 +02:00
795abc57fa better names
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-13 10:28:20 +02:00
7da7e5329c typos
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-13 10:21:52 +02:00
5631372e58 update changelog
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-13 10:18:21 +02:00
28ba4f887d added some auto-conversion
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-13 09:52:59 +02:00
d559646d80 better naming/docs. new const for MAX_SEQ_COUNT 2022-09-13 09:41:21 +02:00
fe1a30327b work on uniform API
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-11 20:50:46 +02:00
94489da003 return usize instead of u8 for byte width
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-06 10:14:23 +02:00
c72c5ad4aa extensions
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
- Add source_data getter for PusTm
- Add std time info updater for CDS short time stamp provider
2022-09-03 20:54:37 +02:00
bb83e67e54 bump changelog
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-03 18:50:21 +02:00
a4e297f0c0 Add new features
- Basic ECSS enumeration support for u8, u16, u32 and u64
- Better names for generic error enums
2022-09-03 18:47:59 +02:00
96d389a651 fix test
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-09-03 16:30:21 +02:00
cc680dba46 timestamp writer should return timestamp error too 2022-09-03 16:28:11 +02:00
42d3487c19 raw accessor function
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-08-28 00:25:22 +02:00
3970061ca1 only allow tests for std envs
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
2022-08-20 23:19:38 +02:00
8 changed files with 372 additions and 132 deletions

View File

@ -6,9 +6,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] # [unreleased]
## [v0.1.0] 16.08.2022 # [v0.2.0] 13.09.2022
## Added
- Basic support for ECSS enumeration types for u8, u16, u32 and u64
## Changed
- Better names for generic error enumerations: `PacketError` renamed to `ByteConversionError`
- CCSDS module: `ssc` abbreviations fully replaced by better name `seq_count`
- Time module: `CcsdsTimeProvider::date_time` now has `Option<DateTime<Utc>>` as
a returnvalue instead of `DateTime<Utc>`
- `PusTc` and `PusTm`: `new_from_raw_slice` renamed to simpler `from_bytes`
# [v0.1.0] 16.08.2022
Initial release with CCSDS Space Packet Primary Header implementation and basic PUS TC and TM Initial release with CCSDS Space Packet Primary Header implementation and basic PUS TC and TM
implementations. implementations.

View File

@ -1,6 +1,6 @@
[package] [package]
name = "spacepackets" name = "spacepackets"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Generic implementations for various CCSDS and ECSS packet standards" description = "Generic implementations for various CCSDS and ECSS packet standards"

View File

@ -20,11 +20,9 @@ Currently, this includes the following components:
# Features # Features
`spacepackets` supports various runtime environments and is also suitable `spacepackets` supports various runtime environments and is also suitable for `no_std` environments.
for suitable for `no_std` environments. It has several features which may be enabled
for disabled.
It also offers support for [`serde`](https://serde.rs/). The Space Paccket, PUS TM and TC It offers support for [`serde`](https://serde.rs/). The Space Packet, PUS TM and TC
implementations derive the `serde` `Serialize` and `Deserialize` trait. This allows serializing and implementations derive the `serde` `Serialize` and `Deserialize` trait. This allows serializing and
deserializing them with an appropriate `serde` provider like deserializing them with an appropriate `serde` provider like
[`postcard`](https://github.com/jamesmunns/postcard). [`postcard`](https://github.com/jamesmunns/postcard).
@ -35,3 +33,8 @@ Default features:
- [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which operate on containers - [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which operate on containers
like [`alloc::vec::Vec`](https://doc.rust-lang.org/beta/alloc/vec/struct.Vec.html). like [`alloc::vec::Vec`](https://doc.rust-lang.org/beta/alloc/vec/struct.Vec.html).
Enabled by the `std` feature. Enabled by the `std` feature.
# Examples
You can check the [documentation](https://docs.rs/spacepackets) of individual modules for various
usage examples.

View File

@ -1,6 +1,6 @@
//! Common definitions and helpers required to create PUS TMTC packets according to //! Common definitions and helpers required to create PUS TMTC packets according to
//! [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/) //! [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/)
use crate::{CcsdsPacket, PacketError}; use crate::{ByteConversionError, CcsdsPacket, SizeMissmatch};
use core::mem::size_of; use core::mem::size_of;
use crc::{Crc, CRC_16_IBM_3740}; use crc::{Crc, CRC_16_IBM_3740};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -33,6 +33,22 @@ impl TryFrom<u8> for PusVersion {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PacketTypeCodes {
Boolean = 1,
Enumerated = 2,
UnsignedInt = 3,
SignedInt = 4,
Real = 5,
BitString = 6,
OctetString = 7,
CharString = 8,
AbsoluteTime = 9,
RelativeTime = 10,
Deduced = 11,
Packet = 12,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PusError { pub enum PusError {
VersionNotSupported(PusVersion), VersionNotSupported(PusVersion),
@ -41,7 +57,13 @@ pub enum PusError {
NoRawData, NoRawData,
/// CRC16 needs to be calculated first /// CRC16 needs to be calculated first
CrcCalculationMissing, CrcCalculationMissing,
PacketError(PacketError), ByteConversionError(ByteConversionError),
}
impl From<ByteConversionError> for PusError {
fn from(e: ByteConversionError) -> Self {
PusError::ByteConversionError(e)
}
} }
pub trait PusPacket: CcsdsPacket { pub trait PusPacket: CcsdsPacket {
@ -135,3 +157,165 @@ macro_rules! sp_header_impls {
pub(crate) use ccsds_impl; pub(crate) use ccsds_impl;
pub(crate) use sp_header_impls; pub(crate) use sp_header_impls;
/// Generic trait for ECSS enumeration which consist of a PFC field denoting their length
/// and an unsigned value. The trait makes no assumptions about the actual type of the unsigned
/// value and only requires implementors to implement a function which writes the enumeration into
/// a raw byte format.
pub trait EcssEnumeration {
/// Packet Format Code, which denotes the number of bits of the enumeration
fn pfc(&self) -> u8;
fn byte_width(&self) -> usize {
(self.pfc() / 8) as usize
}
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>;
}
trait ToBeBytes {
type ByteArray: AsRef<[u8]>;
fn to_be_bytes(&self) -> Self::ByteArray;
}
impl ToBeBytes for u8 {
type ByteArray = [u8; 1];
fn to_be_bytes(&self) -> Self::ByteArray {
u8::to_be_bytes(*self)
}
}
impl ToBeBytes for u16 {
type ByteArray = [u8; 2];
fn to_be_bytes(&self) -> Self::ByteArray {
u16::to_be_bytes(*self)
}
}
impl ToBeBytes for u32 {
type ByteArray = [u8; 4];
fn to_be_bytes(&self) -> Self::ByteArray {
u32::to_be_bytes(*self)
}
}
impl ToBeBytes for u64 {
type ByteArray = [u8; 8];
fn to_be_bytes(&self) -> Self::ByteArray {
u64::to_be_bytes(*self)
}
}
pub struct GenericEcssEnumWrapper<TYPE> {
val: TYPE,
}
impl<TYPE> GenericEcssEnumWrapper<TYPE> {
pub const fn ptc() -> PacketTypeCodes {
PacketTypeCodes::Enumerated
}
pub fn new(val: TYPE) -> Self {
Self { val }
}
}
impl<TYPE: ToBeBytes> EcssEnumeration for GenericEcssEnumWrapper<TYPE> {
fn pfc(&self) -> u8 {
size_of::<TYPE>() as u8 * 8_u8
}
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
if buf.len() < self.byte_width() as usize {
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: self.byte_width() as usize,
}));
}
buf[0..self.byte_width() as usize].copy_from_slice(self.val.to_be_bytes().as_ref());
Ok(())
}
}
pub type EcssEnumU8 = GenericEcssEnumWrapper<u8>;
pub type EcssEnumU16 = GenericEcssEnumWrapper<u16>;
pub type EcssEnumU32 = GenericEcssEnumWrapper<u32>;
pub type EcssEnumU64 = GenericEcssEnumWrapper<u64>;
#[cfg(test)]
mod tests {
use crate::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration};
use crate::ByteConversionError;
#[test]
fn test_enum_u8() {
let mut buf = [0, 0, 0];
let my_enum = EcssEnumU8::new(1);
my_enum
.write_to_bytes(&mut buf[1..2])
.expect("To byte conversion of u8 failed");
assert_eq!(buf[1], 1);
}
#[test]
fn test_enum_u16() {
let mut buf = [0, 0, 0];
let my_enum = EcssEnumU16::new(0x1f2f);
my_enum
.write_to_bytes(&mut buf[1..3])
.expect("To byte conversion of u8 failed");
assert_eq!(buf[1], 0x1f);
assert_eq!(buf[2], 0x2f);
}
#[test]
fn test_slice_u16_too_small() {
let mut buf = [0];
let my_enum = EcssEnumU16::new(0x1f2f);
let res = my_enum.write_to_bytes(&mut buf[0..1]);
assert!(res.is_err());
let error = res.unwrap_err();
match error {
ByteConversionError::ToSliceTooSmall(missmatch) => {
assert_eq!(missmatch.expected, 2);
assert_eq!(missmatch.found, 1);
}
_ => {
panic!("Unexpected error {:?}", error);
}
}
}
#[test]
fn test_enum_u32() {
let mut buf = [0, 0, 0, 0, 0];
let my_enum = EcssEnumU32::new(0x1f2f3f4f);
my_enum
.write_to_bytes(&mut buf[1..5])
.expect("To byte conversion of u8 failed");
assert_eq!(buf[1], 0x1f);
assert_eq!(buf[2], 0x2f);
assert_eq!(buf[3], 0x3f);
assert_eq!(buf[4], 0x4f);
}
#[test]
fn test_slice_u32_too_small() {
let mut buf = [0, 0, 0, 0, 0];
let my_enum = EcssEnumU32::new(0x1f2f3f4f);
let res = my_enum.write_to_bytes(&mut buf[0..3]);
assert!(res.is_err());
let error = res.unwrap_err();
match error {
ByteConversionError::ToSliceTooSmall(missmatch) => {
assert_eq!(missmatch.expected, 4);
assert_eq!(missmatch.found, 3);
}
_ => {
panic!("Unexpected error {:?}", error);
}
}
}
}

View File

@ -14,11 +14,9 @@
//! //!
//! ## Features //! ## Features
//! //!
//! `spacepackets` supports various runtime environments and is also suitable //! `spacepackets` supports various runtime environments and is also suitable for `no_std` environments.
//! for suitable for `no_std` environments. It has several features which may be enabled
//! for disabled.
//! //!
//! It also offers support for [`serde`](https://serde.rs/). The Space Paccket, PUS TM and TC //! It also offers support for [`serde`](https://serde.rs/). The Space Packet, PUS TM and TC
//! implementations derive the `serde` `Serialize` and `Deserialize` trait. This allows serializing and //! implementations derive the `serde` `Serialize` and `Deserialize` trait. This allows serializing and
//! deserializing them with an appropriate `serde` provider like //! deserializing them with an appropriate `serde` provider like
//! [`postcard`](https://github.com/jamesmunns/postcard). //! [`postcard`](https://github.com/jamesmunns/postcard).
@ -46,7 +44,7 @@
#![no_std] #![no_std]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
extern crate alloc; extern crate alloc;
#[cfg(feature = "std")] #[cfg(any(feature = "std", test))]
extern crate std; extern crate std;
use crate::ecss::CCSDS_HEADER_LEN; use crate::ecss::CCSDS_HEADER_LEN;
@ -59,20 +57,23 @@ pub mod tc;
pub mod time; pub mod time;
pub mod tm; pub mod tm;
pub const MAX_APID: u16 = 2u16.pow(11) - 1;
pub const MAX_SEQ_COUNT: u16 = 2u16.pow(14) - 1;
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SizeMissmatch { pub struct SizeMissmatch {
pub found: usize, pub found: usize,
pub expected: usize, pub expected: usize,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PacketError { pub enum ByteConversionError {
/// The passed slice is too small. Returns the found and expected minimum size /// The passed slice is too small. Returns the found and expected minimum size
ToBytesSliceTooSmall(SizeMissmatch), ToSliceTooSmall(SizeMissmatch),
/// The provider buffer it soo small. Returns the found and expected minimum size /// The provider buffer it soo small. Returns the found and expected minimum size
FromBytesSliceTooSmall(SizeMissmatch), FromSliceTooSmall(SizeMissmatch),
/// The [zerocopy] library failed to write to bytes /// The [zerocopy] library failed to write to bytes
ToBytesZeroCopyError, ZeroCopyToError,
FromBytesZeroCopyError, ZeroCopyFromError,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone)]
@ -142,7 +143,7 @@ impl PacketId {
/// not be set and false will be returned. The maximum allowed value for the 11-bit field is /// not be set and false will be returned. The maximum allowed value for the 11-bit field is
/// 2047 /// 2047
pub fn set_apid(&mut self, apid: u16) -> bool { pub fn set_apid(&mut self, apid: u16) -> bool {
if apid > 2u16.pow(11) - 1 { if apid > MAX_APID {
return false; return false;
} }
self.apid = apid; self.apid = apid;
@ -175,12 +176,13 @@ pub struct PacketSequenceCtrl {
} }
impl PacketSequenceCtrl { impl PacketSequenceCtrl {
pub fn new(seq_flags: SequenceFlags, ssc: u16) -> Option<PacketSequenceCtrl> { /// Returns [None] if the passed sequence count exceeds [MAX_SEQ_COUNT]
pub fn new(seq_flags: SequenceFlags, seq_count: u16) -> Option<PacketSequenceCtrl> {
let mut psc = PacketSequenceCtrl { let mut psc = PacketSequenceCtrl {
seq_flags, seq_flags,
seq_count: 0, seq_count: 0,
}; };
psc.set_seq_count(ssc).then(|| psc) psc.set_seq_count(seq_count).then(|| psc)
} }
pub fn raw(&self) -> u16 { pub fn raw(&self) -> u16 {
((self.seq_flags as u16) << 14) | self.seq_count ((self.seq_flags as u16) << 14) | self.seq_count
@ -189,14 +191,14 @@ impl PacketSequenceCtrl {
/// Set a new sequence count. If the passed number is invalid, the sequence count will not be /// Set a new sequence count. If the passed number is invalid, the sequence count will not be
/// set and false will be returned. The maximum allowed value for the 14-bit field is 16383 /// set and false will be returned. The maximum allowed value for the 14-bit field is 16383
pub fn set_seq_count(&mut self, ssc: u16) -> bool { pub fn set_seq_count(&mut self, ssc: u16) -> bool {
if ssc > 2u16.pow(14) - 1 { if ssc > MAX_SEQ_COUNT {
return false; return false;
} }
self.seq_count = ssc; self.seq_count = ssc;
true true
} }
pub fn ssc(&self) -> u16 { pub fn seq_count(&self) -> u16 {
self.seq_count self.seq_count
} }
} }
@ -321,6 +323,7 @@ pub struct SpHeader {
pub psc: PacketSequenceCtrl, pub psc: PacketSequenceCtrl,
pub data_len: u16, pub data_len: u16,
} }
impl Default for SpHeader { impl Default for SpHeader {
fn default() -> Self { fn default() -> Self {
SpHeader { SpHeader {
@ -339,29 +342,36 @@ impl Default for SpHeader {
} }
} }
impl SpHeader { impl SpHeader {
/// Create a new Space Packet Header instance which can be used to create generic
/// Space Packets. This will return [None] if the APID or sequence count argument
/// exceed [MAX_APID] or [MAX_SEQ_COUNT] respectively.
pub fn new( pub fn new(
ptype: PacketType, ptype: PacketType,
sec_header: bool, sec_header: bool,
apid: u16, apid: u16,
ssc: u16, seq_count: u16,
data_len: u16, data_len: u16,
) -> Option<Self> { ) -> Option<Self> {
if ssc > 2u16.pow(14) - 1 || apid > 2u16.pow(11) - 1 { if seq_count > MAX_SEQ_COUNT || apid > MAX_APID {
return None; return None;
} }
let mut header = SpHeader::default(); let mut header = SpHeader::default();
header.packet_id.sec_header_flag = sec_header; header.packet_id.sec_header_flag = sec_header;
header.packet_id.apid = apid; header.packet_id.apid = apid;
header.packet_id.ptype = ptype; header.packet_id.ptype = ptype;
header.psc.seq_count = ssc; header.psc.seq_count = seq_count;
header.data_len = data_len; header.data_len = data_len;
Some(header) Some(header)
} }
/// Helper function for telemetry space packet headers. The packet type field will be
/// set accordingly.
pub fn tm(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> { pub fn tm(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
Self::new(PacketType::Tm, false, apid, seq_count, data_len) Self::new(PacketType::Tm, false, apid, seq_count, data_len)
} }
/// Helper function for telecommand space packet headers. The packet type field will be
/// set accordingly.
pub fn tc(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> { pub fn tc(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
Self::new(PacketType::Tc, false, apid, seq_count, data_len) Self::new(PacketType::Tc, false, apid, seq_count, data_len)
} }
@ -391,15 +401,15 @@ impl SpHeader {
self.packet_id.ptype = packet_type; self.packet_id.ptype = packet_type;
} }
pub fn from_raw_slice(buf: &[u8]) -> Result<Self, PacketError> { pub fn from_raw_slice(buf: &[u8]) -> Result<Self, ByteConversionError> {
if buf.len() < CCSDS_HEADER_LEN + 1 { if buf.len() < CCSDS_HEADER_LEN + 1 {
return Err(PacketError::FromBytesSliceTooSmall(SizeMissmatch { return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(), found: buf.len(),
expected: CCSDS_HEADER_LEN + 1, expected: CCSDS_HEADER_LEN + 1,
})); }));
} }
let zc_header = zc::SpHeader::from_bytes(&buf[0..CCSDS_HEADER_LEN]) let zc_header = zc::SpHeader::from_bytes(&buf[0..CCSDS_HEADER_LEN])
.ok_or(PacketError::FromBytesZeroCopyError)?; .ok_or(ByteConversionError::ZeroCopyFromError)?;
Ok(Self::from(zc_header)) Ok(Self::from(zc_header))
} }
} }

View File

@ -20,13 +20,13 @@
//! // Serialize TC into a raw buffer //! // Serialize TC into a raw buffer
//! let mut test_buf: [u8; 32] = [0; 32]; //! let mut test_buf: [u8; 32] = [0; 32];
//! let size = pus_tc //! let size = pus_tc
//! .write_to(test_buf.as_mut_slice()) //! .write_to_bytes(test_buf.as_mut_slice())
//! .expect("Error writing TC to buffer"); //! .expect("Error writing TC to buffer");
//! assert_eq!(size, 13); //! assert_eq!(size, 13);
//! println!("{:?}", &test_buf[0..size]); //! println!("{:?}", &test_buf[0..size]);
//! //!
//! // Deserialize from the raw byte representation //! // Deserialize from the raw byte representation
//! let pus_tc_deserialized = PusTc::new_from_raw_slice(&test_buf).expect("Deserialization failed"); //! let pus_tc_deserialized = PusTc::from_bytes(&test_buf).expect("Deserialization failed");
//! assert_eq!(pus_tc.service(), 17); //! assert_eq!(pus_tc.service(), 17);
//! assert_eq!(pus_tc.subservice(), 1); //! assert_eq!(pus_tc.subservice(), 1);
//! assert_eq!(pus_tc.apid(), 0x02); //! assert_eq!(pus_tc.apid(), 0x02);
@ -36,7 +36,9 @@ use crate::ecss::{
verify_crc16_from_raw, CrcType, PusError, PusPacket, PusVersion, CRC_CCITT_FALSE, verify_crc16_from_raw, CrcType, PusError, PusPacket, PusVersion, CRC_CCITT_FALSE,
}; };
use crate::SpHeader; use crate::SpHeader;
use crate::{CcsdsPacket, PacketError, PacketType, SequenceFlags, SizeMissmatch, CCSDS_HEADER_LEN}; use crate::{
ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SizeMissmatch, CCSDS_HEADER_LEN,
};
use core::mem::size_of; use core::mem::size_of;
use delegate::delegate; use delegate::delegate;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -124,7 +126,7 @@ pub mod zc {
} }
impl PusTcSecondaryHeader { impl PusTcSecondaryHeader {
pub fn to_bytes(&self, slice: &mut [u8]) -> Option<()> { pub fn write_to_bytes(&self, slice: &mut [u8]) -> Option<()> {
self.write_to(slice) self.write_to(slice)
} }
@ -328,28 +330,27 @@ impl<'slice> PusTc<'slice> {
} }
/// Write the raw PUS byte representation to a provided buffer. /// Write the raw PUS byte representation to a provided buffer.
pub fn write_to(&self, slice: &mut [u8]) -> Result<usize, PusError> { pub fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
let mut curr_idx = 0; let mut curr_idx = 0;
let sph_zc = crate::zc::SpHeader::from(self.sp_header); let sph_zc = crate::zc::SpHeader::from(self.sp_header);
let tc_header_len = size_of::<zc::PusTcSecondaryHeader>(); let tc_header_len = size_of::<zc::PusTcSecondaryHeader>();
let total_size = self.len_packed(); let total_size = self.len_packed();
if total_size > slice.len() { if total_size > slice.len() {
return Err(PusError::PacketError(PacketError::ToBytesSliceTooSmall( return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
SizeMissmatch { found: slice.len(),
found: slice.len(), expected: total_size,
expected: total_size, })
}, .into());
)));
} }
sph_zc sph_zc
.to_bytes(&mut slice[curr_idx..curr_idx + CCSDS_HEADER_LEN]) .to_bytes(&mut slice[curr_idx..curr_idx + CCSDS_HEADER_LEN])
.ok_or(PusError::PacketError(PacketError::ToBytesZeroCopyError))?; .ok_or(ByteConversionError::ZeroCopyToError)?;
curr_idx += CCSDS_HEADER_LEN; curr_idx += CCSDS_HEADER_LEN;
let sec_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap(); let sec_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap();
sec_header sec_header
.to_bytes(&mut slice[curr_idx..curr_idx + tc_header_len]) .write_to_bytes(&mut slice[curr_idx..curr_idx + tc_header_len])
.ok_or(PusError::PacketError(PacketError::ToBytesZeroCopyError))?; .ok_or(ByteConversionError::ZeroCopyToError)?;
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 {
@ -400,7 +401,7 @@ impl<'slice> PusTc<'slice> {
/// Create a [PusTc] instance from a raw slice. On success, it returns a tuple containing /// Create a [PusTc] instance from a raw slice. On success, it returns a tuple containing
/// the instance and the found byte length of the packet /// the instance and the found byte length of the packet
pub fn new_from_raw_slice(slice: &'slice [u8]) -> Result<(Self, usize), PusError> { pub fn from_bytes(slice: &'slice [u8]) -> Result<(Self, usize), PusError> {
let raw_data_len = slice.len(); let raw_data_len = slice.len();
if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA { if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
return Err(PusError::RawDataTooShort(raw_data_len)); return Err(PusError::RawDataTooShort(raw_data_len));
@ -408,16 +409,16 @@ impl<'slice> PusTc<'slice> {
let mut current_idx = 0; let mut current_idx = 0;
let sph = let sph =
crate::zc::SpHeader::from_bytes(&slice[current_idx..current_idx + CCSDS_HEADER_LEN]) crate::zc::SpHeader::from_bytes(&slice[current_idx..current_idx + CCSDS_HEADER_LEN])
.ok_or(PusError::PacketError(PacketError::FromBytesZeroCopyError))?; .ok_or(ByteConversionError::ZeroCopyFromError)?;
current_idx += CCSDS_HEADER_LEN; current_idx += CCSDS_HEADER_LEN;
let total_len = sph.total_len(); let total_len = sph.total_len();
if raw_data_len < total_len || total_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA { if raw_data_len < total_len || total_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
return Err(PusError::RawDataTooShort(raw_data_len)); return Err(PusError::RawDataTooShort(raw_data_len));
} }
let sec_header = crate::tc::zc::PusTcSecondaryHeader::from_bytes( let sec_header = zc::PusTcSecondaryHeader::from_bytes(
&slice[current_idx..current_idx + PUC_TC_SECONDARY_HEADER_LEN], &slice[current_idx..current_idx + PUC_TC_SECONDARY_HEADER_LEN],
) )
.ok_or(PusError::PacketError(PacketError::FromBytesZeroCopyError))?; .ok_or(ByteConversionError::ZeroCopyFromError)?;
current_idx += PUC_TC_SECONDARY_HEADER_LEN; current_idx += PUC_TC_SECONDARY_HEADER_LEN;
let raw_data = &slice[0..total_len]; let raw_data = &slice[0..total_len];
let pus_tc = PusTc { let pus_tc = PusTc {
@ -431,6 +432,10 @@ impl<'slice> PusTc<'slice> {
verify_crc16_from_raw(raw_data, pus_tc.crc16.expect("CRC16 invalid"))?; verify_crc16_from_raw(raw_data, pus_tc.crc16.expect("CRC16 invalid"))?;
Ok((pus_tc, total_len)) Ok((pus_tc, total_len))
} }
pub fn raw(&self) -> Option<&'slice [u8]> {
self.raw_data
}
} }
//noinspection RsTraitImplementation //noinspection RsTraitImplementation
@ -472,8 +477,8 @@ mod tests {
use crate::ecss::{PusError, PusPacket}; use crate::ecss::{PusError, PusPacket};
use crate::tc::ACK_ALL; use crate::tc::ACK_ALL;
use crate::tc::{PusTc, PusTcSecondaryHeader, PusTcSecondaryHeaderT}; use crate::tc::{PusTc, PusTcSecondaryHeader, PusTcSecondaryHeaderT};
use crate::{ByteConversionError, SpHeader};
use crate::{CcsdsPacket, SequenceFlags}; use crate::{CcsdsPacket, SequenceFlags};
use crate::{PacketError, SpHeader};
use alloc::vec::Vec; use alloc::vec::Vec;
fn base_ping_tc_full_ctor() -> PusTc<'static> { fn base_ping_tc_full_ctor() -> PusTc<'static> {
@ -504,7 +509,7 @@ mod tests {
let pus_tc = base_ping_tc_simple_ctor(); let pus_tc = base_ping_tc_simple_ctor();
let mut test_buf: [u8; 32] = [0; 32]; let mut test_buf: [u8; 32] = [0; 32];
let size = pus_tc let size = pus_tc
.write_to(test_buf.as_mut_slice()) .write_to_bytes(test_buf.as_mut_slice())
.expect("Error writing TC to buffer"); .expect("Error writing TC to buffer");
assert_eq!(size, 13); assert_eq!(size, 13);
} }
@ -514,11 +519,11 @@ mod tests {
let pus_tc = base_ping_tc_simple_ctor(); let pus_tc = base_ping_tc_simple_ctor();
let mut test_buf: [u8; 32] = [0; 32]; let mut test_buf: [u8; 32] = [0; 32];
let size = pus_tc let size = pus_tc
.write_to(test_buf.as_mut_slice()) .write_to_bytes(test_buf.as_mut_slice())
.expect("Error writing TC to buffer"); .expect("Error writing TC to buffer");
assert_eq!(size, 13); assert_eq!(size, 13);
let (tc_from_raw, size) = PusTc::new_from_raw_slice(&test_buf) let (tc_from_raw, size) =
.expect("Creating PUS TC struct from raw buffer failed"); PusTc::from_bytes(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
assert_eq!(size, 13); assert_eq!(size, 13);
verify_test_tc(&tc_from_raw, false, 13); verify_test_tc(&tc_from_raw, false, 13);
assert!(tc_from_raw.user_data().is_none()); assert!(tc_from_raw.user_data().is_none());
@ -540,11 +545,11 @@ mod tests {
let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]); let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]);
let mut test_buf: [u8; 32] = [0; 32]; let mut test_buf: [u8; 32] = [0; 32];
let size = pus_tc let size = pus_tc
.write_to(test_buf.as_mut_slice()) .write_to_bytes(test_buf.as_mut_slice())
.expect("Error writing TC to buffer"); .expect("Error writing TC to buffer");
assert_eq!(size, 16); assert_eq!(size, 16);
let (tc_from_raw, size) = PusTc::new_from_raw_slice(&test_buf) let (tc_from_raw, size) =
.expect("Creating PUS TC struct from raw buffer failed"); PusTc::from_bytes(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
assert_eq!(size, 16); assert_eq!(size, 16);
verify_test_tc(&tc_from_raw, true, 16); verify_test_tc(&tc_from_raw, true, 16);
let user_data = tc_from_raw.user_data().unwrap(); let user_data = tc_from_raw.user_data().unwrap();
@ -571,10 +576,10 @@ mod tests {
let pus_tc = base_ping_tc_simple_ctor(); let pus_tc = base_ping_tc_simple_ctor();
let mut test_buf: [u8; 32] = [0; 32]; let mut test_buf: [u8; 32] = [0; 32];
pus_tc pus_tc
.write_to(test_buf.as_mut_slice()) .write_to_bytes(test_buf.as_mut_slice())
.expect("Error writing TC to buffer"); .expect("Error writing TC to buffer");
test_buf[12] = 0; test_buf[12] = 0;
let res = PusTc::new_from_raw_slice(&test_buf); let res = PusTc::from_bytes(&test_buf);
assert!(res.is_err()); assert!(res.is_err());
let err = res.unwrap_err(); let err = res.unwrap_err();
assert!(matches!(err, PusError::IncorrectCrc { .. })); assert!(matches!(err, PusError::IncorrectCrc { .. }));
@ -587,7 +592,7 @@ mod tests {
let mut test_buf: [u8; 32] = [0; 32]; let mut test_buf: [u8; 32] = [0; 32];
pus_tc.calc_own_crc16(); pus_tc.calc_own_crc16();
pus_tc pus_tc
.write_to(test_buf.as_mut_slice()) .write_to_bytes(test_buf.as_mut_slice())
.expect("Error writing TC to buffer"); .expect("Error writing TC to buffer");
verify_test_tc_raw(&test_buf); verify_test_tc_raw(&test_buf);
verify_crc_no_app_data(&test_buf); verify_crc_no_app_data(&test_buf);
@ -598,7 +603,7 @@ mod tests {
let mut pus_tc = base_ping_tc_simple_ctor(); let mut pus_tc = base_ping_tc_simple_ctor();
pus_tc.calc_crc_on_serialization = false; pus_tc.calc_crc_on_serialization = false;
let mut test_buf: [u8; 32] = [0; 32]; let mut test_buf: [u8; 32] = [0; 32];
let res = pus_tc.write_to(test_buf.as_mut_slice()); let res = pus_tc.write_to_bytes(test_buf.as_mut_slice());
assert!(res.is_err()); assert!(res.is_err());
let err = res.unwrap_err(); let err = res.unwrap_err();
assert!(matches!(err, PusError::CrcCalculationMissing { .. })); assert!(matches!(err, PusError::CrcCalculationMissing { .. }));
@ -622,12 +627,12 @@ mod tests {
fn test_write_buf_too_msall() { fn test_write_buf_too_msall() {
let pus_tc = base_ping_tc_simple_ctor(); let pus_tc = base_ping_tc_simple_ctor();
let mut test_buf = [0; 12]; let mut test_buf = [0; 12];
let res = pus_tc.write_to(test_buf.as_mut_slice()); let res = pus_tc.write_to_bytes(test_buf.as_mut_slice());
assert!(res.is_err()); assert!(res.is_err());
let err = res.unwrap_err(); let err = res.unwrap_err();
match err { match err {
PusError::PacketError(err) => match err { PusError::ByteConversionError(err) => match err {
PacketError::ToBytesSliceTooSmall(missmatch) => { ByteConversionError::ToSliceTooSmall(missmatch) => {
assert_eq!(missmatch.expected, pus_tc.len_packed()); assert_eq!(missmatch.expected, pus_tc.len_packed());
assert_eq!(missmatch.found, 12); assert_eq!(missmatch.found, 12);
} }
@ -643,7 +648,7 @@ mod tests {
verify_test_tc(&pus_tc, true, 16); verify_test_tc(&pus_tc, true, 16);
let mut test_buf: [u8; 32] = [0; 32]; let mut test_buf: [u8; 32] = [0; 32];
let size = pus_tc let size = pus_tc
.write_to(test_buf.as_mut_slice()) .write_to_bytes(test_buf.as_mut_slice())
.expect("Error writing TC to buffer"); .expect("Error writing TC to buffer");
assert_eq!(test_buf[11], 1); assert_eq!(test_buf[11], 1);
assert_eq!(test_buf[12], 2); assert_eq!(test_buf[12], 2);
@ -667,7 +672,7 @@ mod tests {
assert_eq!(pus_tc.sequence_flags(), SequenceFlags::Unsegmented); assert_eq!(pus_tc.sequence_flags(), SequenceFlags::Unsegmented);
pus_tc.calc_own_crc16(); pus_tc.calc_own_crc16();
pus_tc pus_tc
.write_to(test_buf.as_mut_slice()) .write_to_bytes(test_buf.as_mut_slice())
.expect("Error writing TC to buffer"); .expect("Error writing TC to buffer");
assert_eq!(test_buf[0], 0x1f); assert_eq!(test_buf[0], 0x1f);
assert_eq!(test_buf[1], 0xff); assert_eq!(test_buf[1], 0xff);

View File

@ -1,5 +1,5 @@
//! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) //! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
use crate::{PacketError, SizeMissmatch}; use crate::{ByteConversionError, SizeMissmatch};
use chrono::{DateTime, TimeZone, Utc}; use chrono::{DateTime, TimeZone, Utc};
#[allow(unused_imports)] #[allow(unused_imports)]
@ -38,12 +38,12 @@ impl TryFrom<u8> for CcsdsTimeCodes {
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TimestampError { pub enum TimestampError {
/// Contains tuple where first value is the expected time code and the second /// Contains tuple where first value is the expected time code and the second
/// value is the found raw value /// value is the found raw value
InvalidTimeCode(CcsdsTimeCodes, u8), InvalidTimeCode(CcsdsTimeCodes, u8),
OtherPacketError(PacketError), OtherPacketError(ByteConversionError),
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -71,7 +71,7 @@ pub const fn ccsds_to_unix_days(ccsds_days: i32) -> i32 {
} }
pub trait TimeWriter { pub trait TimeWriter {
fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<(), PacketError>; fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<(), TimestampError>;
} }
pub trait TimeReader { pub trait TimeReader {
@ -91,10 +91,25 @@ pub trait CcsdsTimeProvider {
fn p_field(&self) -> (usize, [u8; 2]); fn p_field(&self) -> (usize, [u8; 2]);
fn ccdsd_time_code(&self) -> CcsdsTimeCodes; fn ccdsd_time_code(&self) -> CcsdsTimeCodes;
fn unix_seconds(&self) -> i64; fn unix_seconds(&self) -> i64;
fn date_time(&self) -> DateTime<Utc>; fn date_time(&self) -> Option<DateTime<Utc>>;
} }
#[derive(Debug, Copy, Clone)] /// This object is the abstraction for the CCSDS Day Segmented Time Code (CDS).
///
/// It has the capability to generate and read timestamps as specified in the CCSDS 301.0-B-4
/// section 3.3
///
/// # Example
///
/// ```
/// use spacepackets::time::{CdsShortTimeProvider, TimeWriter};
/// use spacepackets::time::CcsdsTimeCodes::Cds;
/// let timestamp_now = CdsShortTimeProvider::from_now().unwrap();
/// let mut raw_stamp = [0; 7];
/// timestamp_now.write_to_bytes(&mut raw_stamp).unwrap();
/// assert_eq!((raw_stamp[0] >> 4) & 0b111, Cds as u8);
/// ```
#[derive(Debug, Copy, Clone, Default)]
pub struct CdsShortTimeProvider { pub struct CdsShortTimeProvider {
pfield: u8, pfield: u8,
ccsds_days: u16, ccsds_days: u16,
@ -106,7 +121,7 @@ pub struct CdsShortTimeProvider {
impl CdsShortTimeProvider { impl CdsShortTimeProvider {
pub fn new(ccsds_days: u16, ms_of_day: u32) -> Self { pub fn new(ccsds_days: u16, ms_of_day: u32) -> Self {
let provider = Self { let provider = Self {
pfield: (CcsdsTimeCodes::Cds as u8) << 4, pfield: (Cds as u8) << 4,
ccsds_days, ccsds_days,
ms_of_day, ms_of_day,
unix_seconds: 0, unix_seconds: 0,
@ -125,7 +140,7 @@ impl CdsShortTimeProvider {
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: (Cds as u8) << 4,
ccsds_days: unix_to_ccsds_days((unix_days_seconds / SECONDS_PER_DAY as u64) as i32) ccsds_days: unix_to_ccsds_days((unix_days_seconds / SECONDS_PER_DAY as u64) as i32)
as u16, as u16,
ms_of_day: ms_of_day as u32, ms_of_day: ms_of_day as u32,
@ -135,6 +150,17 @@ impl CdsShortTimeProvider {
Ok(provider.setup(unix_days_seconds as i64, ms_of_day)) Ok(provider.setup(unix_days_seconds as i64, ms_of_day))
} }
#[cfg(feature = "std")]
pub fn update_from_now(&mut self) -> Result<(), SystemTimeError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
let epoch = now.as_secs();
let secs_of_day = epoch % SECONDS_PER_DAY as u64;
let unix_days_seconds = epoch - secs_of_day;
let ms_of_day = secs_of_day * 1000 + now.subsec_millis() as u64;
self.setup(unix_days_seconds as i64, ms_of_day);
Ok(())
}
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((ms_of_day % 1000) as u32); self.calc_date_time((ms_of_day % 1000) as u32);
@ -181,25 +207,27 @@ impl CcsdsTimeProvider for CdsShortTimeProvider {
} }
fn ccdsd_time_code(&self) -> CcsdsTimeCodes { fn ccdsd_time_code(&self) -> CcsdsTimeCodes {
CcsdsTimeCodes::Cds Cds
} }
fn unix_seconds(&self) -> i64 { fn unix_seconds(&self) -> i64 {
self.unix_seconds self.unix_seconds
} }
fn date_time(&self) -> DateTime<Utc> { fn date_time(&self) -> Option<DateTime<Utc>> {
self.date_time.expect("Invalid date time") self.date_time
} }
} }
impl TimeWriter for CdsShortTimeProvider { impl TimeWriter for CdsShortTimeProvider {
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), PacketError> { fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), TimestampError> {
if buf.len() < self.len_as_bytes() { if buf.len() < self.len_as_bytes() {
return Err(PacketError::ToBytesSliceTooSmall(SizeMissmatch { return Err(TimestampError::OtherPacketError(
expected: self.len_as_bytes(), ByteConversionError::ToSliceTooSmall(SizeMissmatch {
found: buf.len(), expected: self.len_as_bytes(),
})); found: buf.len(),
}),
));
} }
buf[0] = self.pfield; buf[0] = self.pfield;
buf[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());
@ -212,7 +240,7 @@ impl TimeReader for CdsShortTimeProvider {
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError> { fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError> {
if buf.len() < CDS_SHORT_LEN { if buf.len() < CDS_SHORT_LEN {
return Err(TimestampError::OtherPacketError( return Err(TimestampError::OtherPacketError(
PacketError::FromBytesSliceTooSmall(SizeMissmatch { ByteConversionError::FromSliceTooSmall(SizeMissmatch {
expected: CDS_SHORT_LEN, expected: CDS_SHORT_LEN,
found: buf.len(), found: buf.len(),
}), }),
@ -222,19 +250,9 @@ impl TimeReader for CdsShortTimeProvider {
match CcsdsTimeCodes::try_from(pfield >> 4 & 0b111) { match CcsdsTimeCodes::try_from(pfield >> 4 & 0b111) {
Ok(cds_type) => match cds_type { Ok(cds_type) => match cds_type {
Cds => (), Cds => (),
_ => { _ => return Err(TimestampError::InvalidTimeCode(Cds, cds_type as u8)),
return Err(TimestampError::InvalidTimeCode(
CcsdsTimeCodes::Cds,
cds_type as u8,
))
}
}, },
_ => { _ => return Err(TimestampError::InvalidTimeCode(Cds, pfield >> 4 & 0b111)),
return Err(TimestampError::InvalidTimeCode(
CcsdsTimeCodes::Cds,
pfield >> 4 & 0b111,
))
}
}; };
let ccsds_days: u16 = u16::from_be_bytes(buf[1..3].try_into().unwrap()); let ccsds_days: u16 = u16::from_be_bytes(buf[1..3].try_into().unwrap());
let ms_of_day: u32 = u32::from_be_bytes(buf[3..7].try_into().unwrap()); let ms_of_day: u32 = u32::from_be_bytes(buf[3..7].try_into().unwrap());
@ -246,7 +264,7 @@ impl TimeReader for CdsShortTimeProvider {
mod tests { mod tests {
use super::*; use super::*;
use crate::time::TimestampError::{InvalidTimeCode, OtherPacketError}; use crate::time::TimestampError::{InvalidTimeCode, OtherPacketError};
use crate::PacketError::{FromBytesSliceTooSmall, ToBytesSliceTooSmall}; use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
use alloc::format; use alloc::format;
use chrono::{Datelike, Timelike}; use chrono::{Datelike, Timelike};
@ -270,12 +288,9 @@ mod tests {
time_stamper.unix_seconds(), time_stamper.unix_seconds(),
(DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as i64 (DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as i64
); );
assert_eq!(time_stamper.ccdsd_time_code(), CcsdsTimeCodes::Cds); assert_eq!(time_stamper.ccdsd_time_code(), Cds);
assert_eq!( assert_eq!(time_stamper.p_field(), (1, [(Cds as u8) << 4, 0]));
time_stamper.p_field(), let date_time = time_stamper.date_time().unwrap();
(1, [(CcsdsTimeCodes::Cds as u8) << 4, 0])
);
let date_time = time_stamper.date_time();
assert_eq!(date_time.year(), 1958); assert_eq!(date_time.year(), 1958);
assert_eq!(date_time.month(), 1); assert_eq!(date_time.month(), 1);
assert_eq!(date_time.day(), 1); assert_eq!(date_time.day(), 1);
@ -288,7 +303,7 @@ mod tests {
fn test_time_stamp_unix_epoch() { fn test_time_stamp_unix_epoch() {
let time_stamper = CdsShortTimeProvider::new((-DAYS_CCSDS_TO_UNIX) as u16, 0); let time_stamper = CdsShortTimeProvider::new((-DAYS_CCSDS_TO_UNIX) as u16, 0);
assert_eq!(time_stamper.unix_seconds(), 0); assert_eq!(time_stamper.unix_seconds(), 0);
let date_time = time_stamper.date_time(); let date_time = time_stamper.date_time().unwrap();
assert_eq!(date_time.year(), 1970); assert_eq!(date_time.year(), 1970);
assert_eq!(date_time.month(), 1); assert_eq!(date_time.month(), 1);
assert_eq!(date_time.day(), 1); assert_eq!(date_time.day(), 1);
@ -334,7 +349,7 @@ mod tests {
let res = time_stamper.write_to_bytes(&mut buf[0..i]); let res = time_stamper.write_to_bytes(&mut buf[0..i]);
assert!(res.is_err()); assert!(res.is_err());
match res.unwrap_err() { match res.unwrap_err() {
ToBytesSliceTooSmall(missmatch) => { OtherPacketError(ToSliceTooSmall(missmatch)) => {
assert_eq!(missmatch.found, i); assert_eq!(missmatch.found, i);
assert_eq!(missmatch.expected, 7); assert_eq!(missmatch.expected, 7);
} }
@ -357,7 +372,7 @@ mod tests {
panic!("Unexpected error"); panic!("Unexpected error");
} }
OtherPacketError(e) => match e { OtherPacketError(e) => match e {
FromBytesSliceTooSmall(missmatch) => { FromSliceTooSmall(missmatch) => {
assert_eq!(missmatch.found, i); assert_eq!(missmatch.found, i);
assert_eq!(missmatch.expected, 7); assert_eq!(missmatch.expected, 7);
} }
@ -379,7 +394,7 @@ mod tests {
let err = res.unwrap_err(); let err = res.unwrap_err();
match err { match err {
InvalidTimeCode(code, raw) => { InvalidTimeCode(code, raw) => {
assert_eq!(code, CcsdsTimeCodes::Cds); assert_eq!(code, Cds);
assert_eq!(raw, 0); assert_eq!(raw, 0);
} }
OtherPacketError(_) => {} OtherPacketError(_) => {}
@ -412,7 +427,7 @@ mod tests {
fn test_time_now() { fn test_time_now() {
let timestamp_now = CdsShortTimeProvider::from_now().unwrap(); let timestamp_now = CdsShortTimeProvider::from_now().unwrap();
let compare_stamp = Utc::now(); let compare_stamp = Utc::now();
let dt = timestamp_now.date_time(); let dt = timestamp_now.date_time().unwrap();
if compare_stamp.year() > dt.year() { if compare_stamp.year() > dt.year() {
assert_eq!(compare_stamp.year() - dt.year(), 1); assert_eq!(compare_stamp.year() - dt.year(), 1);
} else { } else {

View File

@ -5,7 +5,8 @@ use crate::ecss::{
verify_crc16_from_raw, CrcType, PusError, PusPacket, PusVersion, CRC_CCITT_FALSE, verify_crc16_from_raw, CrcType, PusError, PusPacket, PusVersion, CRC_CCITT_FALSE,
}; };
use crate::{ use crate::{
CcsdsPacket, PacketError, PacketType, SequenceFlags, SizeMissmatch, SpHeader, CCSDS_HEADER_LEN, ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SizeMissmatch, SpHeader,
CCSDS_HEADER_LEN,
}; };
use core::mem::size_of; use core::mem::size_of;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -66,7 +67,7 @@ pub mod zc {
} }
impl PusTmSecHeaderWithoutTimestamp { impl PusTmSecHeaderWithoutTimestamp {
pub fn to_bytes(&self, slice: &mut [u8]) -> Option<()> { pub fn write_to_bytes(&self, slice: &mut [u8]) -> Option<()> {
self.write_to(slice) self.write_to(slice)
} }
@ -258,6 +259,10 @@ impl<'slice> PusTm<'slice> {
self.sec_header.time_stamp self.sec_header.time_stamp
} }
pub fn source_data(&self) -> Option<&'slice [u8]> {
self.source_data
}
pub fn set_dest_id(&mut self, dest_id: u16) { pub fn set_dest_id(&mut self, dest_id: u16) {
self.sec_header.dest_id = dest_id; self.sec_header.dest_id = dest_id;
} }
@ -304,28 +309,27 @@ impl<'slice> PusTm<'slice> {
} }
/// Write the raw PUS byte representation to a provided buffer. /// Write the raw PUS byte representation to a provided buffer.
pub fn write_to(&self, slice: &mut [u8]) -> Result<usize, PusError> { pub fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
let mut curr_idx = 0; let mut curr_idx = 0;
let sph_zc = crate::zc::SpHeader::from(self.sp_header); let sph_zc = crate::zc::SpHeader::from(self.sp_header);
let total_size = self.len_packed(); let total_size = self.len_packed();
if total_size > slice.len() { if total_size > slice.len() {
return Err(PusError::PacketError(PacketError::ToBytesSliceTooSmall( return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
SizeMissmatch { found: slice.len(),
found: slice.len(), expected: total_size,
expected: total_size, })
}, .into());
)));
} }
sph_zc sph_zc
.to_bytes(&mut slice[curr_idx..curr_idx + CCSDS_HEADER_LEN]) .to_bytes(&mut slice[curr_idx..curr_idx + CCSDS_HEADER_LEN])
.ok_or(PusError::PacketError(PacketError::ToBytesZeroCopyError))?; .ok_or(ByteConversionError::ZeroCopyToError)?;
curr_idx += CCSDS_HEADER_LEN; curr_idx += CCSDS_HEADER_LEN;
let sec_header_len = size_of::<zc::PusTmSecHeaderWithoutTimestamp>(); let sec_header_len = size_of::<zc::PusTmSecHeaderWithoutTimestamp>();
let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap(); let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap();
sec_header sec_header
.to_bytes(&mut slice[curr_idx..curr_idx + sec_header_len]) .write_to_bytes(&mut slice[curr_idx..curr_idx + sec_header_len])
.ok_or(PusError::PacketError(PacketError::ToBytesZeroCopyError))?; .ok_or(ByteConversionError::ZeroCopyToError)?;
curr_idx += sec_header_len; curr_idx += sec_header_len;
let timestamp_len = self.sec_header.time_stamp.len(); let timestamp_len = self.sec_header.time_stamp.len();
slice[curr_idx..curr_idx + timestamp_len].copy_from_slice(self.sec_header.time_stamp); slice[curr_idx..curr_idx + timestamp_len].copy_from_slice(self.sec_header.time_stamp);
@ -383,7 +387,7 @@ impl<'slice> PusTm<'slice> {
/// Create a [PusTm] instance from a raw slice. On success, it returns a tuple containing /// Create a [PusTm] instance from a raw slice. On success, it returns a tuple containing
/// the instance and the found byte length of the packet. The timestamp length needs to be /// the instance and the found byte length of the packet. The timestamp length needs to be
/// known beforehand. /// known beforehand.
pub fn new_from_raw_slice( pub fn from_bytes(
slice: &'slice [u8], slice: &'slice [u8],
timestamp_len: usize, timestamp_len: usize,
) -> Result<(Self, usize), PusError> { ) -> Result<(Self, usize), PusError> {
@ -394,7 +398,7 @@ impl<'slice> PusTm<'slice> {
let mut current_idx = 0; let mut current_idx = 0;
let sph = let sph =
crate::zc::SpHeader::from_bytes(&slice[current_idx..current_idx + CCSDS_HEADER_LEN]) crate::zc::SpHeader::from_bytes(&slice[current_idx..current_idx + CCSDS_HEADER_LEN])
.ok_or(PusError::PacketError(PacketError::FromBytesZeroCopyError))?; .ok_or(ByteConversionError::ZeroCopyFromError)?;
current_idx += 6; current_idx += 6;
let total_len = sph.total_len(); let total_len = sph.total_len();
if raw_data_len < total_len || total_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA { if raw_data_len < total_len || total_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA {
@ -403,7 +407,7 @@ impl<'slice> PusTm<'slice> {
let sec_header_zc = zc::PusTmSecHeaderWithoutTimestamp::from_bytes( let sec_header_zc = zc::PusTmSecHeaderWithoutTimestamp::from_bytes(
&slice[current_idx..current_idx + PUC_TM_MIN_SEC_HEADER_LEN], &slice[current_idx..current_idx + PUC_TM_MIN_SEC_HEADER_LEN],
) )
.ok_or(PusError::PacketError(PacketError::FromBytesZeroCopyError))?; .ok_or(ByteConversionError::ZeroCopyFromError)?;
current_idx += PUC_TM_MIN_SEC_HEADER_LEN; current_idx += PUC_TM_MIN_SEC_HEADER_LEN;
let zc_sec_header_wrapper = zc::PusTmSecHeader { let zc_sec_header_wrapper = zc::PusTmSecHeader {
zc_header: sec_header_zc, zc_header: sec_header_zc,
@ -492,7 +496,9 @@ mod tests {
let time_stamp = dummy_time_stamp(); let time_stamp = dummy_time_stamp();
let pus_tm = base_ping_reply_full_ctor(&time_stamp); let pus_tm = base_ping_reply_full_ctor(&time_stamp);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let ser_len = pus_tm.write_to(&mut buf).expect("Serialization failed"); let ser_len = pus_tm
.write_to_bytes(&mut buf)
.expect("Serialization failed");
assert_eq!(ser_len, 22); assert_eq!(ser_len, 22);
verify_raw_ping_reply(&buf); verify_raw_ping_reply(&buf);
} }
@ -502,7 +508,9 @@ mod tests {
let src_data = [1, 2, 3]; let src_data = [1, 2, 3];
let hk_reply = base_hk_reply(dummy_time_stamp(), &src_data); let hk_reply = base_hk_reply(dummy_time_stamp(), &src_data);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let ser_len = hk_reply.write_to(&mut buf).expect("Serialization failed"); let ser_len = hk_reply
.write_to_bytes(&mut buf)
.expect("Serialization failed");
assert_eq!(ser_len, 25); assert_eq!(ser_len, 25);
assert_eq!(buf[20], 1); assert_eq!(buf[20], 1);
assert_eq!(buf[21], 2); assert_eq!(buf[21], 2);
@ -528,10 +536,11 @@ mod tests {
let time_stamp = dummy_time_stamp(); let time_stamp = dummy_time_stamp();
let pus_tm = base_ping_reply_full_ctor(&time_stamp); let pus_tm = base_ping_reply_full_ctor(&time_stamp);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let ser_len = pus_tm.write_to(&mut buf).expect("Serialization failed"); let ser_len = pus_tm
.write_to_bytes(&mut buf)
.expect("Serialization failed");
assert_eq!(ser_len, 22); assert_eq!(ser_len, 22);
let (tm_deserialized, size) = let (tm_deserialized, size) = PusTm::from_bytes(&buf, 7).expect("Deserialization failed");
PusTm::new_from_raw_slice(&buf, 7).expect("Deserialization failed");
assert_eq!(ser_len, size); assert_eq!(ser_len, size);
verify_ping_reply(&tm_deserialized, false, 22, dummy_time_stamp()); verify_ping_reply(&tm_deserialized, false, 22, dummy_time_stamp());
} }
@ -544,13 +553,13 @@ mod tests {
tm.calc_crc_on_serialization = false; tm.calc_crc_on_serialization = false;
assert_eq!(tm.data_len(), 0x00); assert_eq!(tm.data_len(), 0x00);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let res = tm.write_to(&mut buf); let res = tm.write_to_bytes(&mut buf);
assert!(res.is_err()); assert!(res.is_err());
assert!(matches!(res.unwrap_err(), PusError::CrcCalculationMissing)); assert!(matches!(res.unwrap_err(), PusError::CrcCalculationMissing));
tm.update_ccsds_data_len(); tm.update_ccsds_data_len();
assert_eq!(tm.data_len(), 15); assert_eq!(tm.data_len(), 15);
tm.calc_own_crc16(); tm.calc_own_crc16();
let res = tm.write_to(&mut buf); let res = tm.write_to_bytes(&mut buf);
assert!(res.is_ok()); assert!(res.is_ok());
tm.sp_header.data_len = 0; tm.sp_header.data_len = 0;
tm.update_packet_fields(); tm.update_packet_fields();
@ -562,13 +571,13 @@ mod tests {
let time_stamp = dummy_time_stamp(); let time_stamp = dummy_time_stamp();
let pus_tm = base_ping_reply_full_ctor(&time_stamp); let pus_tm = base_ping_reply_full_ctor(&time_stamp);
let mut buf: [u8; 16] = [0; 16]; let mut buf: [u8; 16] = [0; 16];
let res = pus_tm.write_to(&mut buf); let res = pus_tm.write_to_bytes(&mut buf);
assert!(res.is_err()); assert!(res.is_err());
let error = res.unwrap_err(); let error = res.unwrap_err();
assert!(matches!(error, PusError::PacketError { .. })); assert!(matches!(error, PusError::ByteConversionError { .. }));
match error { match error {
PusError::PacketError(err) => match err { PusError::ByteConversionError(err) => match err {
PacketError::ToBytesSliceTooSmall(size_missmatch) => { ByteConversionError::ToSliceTooSmall(size_missmatch) => {
assert_eq!(size_missmatch.expected, 22); assert_eq!(size_missmatch.expected, 22);
assert_eq!(size_missmatch.found, 16); assert_eq!(size_missmatch.found, 16);
} }