add PUS A support

This commit is contained in:
Robin Mueller
2025-07-29 20:26:37 +02:00
committed by Robin Mueller
parent 6e2c35e0c0
commit 3d344c11cc
11 changed files with 3385 additions and 56 deletions

View File

@@ -19,7 +19,9 @@ pub mod event;
pub mod hk;
pub mod scheduling;
pub mod tc;
pub mod tc_pus_a;
pub mod tm;
pub mod tm_pus_a;
pub mod verification;
pub type CrcType = u16;
@@ -80,18 +82,17 @@ pub enum PusVersion {
EsaPus = 0,
PusA = 1,
PusC = 2,
Invalid = 0b1111,
}
impl TryFrom<u8> for PusVersion {
type Error = ();
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
x if x == PusVersion::EsaPus as u8 => Ok(PusVersion::EsaPus),
x if x == PusVersion::PusA as u8 => Ok(PusVersion::PusA),
x if x == PusVersion::PusC as u8 => Ok(PusVersion::PusC),
_ => Err(()),
_ => Err(value),
}
}
}
@@ -154,7 +155,7 @@ pub enum PfcReal {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PusError {
#[error("PUS version {0:?} not supported")]
VersionNotSupported(PusVersion),
VersionNotSupported(u8),
#[error("checksum verification for crc16 {0:#06x} failed")]
ChecksumFailure(u16),
/// CRC16 needs to be calculated first
@@ -167,9 +168,7 @@ pub enum PusError {
/// Generic trait to describe common attributes for both PUS Telecommands (TC) and PUS Telemetry
/// (TM) packets. All PUS packets are also a special type of [CcsdsPacket]s.
pub trait PusPacket: CcsdsPacket {
const PUS_VERSION: PusVersion = PusVersion::PusC;
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
fn service(&self) -> u8;
fn subservice(&self) -> u8;
fn user_data(&self) -> &[u8];
@@ -369,6 +368,7 @@ generic_ecss_enum_typedefs_and_from_impls! {
/// byte representation. This is especially useful for generic abstractions which depend only
/// on the serialization of those packets.
pub trait WritablePusPacket {
/// The length here also includes the CRC length.
fn len_written(&self) -> usize;
/// Writes the packet to the given slice without writing the CRC.
@@ -536,9 +536,9 @@ mod tests {
#[test]
fn test_pus_error_display() {
let unsupport_version = PusError::VersionNotSupported(super::PusVersion::EsaPus);
let unsupport_version = PusError::VersionNotSupported(super::PusVersion::EsaPus as u8);
let write_str = unsupport_version.to_string();
assert_eq!(write_str, "PUS version EsaPus not supported")
assert_eq!(write_str, "PUS version 0 not supported")
}
#[test]
@@ -572,8 +572,8 @@ mod tests {
#[test]
fn test_pus_error_eq_impl() {
assert_eq!(
PusError::VersionNotSupported(PusVersion::EsaPus),
PusError::VersionNotSupported(PusVersion::EsaPus)
PusError::VersionNotSupported(PusVersion::EsaPus as u8),
PusError::VersionNotSupported(PusVersion::EsaPus as u8)
);
}

View File

@@ -79,7 +79,7 @@ pub const ACK_ALL: u8 = AckOpts::Acceptance as u8
| AckOpts::Completion as u8;
pub trait GenericPusTcSecondaryHeader {
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
fn ack_flags(&self) -> u8;
fn service(&self) -> u8;
fn subservice(&self) -> u8;
@@ -104,7 +104,7 @@ pub mod zc {
type Error = PusError;
fn try_from(value: crate::ecss::tc::PusTcSecondaryHeader) -> Result<Self, Self::Error> {
if value.version != PusVersion::PusC {
return Err(PusError::VersionNotSupported(value.version));
return Err(PusError::VersionNotSupported(value.version as u8));
}
Ok(PusTcSecondaryHeader {
version_ack: ((value.version as u8) << 4) | value.ack,
@@ -117,8 +117,8 @@ pub mod zc {
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
#[inline]
fn pus_version(&self) -> PusVersion {
PusVersion::try_from((self.version_ack >> 4) & 0b1111).unwrap_or(PusVersion::Invalid)
fn pus_version(&self) -> Result<PusVersion, u8> {
PusVersion::try_from((self.version_ack >> 4) & 0b1111)
}
#[inline]
@@ -156,8 +156,8 @@ pub struct PusTcSecondaryHeader {
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
#[inline]
fn pus_version(&self) -> PusVersion {
self.version
fn pus_version(&self) -> Result<PusVersion, u8> {
Ok(self.version)
}
#[inline]
@@ -444,7 +444,7 @@ impl CcsdsPacket for PusTcCreator<'_> {
impl PusPacket for PusTcCreator<'_> {
delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
#[inline]
fn service(&self) -> u8;
#[inline]
@@ -465,7 +465,7 @@ impl PusPacket for PusTcCreator<'_> {
impl GenericPusTcSecondaryHeader for PusTcCreator<'_> {
delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
#[inline]
fn service(&self) -> u8;
#[inline]
@@ -730,7 +730,7 @@ impl CcsdsPacket for PusTcReader<'_> {
impl PusPacket for PusTcReader<'_> {
delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
#[inline]
fn service(&self) -> u8;
#[inline]
@@ -751,7 +751,7 @@ impl PusPacket for PusTcReader<'_> {
impl GenericPusTcSecondaryHeader for PusTcReader<'_> {
delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
#[inline]
fn service(&self) -> u8;
#[inline]
@@ -1157,14 +1157,14 @@ mod tests {
assert_eq!(PusPacket::subservice(tc), 1);
assert_eq!(GenericPusTcSecondaryHeader::subservice(tc), 1);
assert!(tc.sec_header_flag());
assert_eq!(PusPacket::pus_version(tc), PusC);
assert_eq!(PusPacket::pus_version(tc).unwrap(), PusC);
assert_eq!(tc.seq_count(), 0x34);
assert_eq!(tc.source_id(), 0);
assert_eq!(tc.apid(), 0x02);
assert_eq!(tc.ack_flags(), ACK_ALL);
assert_eq!(PusPacket::pus_version(tc), PusVersion::PusC);
assert_eq!(PusPacket::pus_version(tc).unwrap(), PusVersion::PusC);
assert_eq!(
GenericPusTcSecondaryHeader::pus_version(tc),
GenericPusTcSecondaryHeader::pus_version(tc).unwrap(),
PusVersion::PusC
);
}

1353
src/ecss/tc_pus_a.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -75,7 +75,7 @@ pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize =
CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>();
pub trait GenericPusTmSecondaryHeader {
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
fn sc_time_ref_status(&self) -> u8;
fn service(&self) -> u8;
fn subservice(&self) -> u8;
@@ -107,7 +107,7 @@ pub mod zc {
type Error = PusError;
fn try_from(header: crate::ecss::tm::PusTmSecondaryHeader) -> Result<Self, Self::Error> {
if header.pus_version != PusVersion::PusC {
return Err(PusError::VersionNotSupported(header.pus_version));
return Err(PusError::VersionNotSupported(header.pus_version as u8));
}
Ok(PusTmSecHeaderWithoutTimestamp {
pus_version_and_sc_time_ref_status: ((header.pus_version as u8) << 4)
@@ -122,9 +122,8 @@ pub mod zc {
impl GenericPusTmSecondaryHeader for PusTmSecHeaderWithoutTimestamp {
#[inline]
fn pus_version(&self) -> PusVersion {
fn pus_version(&self) -> Result<PusVersion, u8> {
PusVersion::try_from((self.pus_version_and_sc_time_ref_status >> 4) & 0b1111)
.unwrap_or(PusVersion::Invalid)
}
#[inline]
@@ -201,8 +200,8 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
impl GenericPusTmSecondaryHeader for PusTmSecondaryHeader<'_> {
#[inline]
fn pus_version(&self) -> PusVersion {
self.pus_version
fn pus_version(&self) -> Result<PusVersion, u8> {
Ok(self.pus_version)
}
#[inline]
@@ -232,12 +231,16 @@ impl GenericPusTmSecondaryHeader for PusTmSecondaryHeader<'_> {
}
impl<'slice> TryFrom<zc::PusTmSecHeader<'slice>> for PusTmSecondaryHeader<'slice> {
type Error = ();
type Error = PusError;
#[inline]
fn try_from(sec_header: zc::PusTmSecHeader<'slice>) -> Result<Self, Self::Error> {
let version = sec_header.zc_header.pus_version();
if let Err(e) = version {
return Err(PusError::VersionNotSupported(e));
}
Ok(PusTmSecondaryHeader {
pus_version: sec_header.zc_header.pus_version(),
pus_version: version.unwrap(),
sc_time_ref_status: sec_header.zc_header.sc_time_ref_status(),
service: sec_header.zc_header.service(),
subservice: sec_header.zc_header.subservice(),
@@ -488,9 +491,12 @@ impl CcsdsPacket for PusTmCreator<'_, '_> {
}
impl PusPacket for PusTmCreator<'_, '_> {
#[inline]
fn pus_version(&self) -> Result<PusVersion, u8> {
Ok(self.sec_header.pus_version)
}
delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion;
#[inline]
fn service(&self) -> u8;
#[inline]
@@ -511,7 +517,7 @@ impl PusPacket for PusTmCreator<'_, '_> {
impl GenericPusTmSecondaryHeader for PusTmCreator<'_, '_> {
delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
#[inline]
fn service(&self) -> u8;
#[inline]
@@ -797,7 +803,7 @@ impl CcsdsPacket for PusTmReader<'_> {
impl PusPacket for PusTmReader<'_> {
delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
#[inline]
fn service(&self) -> u8;
#[inline]
@@ -818,7 +824,7 @@ impl PusPacket for PusTmReader<'_> {
impl GenericPusTmSecondaryHeader for PusTmReader<'_> {
delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
#[inline]
fn service(&self) -> u8;
#[inline]
@@ -977,7 +983,7 @@ impl CcsdsPacket for PusTmZeroCopyWriter<'_> {
impl PusPacket for PusTmZeroCopyWriter<'_> {
#[inline]
fn pus_version(&self) -> PusVersion {
fn pus_version(&self) -> Result<PusVersion, u8> {
self.sec_header_without_timestamp().pus_version()
}
@@ -1011,7 +1017,7 @@ impl GenericPusTmSecondaryHeader for PusTmZeroCopyWriter<'_> {
delegate! {
to self.sec_header_without_timestamp() {
#[inline]
fn pus_version(&self) -> PusVersion;
fn pus_version(&self) -> Result<PusVersion, u8>;
#[inline]
fn sc_time_ref_status(&self) -> u8;
#[inline]
@@ -1047,12 +1053,12 @@ mod tests {
const DUMMY_DATA: &[u8] = &[0, 1, 2];
fn base_ping_reply_full_ctor(timestamp: &[u8]) -> PusTmCreator {
fn base_ping_reply_full_ctor<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> {
let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
PusTmCreator::new_no_source_data(sph, tm_header, true)
}
fn ping_reply_with_data(timestamp: &[u8]) -> PusTmCreator {
fn ping_reply_with_data<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> {
let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
PusTmCreator::new(sph, tm_header, DUMMY_DATA, true)
@@ -1442,12 +1448,12 @@ mod tests {
if has_user_data {
assert!(!tm.user_data().is_empty());
}
assert_eq!(PusPacket::pus_version(tm), PusC);
assert_eq!(PusPacket::pus_version(tm).unwrap(), PusC);
assert_eq!(tm.apid(), 0x123);
assert_eq!(tm.seq_count(), 0x234);
assert_eq!(PusPacket::pus_version(tm), PusVersion::PusC);
assert_eq!(PusPacket::pus_version(tm).unwrap(), PusVersion::PusC);
assert_eq!(
GenericPusTmSecondaryHeader::pus_version(tm),
GenericPusTmSecondaryHeader::pus_version(tm).unwrap(),
PusVersion::PusC
);
assert_eq!(tm.data_len(), exp_full_len as u16 - 7);

1952
src/ecss/tm_pus_a.rs Normal file

File diff suppressed because it is too large Load Diff