This commit is contained in:
parent
938c4ba770
commit
aeb2e806b8
@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
# [v0.3.1] 03.12.2022
|
# [v0.3.1] 03.12.2022
|
||||||
|
|
||||||
- Small fix for faulty docs.rs build
|
- Small fix for faulty docs.rs build
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "spacepackets"
|
name = "spacepackets"
|
||||||
version = "0.3.1"
|
version = "0.4.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"
|
||||||
@ -33,12 +33,10 @@ default-features = false
|
|||||||
[dev-dependencies.postcard]
|
[dev-dependencies.postcard]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
|
|
||||||
[dev-dependencies.serde_json]
|
|
||||||
version = "1.0"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "serde"]
|
default = ["std", "dep:serde"]
|
||||||
std = ["chrono/std", "chrono/clock", "alloc"]
|
std = ["chrono/std", "chrono/clock", "alloc"]
|
||||||
|
serde = ["chrono/serde"]
|
||||||
alloc = ["postcard/alloc"]
|
alloc = ["postcard/alloc"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
|
@ -37,6 +37,7 @@ impl TryFrom<u8> for PusVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum PacketTypeCodes {
|
pub enum PacketTypeCodes {
|
||||||
Boolean = 1,
|
Boolean = 1,
|
||||||
Enumerated = 2,
|
Enumerated = 2,
|
||||||
@ -53,6 +54,7 @@ pub enum PacketTypeCodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum PusError {
|
pub enum PusError {
|
||||||
VersionNotSupported(PusVersion),
|
VersionNotSupported(PusVersion),
|
||||||
IncorrectCrc(u16),
|
IncorrectCrc(u16),
|
||||||
@ -214,6 +216,7 @@ impl ToBeBytes for u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct GenericEcssEnumWrapper<TYPE> {
|
pub struct GenericEcssEnumWrapper<TYPE> {
|
||||||
val: TYPE,
|
val: TYPE,
|
||||||
}
|
}
|
||||||
|
82
src/lib.rs
82
src/lib.rs
@ -138,13 +138,22 @@ pub struct PacketId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PacketId {
|
impl PacketId {
|
||||||
pub fn new(ptype: PacketType, sec_header_flag: bool, apid: u16) -> Option<PacketId> {
|
const fn const_new(ptype: PacketType, sec_header_flag: bool, apid: u16) -> PacketId {
|
||||||
let mut pid = PacketId {
|
if apid > MAX_APID {
|
||||||
|
panic!("APID too large");
|
||||||
|
}
|
||||||
|
PacketId {
|
||||||
ptype,
|
ptype,
|
||||||
sec_header_flag,
|
sec_header_flag,
|
||||||
apid: 0,
|
apid,
|
||||||
};
|
}
|
||||||
pid.set_apid(apid).then_some(pid)
|
}
|
||||||
|
|
||||||
|
pub fn new(ptype: PacketType, sec_header_flag: bool, apid: u16) -> Option<PacketId> {
|
||||||
|
if apid > MAX_APID {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(PacketId::const_new(ptype, sec_header_flag, apid))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a new Application Process ID (APID). If the passed number is invalid, the APID will
|
/// Set a new Application Process ID (APID). If the passed number is invalid, the APID will
|
||||||
@ -185,20 +194,31 @@ pub struct PacketSequenceCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PacketSequenceCtrl {
|
impl PacketSequenceCtrl {
|
||||||
/// Returns [None] if the passed sequence count exceeds [MAX_SEQ_COUNT]
|
/// const variant of [PacketSequenceCtrl::new], but panics if the sequence count exceeds
|
||||||
pub fn new(seq_flags: SequenceFlags, seq_count: u16) -> Option<PacketSequenceCtrl> {
|
/// [MAX_SEQ_COUNT].
|
||||||
let mut psc = PacketSequenceCtrl {
|
const fn const_new(seq_flags: SequenceFlags, seq_count: u16) -> PacketSequenceCtrl {
|
||||||
|
if seq_count > MAX_SEQ_COUNT {
|
||||||
|
panic!("Sequence count too large");
|
||||||
|
}
|
||||||
|
PacketSequenceCtrl {
|
||||||
seq_flags,
|
seq_flags,
|
||||||
seq_count: 0,
|
seq_count,
|
||||||
};
|
}
|
||||||
psc.set_seq_count(seq_count).then_some(psc)
|
}
|
||||||
|
|
||||||
|
/// Returns [None] if the passed sequence count exceeds [MAX_SEQ_COUNT].
|
||||||
|
pub fn new(seq_flags: SequenceFlags, seq_count: u16) -> Option<PacketSequenceCtrl> {
|
||||||
|
if seq_count > MAX_SEQ_COUNT {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(PacketSequenceCtrl::const_new(seq_flags, seq_count))
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 > MAX_SEQ_COUNT {
|
if ssc > MAX_SEQ_COUNT {
|
||||||
return false;
|
return false;
|
||||||
@ -239,7 +259,7 @@ macro_rules! sph_from_other {
|
|||||||
const SSC_MASK: u16 = 0x3FFF;
|
const SSC_MASK: u16 = 0x3FFF;
|
||||||
const VERSION_MASK: u16 = 0xE000;
|
const VERSION_MASK: u16 = 0xE000;
|
||||||
|
|
||||||
/// Generic trait to access fields of a CCSDS space packet header according to CCSDS 133.0-B-2
|
/// Generic trait to access fields of a CCSDS space packet header according to CCSDS 133.0-B-2.
|
||||||
pub trait CcsdsPacket {
|
pub trait CcsdsPacket {
|
||||||
fn ccsds_version(&self) -> u8;
|
fn ccsds_version(&self) -> u8;
|
||||||
fn packet_id(&self) -> PacketId;
|
fn packet_id(&self) -> PacketId;
|
||||||
@ -253,7 +273,7 @@ pub trait CcsdsPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve 13 bit Packet Identification field. Can usually be retrieved with a bitwise AND
|
/// Retrieve 13 bit Packet Identification field. Can usually be retrieved with a bitwise AND
|
||||||
/// of the first 2 bytes with 0x1FFF
|
/// of the first 2 bytes with 0x1FFF.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn packet_id_raw(&self) -> u16 {
|
fn packet_id_raw(&self) -> u16 {
|
||||||
self.packet_id().raw()
|
self.packet_id().raw()
|
||||||
@ -264,8 +284,8 @@ pub trait CcsdsPacket {
|
|||||||
self.psc().raw()
|
self.psc().raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve Packet Type (TM: 0, TC: 1).
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Retrieve Packet Type (TM: 0, TC: 1)
|
|
||||||
fn ptype(&self) -> PacketType {
|
fn ptype(&self) -> PacketType {
|
||||||
// This call should never fail because only 0 and 1 can be passed to the try_from call
|
// This call should never fail because only 0 and 1 can be passed to the try_from call
|
||||||
self.packet_id().ptype
|
self.packet_id().ptype
|
||||||
@ -282,13 +302,13 @@ pub trait CcsdsPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the secondary header flag. Returns true if a secondary header is present
|
/// Retrieve the secondary header flag. Returns true if a secondary header is present
|
||||||
/// and false if it is not
|
/// and false if it is not.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sec_header_flag(&self) -> bool {
|
fn sec_header_flag(&self) -> bool {
|
||||||
self.packet_id().sec_header_flag
|
self.packet_id().sec_header_flag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve Application Process ID
|
/// Retrieve Application Process ID.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn apid(&self) -> u16 {
|
fn apid(&self) -> u16 {
|
||||||
self.packet_id().apid
|
self.packet_id().apid
|
||||||
@ -316,7 +336,7 @@ pub trait CcsdsPrimaryHeader {
|
|||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Space Packet Primary Header according to CCSDS 133.0-B-2
|
/// Space Packet Primary Header according to CCSDS 133.0-B-2.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
@ -351,6 +371,7 @@ impl Default for SpHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpHeader {
|
impl SpHeader {
|
||||||
/// Create a new Space Packet Header instance which can be used to create generic
|
/// 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
|
/// Space Packets. This will return [None] if the APID or sequence count argument
|
||||||
@ -549,25 +570,19 @@ pub mod zc {
|
|||||||
sph_from_other!(SpHeader, crate::SpHeader);
|
sph_from_other!(SpHeader, crate::SpHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "std"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::CcsdsPrimaryHeader;
|
use crate::CcsdsPrimaryHeader;
|
||||||
use crate::SpHeader;
|
use crate::SpHeader;
|
||||||
use crate::{
|
use crate::{
|
||||||
packet_type_in_raw_packet_id, zc, CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType,
|
packet_type_in_raw_packet_id, zc, CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType,
|
||||||
SequenceFlags,
|
SequenceFlags,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use num::pow;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use num_traits::pow;
|
use num_traits::pow;
|
||||||
use postcard::from_bytes;
|
#[cfg(feature = "serde")]
|
||||||
#[cfg(feature = "alloc")]
|
use postcard::{from_bytes, to_allocvec};
|
||||||
use postcard::to_allocvec;
|
|
||||||
use std::println;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_seq_flag_helpers() {
|
fn test_seq_flag_helpers() {
|
||||||
@ -650,7 +665,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(all(feature = "std", feature = "serde"))]
|
#[cfg(feature = "serde")]
|
||||||
fn test_serde_sph() {
|
fn test_serde_sph() {
|
||||||
let sp_header = SpHeader::tc(0x42, 12, 0).expect("Error creating SP header");
|
let sp_header = SpHeader::tc(0x42, 12, 0).expect("Error creating SP header");
|
||||||
assert_eq!(sp_header.ccsds_version(), 0b000);
|
assert_eq!(sp_header.ccsds_version(), 0b000);
|
||||||
@ -773,13 +788,4 @@ mod tests {
|
|||||||
assert_eq!(sp_header.ptype(), PacketType::Tc);
|
assert_eq!(sp_header.ptype(), PacketType::Tc);
|
||||||
assert_eq!(sp_header.data_len(), 0);
|
assert_eq!(sp_header.data_len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
fn test_serde_serialization() {
|
|
||||||
let sp_header = SpHeader::tc(0x42, 12, 0).expect("Error creating SP header");
|
|
||||||
let as_str =
|
|
||||||
serde_json::to_string(&sp_header).expect("Converting SP header to JSON string failed");
|
|
||||||
println!("{:?}", as_str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
26
src/tc.rs
26
src/tc.rs
@ -67,7 +67,7 @@ pub const ACK_ALL: u8 = AckOpts::Acceptance as u8
|
|||||||
| AckOpts::Progress as u8
|
| AckOpts::Progress as u8
|
||||||
| AckOpts::Completion as u8;
|
| AckOpts::Completion as u8;
|
||||||
|
|
||||||
pub trait PusTcSecondaryHeaderT {
|
pub trait GenericPusTcSecondaryHeader {
|
||||||
fn pus_version(&self) -> PusVersion;
|
fn pus_version(&self) -> PusVersion;
|
||||||
fn ack_flags(&self) -> u8;
|
fn ack_flags(&self) -> u8;
|
||||||
fn service(&self) -> u8;
|
fn service(&self) -> u8;
|
||||||
@ -77,7 +77,7 @@ pub trait PusTcSecondaryHeaderT {
|
|||||||
|
|
||||||
pub mod zc {
|
pub mod zc {
|
||||||
use crate::ecss::{PusError, PusVersion};
|
use crate::ecss::{PusError, PusVersion};
|
||||||
use crate::tc::PusTcSecondaryHeaderT;
|
use crate::tc::GenericPusTcSecondaryHeader;
|
||||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
||||||
|
|
||||||
#[derive(FromBytes, AsBytes, Unaligned)]
|
#[derive(FromBytes, AsBytes, Unaligned)]
|
||||||
@ -104,7 +104,7 @@ pub mod zc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusTcSecondaryHeaderT for PusTcSecondaryHeader {
|
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
|
||||||
fn pus_version(&self) -> PusVersion {
|
fn pus_version(&self) -> PusVersion {
|
||||||
PusVersion::try_from(self.version_ack >> 4 & 0b1111).unwrap_or(PusVersion::Invalid)
|
PusVersion::try_from(self.version_ack >> 4 & 0b1111).unwrap_or(PusVersion::Invalid)
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ pub struct PusTcSecondaryHeader {
|
|||||||
pub version: PusVersion,
|
pub version: PusVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusTcSecondaryHeaderT for PusTcSecondaryHeader {
|
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
|
||||||
fn pus_version(&self) -> PusVersion {
|
fn pus_version(&self) -> PusVersion {
|
||||||
self.version
|
self.version
|
||||||
}
|
}
|
||||||
@ -263,7 +263,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Simplified version of the [PusTc::new] function which allows to only specify service and
|
/// Simplified version of the [PusTc::new] function which allows to only specify service and
|
||||||
/// subservice instead of the full PUS TC secondary header
|
/// subservice instead of the full PUS TC secondary header.
|
||||||
pub fn new_simple(
|
pub fn new_simple(
|
||||||
sph: &mut SpHeader,
|
sph: &mut SpHeader,
|
||||||
service: u8,
|
service: u8,
|
||||||
@ -306,7 +306,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
/// used.
|
/// used.
|
||||||
/// If this was not done or the application data is set or changed after construction,
|
/// If this was not done or the application data is set or changed after construction,
|
||||||
/// this function needs to be called to ensure that the data length field of the CCSDS header
|
/// this function needs to be called to ensure that the data length field of the CCSDS header
|
||||||
/// is set correctly
|
/// is set correctly.
|
||||||
pub fn update_ccsds_data_len(&mut self) {
|
pub fn update_ccsds_data_len(&mut self) {
|
||||||
self.sp_header.data_len =
|
self.sp_header.data_len =
|
||||||
self.len_packed() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
|
self.len_packed() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
|
||||||
@ -326,7 +326,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
self.crc16 = Some(digest.finalize())
|
self.crc16 = Some(digest.finalize())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This helper function calls both [PusTc.update_ccsds_data_len] and [PusTc.calc_own_crc16]
|
/// This helper function calls both [PusTc.update_ccsds_data_len] and [PusTc.calc_own_crc16].
|
||||||
pub fn update_packet_fields(&mut self) {
|
pub fn update_packet_fields(&mut self) {
|
||||||
self.update_ccsds_data_len();
|
self.update_ccsds_data_len();
|
||||||
self.calc_own_crc16();
|
self.calc_own_crc16();
|
||||||
@ -404,7 +404,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 from_bytes(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 {
|
||||||
@ -465,7 +465,7 @@ impl PusPacket for PusTc<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
//noinspection RsTraitImplementation
|
||||||
impl PusTcSecondaryHeaderT for PusTc<'_> {
|
impl GenericPusTcSecondaryHeader for PusTc<'_> {
|
||||||
delegate!(to self.sec_header {
|
delegate!(to self.sec_header {
|
||||||
fn pus_version(&self) -> PusVersion;
|
fn pus_version(&self) -> PusVersion;
|
||||||
fn service(&self) -> u8;
|
fn service(&self) -> u8;
|
||||||
@ -475,15 +475,14 @@ impl PusTcSecondaryHeaderT for PusTc<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "std"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::ecss::PusVersion::PusC;
|
use crate::ecss::PusVersion::PusC;
|
||||||
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::{GenericPusTcSecondaryHeader, PusTc, PusTcSecondaryHeader};
|
||||||
use crate::{ByteConversionError, SpHeader};
|
use crate::{ByteConversionError, SpHeader};
|
||||||
use crate::{CcsdsPacket, SequenceFlags};
|
use crate::{CcsdsPacket, SequenceFlags};
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
fn base_ping_tc_full_ctor() -> PusTc<'static> {
|
fn base_ping_tc_full_ctor() -> PusTc<'static> {
|
||||||
@ -564,7 +563,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
fn test_vec_ser_deser() {
|
fn test_vec_ser_deser() {
|
||||||
let pus_tc = base_ping_tc_simple_ctor();
|
let pus_tc = base_ping_tc_simple_ctor();
|
||||||
let mut test_vec = Vec::new();
|
let mut test_vec = Vec::new();
|
||||||
@ -629,7 +627,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_buf_too_msall() {
|
fn test_write_buf_too_small() {
|
||||||
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_bytes(test_buf.as_mut_slice());
|
let res = pus_tc.write_to_bytes(test_buf.as_mut_slice());
|
||||||
|
45
src/time.rs
45
src/time.rs
@ -10,11 +10,15 @@ use crate::time::CcsdsTimeCodes::Cds;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::time::{SystemTime, SystemTimeError};
|
use std::time::{SystemTime, SystemTimeError};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
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;
|
||||||
pub const SECONDS_PER_DAY: u32 = 86400;
|
pub const SECONDS_PER_DAY: u32 = 86400;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum CcsdsTimeCodes {
|
pub enum CcsdsTimeCodes {
|
||||||
None = 0,
|
None = 0,
|
||||||
CucCcsdsEpoch = 0b001,
|
CucCcsdsEpoch = 0b001,
|
||||||
@ -23,7 +27,7 @@ pub enum CcsdsTimeCodes {
|
|||||||
Ccs = 0b101,
|
Ccs = 0b101,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CDS_SHORT_P_FIELD: u8 = (CcsdsTimeCodes::Cds as u8) << 4;
|
const CDS_SHORT_P_FIELD: u8 = (Cds as u8) << 4;
|
||||||
|
|
||||||
impl TryFrom<u8> for CcsdsTimeCodes {
|
impl TryFrom<u8> for CcsdsTimeCodes {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
@ -41,6 +45,7 @@ impl TryFrom<u8> for CcsdsTimeCodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
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
|
||||||
@ -83,7 +88,7 @@ pub trait TimeReader {
|
|||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for generic CCSDS time providers
|
/// Trait for generic CCSDS time providers.
|
||||||
pub trait CcsdsTimeProvider {
|
pub trait CcsdsTimeProvider {
|
||||||
fn len_as_bytes(&self) -> usize;
|
fn len_as_bytes(&self) -> usize;
|
||||||
|
|
||||||
@ -112,12 +117,12 @@ pub trait CcsdsTimeProvider {
|
|||||||
/// timestamp_now.write_to_bytes(&mut raw_stamp).unwrap();
|
/// timestamp_now.write_to_bytes(&mut raw_stamp).unwrap();
|
||||||
/// assert_eq!((raw_stamp[0] >> 4) & 0b111, Cds as u8);
|
/// assert_eq!((raw_stamp[0] >> 4) & 0b111, Cds as u8);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct CdsShortTimeProvider {
|
pub struct CdsShortTimeProvider {
|
||||||
ccsds_days: u16,
|
ccsds_days: u16,
|
||||||
ms_of_day: u32,
|
ms_of_day: u32,
|
||||||
unix_seconds: i64,
|
unix_seconds: i64,
|
||||||
date_time: Option<DateTime<Utc>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CdsShortTimeProvider {
|
impl CdsShortTimeProvider {
|
||||||
@ -126,7 +131,6 @@ impl CdsShortTimeProvider {
|
|||||||
ccsds_days,
|
ccsds_days,
|
||||||
ms_of_day,
|
ms_of_day,
|
||||||
unix_seconds: 0,
|
unix_seconds: 0,
|
||||||
date_time: None,
|
|
||||||
};
|
};
|
||||||
let unix_days_seconds =
|
let unix_days_seconds =
|
||||||
ccsds_to_unix_days(ccsds_days as i32) as i64 * SECONDS_PER_DAY as i64;
|
ccsds_to_unix_days(ccsds_days as i32) as i64 * SECONDS_PER_DAY as i64;
|
||||||
@ -146,7 +150,6 @@ impl CdsShortTimeProvider {
|
|||||||
as u16,
|
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,
|
|
||||||
};
|
};
|
||||||
Ok(provider.setup(unix_days_seconds as i64, ms_of_day))
|
Ok(provider.setup(unix_days_seconds as i64, ms_of_day))
|
||||||
}
|
}
|
||||||
@ -165,7 +168,6 @@ impl CdsShortTimeProvider {
|
|||||||
|
|
||||||
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,14 +195,13 @@ impl CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_date_time(&mut self, ms_since_last_second: u32) {
|
fn calc_date_time(&self, ms_since_last_second: u32) -> Option<DateTime<Utc>> {
|
||||||
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;
|
||||||
if let LocalResult::Single(val) = Utc.timestamp_opt(self.unix_seconds, ns_since_last_sec) {
|
if let LocalResult::Single(val) = Utc.timestamp_opt(self.unix_seconds, ns_since_last_sec) {
|
||||||
self.date_time = Some(val);
|
return Some(val);
|
||||||
} else {
|
|
||||||
self.date_time = None;
|
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +223,7 @@ impl CcsdsTimeProvider for CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn date_time(&self) -> Option<DateTime<Utc>> {
|
fn date_time(&self) -> Option<DateTime<Utc>> {
|
||||||
self.date_time
|
self.calc_date_time((self.ms_of_day % 1000) as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,14 +268,16 @@ impl TimeReader for CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "std"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::time::TimestampError::{InvalidTimeCode, OtherPacketError};
|
use crate::time::TimestampError::{InvalidTimeCode, OtherPacketError};
|
||||||
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
|
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, Timelike};
|
||||||
|
use postcard::from_bytes;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use postcard::to_allocvec;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_creation() {
|
fn test_creation() {
|
||||||
@ -282,7 +285,6 @@ mod tests {
|
|||||||
assert_eq!(ccsds_to_unix_days(0), DAYS_CCSDS_TO_UNIX);
|
assert_eq!(ccsds_to_unix_days(0), DAYS_CCSDS_TO_UNIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_current_time() {
|
fn test_get_current_time() {
|
||||||
let sec_floats = seconds_since_epoch();
|
let sec_floats = seconds_since_epoch();
|
||||||
@ -430,7 +432,6 @@ mod tests {
|
|||||||
assert_eq!(read_stamp.ms_of_day, u32::MAX - 1);
|
assert_eq!(read_stamp.ms_of_day, u32::MAX - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_now() {
|
fn test_time_now() {
|
||||||
let timestamp_now = CdsShortTimeProvider::from_now().unwrap();
|
let timestamp_now = CdsShortTimeProvider::from_now().unwrap();
|
||||||
@ -456,7 +457,17 @@ mod tests {
|
|||||||
generic_dt_property_equality_check(dt.minute(), compare_stamp.minute(), 0, 59);
|
generic_dt_property_equality_check(dt.minute(), compare_stamp.minute(), 0, 59);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn test_serialization() {
|
||||||
|
let stamp_now = CdsShortTimeProvider::from_now().expect("Error retrieving time");
|
||||||
|
let val = to_allocvec(&stamp_now).expect("Serializing timestamp failed");
|
||||||
|
assert!(val.len() > 0);
|
||||||
|
let stamp_deser: CdsShortTimeProvider =
|
||||||
|
from_bytes(&val).expect("Stamp deserialization failed");
|
||||||
|
assert_eq!(stamp_deser, stamp_now);
|
||||||
|
}
|
||||||
|
|
||||||
fn generic_dt_property_equality_check(first: u32, second: u32, start: u32, end: u32) {
|
fn generic_dt_property_equality_check(first: u32, second: u32, start: u32, end: u32) {
|
||||||
if second < first {
|
if second < first {
|
||||||
assert_eq!(second, start);
|
assert_eq!(second, start);
|
||||||
|
@ -22,7 +22,7 @@ pub const PUC_TM_MIN_SEC_HEADER_LEN: usize = 7;
|
|||||||
pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize =
|
pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize =
|
||||||
CCSDS_HEADER_LEN + PUC_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>();
|
CCSDS_HEADER_LEN + PUC_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>();
|
||||||
|
|
||||||
pub trait PusTmSecondaryHeaderT {
|
pub trait GenericPusTmSecondaryHeader {
|
||||||
fn pus_version(&self) -> PusVersion;
|
fn pus_version(&self) -> PusVersion;
|
||||||
fn sc_time_ref_status(&self) -> u8;
|
fn sc_time_ref_status(&self) -> u8;
|
||||||
fn service(&self) -> u8;
|
fn service(&self) -> u8;
|
||||||
@ -32,6 +32,7 @@ pub trait PusTmSecondaryHeaderT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod zc {
|
pub mod zc {
|
||||||
|
use super::GenericPusTmSecondaryHeader;
|
||||||
use crate::ecss::{PusError, PusVersion};
|
use crate::ecss::{PusError, PusVersion};
|
||||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ pub mod zc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::PusTmSecondaryHeaderT for PusTmSecHeaderWithoutTimestamp {
|
impl GenericPusTmSecondaryHeader for PusTmSecHeaderWithoutTimestamp {
|
||||||
fn pus_version(&self) -> PusVersion {
|
fn pus_version(&self) -> PusVersion {
|
||||||
PusVersion::try_from(self.pus_version_and_sc_time_ref_status >> 4 & 0b1111)
|
PusVersion::try_from(self.pus_version_and_sc_time_ref_status >> 4 & 0b1111)
|
||||||
.unwrap_or(PusVersion::Invalid)
|
.unwrap_or(PusVersion::Invalid)
|
||||||
@ -149,7 +150,7 @@ impl<'slice> PusTmSecondaryHeader<'slice> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusTmSecondaryHeaderT for PusTmSecondaryHeader<'_> {
|
impl GenericPusTmSecondaryHeader for PusTmSecondaryHeader<'_> {
|
||||||
fn pus_version(&self) -> PusVersion {
|
fn pus_version(&self) -> PusVersion {
|
||||||
self.pus_version
|
self.pus_version
|
||||||
}
|
}
|
||||||
@ -455,7 +456,7 @@ impl PusPacket for PusTm<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
//noinspection RsTraitImplementation
|
||||||
impl PusTmSecondaryHeaderT for PusTm<'_> {
|
impl GenericPusTmSecondaryHeader for PusTm<'_> {
|
||||||
delegate!(to self.sec_header {
|
delegate!(to self.sec_header {
|
||||||
fn pus_version(&self) -> PusVersion;
|
fn pus_version(&self) -> PusVersion;
|
||||||
fn service(&self) -> u8;
|
fn service(&self) -> u8;
|
||||||
|
Loading…
Reference in New Issue
Block a user