Compare commits

..

15 Commits

Author SHA1 Message Date
de99bb926a Merge pull request 'small changelog tweak' (#128) from small-changelog-tweak into main
Reviewed-on: #128
2025-05-10 15:08:18 +02:00
167f53cac7
small changelog tweak 2025-05-10 15:07:58 +02:00
172227b843 Merge pull request 'update MSRV check' (#127) from update-msrv-check into main
Reviewed-on: #127
2025-05-10 15:04:21 +02:00
1bbca6866b
update MSRV check 2025-05-10 15:03:05 +02:00
b569208d45 Merge pull request 'prepare v0.14.0' (#126) from prepare-release into main
Reviewed-on: #126
2025-05-10 14:58:45 +02:00
d9709ffd6c
prepare v0.14.0 2025-05-10 14:54:27 +02:00
243dc64a78 Merge pull request 'remove badge' (#125) from remove-badge into main
Reviewed-on: #125
2025-05-10 14:38:23 +02:00
a6dc173f7f Merge branch 'main' into remove-badge 2025-05-10 14:38:19 +02:00
86dddbeef5
remove badge 2025-05-10 14:36:00 +02:00
17d112e838 Merge pull request 'one more test fix' (#124) from one-more-test-fix into main
Reviewed-on: #124
2025-05-10 14:31:53 +02:00
9c8467ccfe
one more test fix 2025-05-10 14:30:00 +02:00
217a8c2cc7 Merge pull request 'formatting' (#123) from formatting into main
Reviewed-on: #123
2025-05-10 14:26:27 +02:00
349e34bed6
formatting 2025-05-10 14:25:44 +02:00
d6a76ca360 Merge pull request 'CRC handling and dependency update' (#122) from msp430-tweak into main
Reviewed-on: #122
2025-05-10 14:24:54 +02:00
8f4351771b API variants which use table-less CRC 2025-05-10 13:58:10 +02:00
10 changed files with 327 additions and 120 deletions

View File

@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.81.0 - uses: dtolnay/rust-toolchain@1.70.0
- run: cargo check --release - run: cargo check --release
cross-check: cross-check:

View File

@ -8,6 +8,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.14.0] 2025-05-10
## Changed
- Moved CRC constants/implementations to dedicated `crc` module.
- `crc::CRC_CCITT_FALSE_NO_TABLE` and `crc::CRC_CCITT_FALSE_BIG_TABLE` variants.
- Renamed `PusPacket::crc16` to `PusPacket::opt_crc16`.
## Added
- `WritablePusPacket::write_to_bytes_crc_no_table` and `WritablePusPacket::write_to_bytes_no_crc`
variants.
- `PusTmReader::new_crc_no_table` and `PusTcReader::new_crc_no_table` variants.
- `crc16` methods for PUS TM and PUS TC reader.
- PUS TM and PUS TC reader now return the reader instance directly instead of a tuple of the reader
and the read size. The instance `total_len` method can be used to retrieve the read lenght.
# [v0.13.1] 2025-03-21 # [v0.13.1] 2025-03-21
- Bugfix due to operator precendence for `PusTcSecondaryHeader::pus_version`, - Bugfix due to operator precendence for `PusTcSecondaryHeader::pus_version`,
@ -568,6 +585,8 @@ The timestamp of `PusTm` is now optional. See Added and Changed section for deta
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.
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.14.0...HEAD
[v0.14.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.13.1...v0.14.0
[v0.13.1]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.13.0...v0.13.1 [v0.13.1]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.13.0...v0.13.1
[v0.13.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.12.0...v0.13.0 [v0.13.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.12.0...v0.13.0
[v0.12.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.11.2...v0.12.0 [v0.12.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.11.2...v0.12.0

View File

@ -1,8 +1,8 @@
[package] [package]
name = "spacepackets" name = "spacepackets"
version = "0.13.1" version = "0.14.0"
edition = "2021" edition = "2021"
rust-version = "1.81.0" rust-version = "1.70.0"
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"
homepage = "https://egit.irs.uni-stuttgart.de/rust/spacepackets" homepage = "https://egit.irs.uni-stuttgart.de/rust/spacepackets"
@ -13,45 +13,18 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
crc = "3" crc = "3.3"
delegate = ">=0.8, <=0.13" delegate = ">=0.8, <=0.13"
paste = "1" paste = "1"
zerocopy = { version = "0.8", features = ["derive"] }
thiserror = { version = "2", default-features = false }
num_enum = { version = ">0.5, <=0.7", default-features = false }
num-traits = { version = "0.2", default-features = false }
serde = { version = "1", optional = true, default-features = false, features = ["derive"] }
[dependencies.zerocopy] time = { version = "0.3", default-features = false, optional = true }
version = "0.8" chrono = { version = "0.4", default-features = false, optional = true }
features = ["derive"] defmt = { version = "1", default-features = false, optional = true }
[dependencies.thiserror]
version = "2"
default-features = false
[dependencies.num_enum]
version = ">0.5, <=0.7"
default-features = false
[dependencies.serde]
version = "1"
optional = true
default-features = false
features = ["derive"]
[dependencies.time]
version = "0.3"
default-features = false
optional = true
[dependencies.chrono]
version = "0.4"
default-features = false
optional = true
[dependencies.num-traits]
version = "0.2"
default-features = false
[dependencies.defmt]
version = "0.3"
optional = true
[features] [features]
default = ["std"] default = ["std"]

View File

@ -1,7 +1,6 @@
[![Crates.io](https://img.shields.io/crates/v/spacepackets)](https://crates.io/crates/spacepackets) [![Crates.io](https://img.shields.io/crates/v/spacepackets)](https://crates.io/crates/spacepackets)
[![docs.rs](https://img.shields.io/docsrs/spacepackets)](https://docs.rs/spacepackets) [![docs.rs](https://img.shields.io/docsrs/spacepackets)](https://docs.rs/spacepackets)
[![ci](https://github.com/us-irs/spacepackets-rs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/us-irs/spacepackets-rs/actions/workflows/ci.yml) [![ci](https://github.com/us-irs/spacepackets-rs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/us-irs/spacepackets-rs/actions/workflows/ci.yml)
[![coverage](https://shields.io/endpoint?url=https://absatsw.irs.uni-stuttgart.de/projects/spacepackets/coverage-rs/latest/coverage.json)](https://absatsw.irs.uni-stuttgart.de/projects/spacepackets/coverage-rs/latest/index.html)
ECSS and CCSDS Spacepackets ECSS and CCSDS Spacepackets
====== ======

View File

@ -1,8 +1,8 @@
//! CFDP Packet Data Unit (PDU) support. //! CFDP Packet Data Unit (PDU) support.
use crate::cfdp::*; use crate::cfdp::*;
use crate::crc::CRC_CCITT_FALSE;
use crate::util::{UnsignedByteField, UnsignedByteFieldU8, UnsignedEnum}; use crate::util::{UnsignedByteField, UnsignedByteFieldU8, UnsignedEnum};
use crate::ByteConversionError; use crate::ByteConversionError;
use crate::CRC_CCITT_FALSE;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::vec::Vec; use alloc::vec::Vec;

11
src/crc.rs Normal file
View File

@ -0,0 +1,11 @@
/// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard, using
/// a [crc::NoTable] as the CRC implementation.
pub const CRC_CCITT_FALSE_NO_TABLE: crc::Crc<u16, crc::NoTable> =
crc::Crc::<u16, crc::NoTable>::new(&crc::CRC_16_IBM_3740);
/// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard, using
/// [crc::Table<1>] as the CRC implementation.
pub const CRC_CCITT_FALSE: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_3740);
/// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard, using
/// a [crc::Table<16>] large table as the CRC implementation.
pub const CRC_CCITT_FALSE_BIG_TABLE: crc::Crc<u16, crc::Table<16>> =
crc::Crc::<u16, crc::Table<16>>::new(&crc::CRC_16_IBM_3740);

View File

@ -3,7 +3,10 @@
//! //!
//! You can find the PUS telecommand types in the [tc] module and the the PUS telemetry //! You can find the PUS telecommand types in the [tc] module and the the PUS telemetry
//! types inside the [tm] module. //! types inside the [tm] module.
use crate::{ByteConversionError, CcsdsPacket, CRC_CCITT_FALSE}; use crate::{
crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE},
ByteConversionError, CcsdsPacket,
};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::vec::Vec; use alloc::vec::Vec;
use core::fmt::Debug; use core::fmt::Debug;
@ -170,7 +173,7 @@ pub trait PusPacket: CcsdsPacket {
fn service(&self) -> u8; fn service(&self) -> u8;
fn subservice(&self) -> u8; fn subservice(&self) -> u8;
fn user_data(&self) -> &[u8]; fn user_data(&self) -> &[u8];
fn crc16(&self) -> Option<u16>; fn opt_crc16(&self) -> Option<u16>;
} }
pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, ByteConversionError> { pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, ByteConversionError> {
@ -207,7 +210,7 @@ pub(crate) fn user_data_from_raw(
} }
} }
pub(crate) fn verify_crc16_ccitt_false_from_raw_to_pus_error( pub fn verify_crc16_ccitt_false_from_raw_to_pus_error(
raw_data: &[u8], raw_data: &[u8],
crc16: u16, crc16: u16,
) -> Result<(), PusError> { ) -> Result<(), PusError> {
@ -216,6 +219,15 @@ pub(crate) fn verify_crc16_ccitt_false_from_raw_to_pus_error(
.ok_or(PusError::ChecksumFailure(crc16)) .ok_or(PusError::ChecksumFailure(crc16))
} }
pub fn verify_crc16_ccitt_false_from_raw_to_pus_error_no_table(
raw_data: &[u8],
crc16: u16,
) -> Result<(), PusError> {
verify_crc16_ccitt_false_from_raw_no_table(raw_data)
.then_some(())
.ok_or(PusError::ChecksumFailure(crc16))
}
pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool { pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool {
let mut digest = CRC_CCITT_FALSE.digest(); let mut digest = CRC_CCITT_FALSE.digest();
digest.update(raw_data); digest.update(raw_data);
@ -225,6 +237,15 @@ pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool {
false false
} }
pub(crate) fn verify_crc16_ccitt_false_from_raw_no_table(raw_data: &[u8]) -> bool {
let mut digest = CRC_CCITT_FALSE_NO_TABLE.digest();
digest.update(raw_data);
if digest.finalize() == 0 {
return true;
}
false
}
macro_rules! ccsds_impl { macro_rules! ccsds_impl {
() => { () => {
delegate!(to self.sp_header { delegate!(to self.sp_header {
@ -349,7 +370,34 @@ generic_ecss_enum_typedefs_and_from_impls! {
/// on the serialization of those packets. /// on the serialization of those packets.
pub trait WritablePusPacket { pub trait WritablePusPacket {
fn len_written(&self) -> usize; fn len_written(&self) -> usize;
fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError>;
/// Writes the packet to the given slice without writing the CRC.
///
/// The returned size is the written size WITHOUT the CRC.
fn write_to_bytes_no_crc(&self, slice: &mut [u8]) -> Result<usize, PusError>;
/// First uses [Self::write_to_bytes_no_crc] to write the packet to the given slice and then
/// uses the [CRC_CCITT_FALS] to calculate the CRC and write it to the slice.
fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
let mut curr_idx = self.write_to_bytes_no_crc(slice)?;
let mut digest = CRC_CCITT_FALSE.digest();
digest.update(&slice[0..curr_idx]);
slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
curr_idx += 2;
Ok(curr_idx)
}
/// First uses [Self::write_to_bytes_no_crc] to write the packet to the given slice and then
/// uses the [CRC_CCITT_FALSE_NO_TABLE] to calculate the CRC and write it to the slice.
fn write_to_bytes_crc_no_table(&self, slice: &mut [u8]) -> Result<usize, PusError> {
let mut curr_idx = self.write_to_bytes_no_crc(slice)?;
let mut digest = CRC_CCITT_FALSE_NO_TABLE.digest();
digest.update(&slice[0..curr_idx]);
slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
curr_idx += 2;
Ok(curr_idx)
}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<Vec<u8>, PusError> { fn to_vec(&self) -> Result<Vec<u8>, PusError> {
// This is the correct way to do this. See // This is the correct way to do this. See

View File

@ -33,13 +33,14 @@
//! assert_eq!(pus_tc.subservice(), 1); //! assert_eq!(pus_tc.subservice(), 1);
//! assert_eq!(pus_tc.apid(), 0x02); //! assert_eq!(pus_tc.apid(), 0x02);
//! ``` //! ```
use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE};
use crate::ecss::{ use crate::ecss::{
ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw, ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw,
verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion, verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion,
WritablePusPacket, WritablePusPacket,
}; };
use crate::SpHeader;
use crate::{ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, CCSDS_HEADER_LEN}; use crate::{ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, CCSDS_HEADER_LEN};
use crate::{SpHeader, CRC_CCITT_FALSE};
use core::mem::size_of; use core::mem::size_of;
use delegate::delegate; use delegate::delegate;
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
@ -50,6 +51,8 @@ use zerocopy::{FromBytes, IntoBytes};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::vec::Vec; use alloc::vec::Vec;
use super::verify_crc16_ccitt_false_from_raw_to_pus_error_no_table;
/// PUS C secondary header length is fixed /// PUS C secondary header length is fixed
pub const PUC_TC_SECONDARY_HEADER_LEN: usize = size_of::<zc::PusTcSecondaryHeader>(); pub const PUC_TC_SECONDARY_HEADER_LEN: usize = size_of::<zc::PusTcSecondaryHeader>();
pub const PUS_TC_MIN_LEN_WITHOUT_APP_DATA: usize = pub const PUS_TC_MIN_LEN_WITHOUT_APP_DATA: usize =
@ -239,13 +242,13 @@ impl<'app_data> PusTcCreator<'app_data> {
/// # Arguments /// # Arguments
/// ///
/// * `sp_header` - Space packet header information. The correct packet type and the secondary /// * `sp_header` - Space packet header information. The correct packet type and the secondary
/// header flag are set correctly by the constructor. /// header flag are set correctly by the constructor.
/// * `sec_header` - Information contained in the data field header, including the service /// * `sec_header` - Information contained in the data field header, including the service
/// and subservice type /// and subservice type
/// * `app_data` - Custom application data /// * `app_data` - Custom application data
/// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length /// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length
/// field. If this is not set to true, [Self::update_ccsds_data_len] can be called to set /// field. If this is not set to true, [Self::update_ccsds_data_len] can be called to set
/// the correct value to this field manually /// the correct value to this field manually
#[inline] #[inline]
pub fn new( pub fn new(
mut sp_header: SpHeader, mut sp_header: SpHeader,
@ -342,6 +345,17 @@ impl<'app_data> PusTcCreator<'app_data> {
digest.finalize() digest.finalize()
} }
/// This function calculates and returns the CRC16 for the current packet.
pub fn calc_own_crc16_no_table(&self) -> u16 {
let mut digest = CRC_CCITT_FALSE_NO_TABLE.digest();
let sph_zc = crate::zc::SpHeader::from(self.sp_header);
digest.update(sph_zc.as_bytes());
let pus_tc_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap();
digest.update(pus_tc_header.as_bytes());
digest.update(self.app_data);
digest.finalize()
}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize { pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize {
let sph_zc = crate::zc::SpHeader::from(self.sp_header); let sph_zc = crate::zc::SpHeader::from(self.sp_header);
@ -366,8 +380,10 @@ impl WritablePusPacket for PusTcCreator<'_> {
PUS_TC_MIN_LEN_WITHOUT_APP_DATA + self.app_data.len() PUS_TC_MIN_LEN_WITHOUT_APP_DATA + self.app_data.len()
} }
/// Write the raw PUS byte representation to a provided buffer. /// Writes the packet to the given slice without writing the CRC.
fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> { ///
/// The returned size is the written size WITHOUT the CRC.
fn write_to_bytes_no_crc(&self, slice: &mut [u8]) -> Result<usize, PusError> {
let mut curr_idx = 0; let mut curr_idx = 0;
let tc_header_len = size_of::<zc::PusTcSecondaryHeader>(); let tc_header_len = size_of::<zc::PusTcSecondaryHeader>();
let total_size = self.len_written(); let total_size = self.len_written();
@ -388,10 +404,6 @@ impl WritablePusPacket for PusTcCreator<'_> {
curr_idx += tc_header_len; curr_idx += tc_header_len;
slice[curr_idx..curr_idx + self.app_data.len()].copy_from_slice(self.app_data); slice[curr_idx..curr_idx + self.app_data.len()].copy_from_slice(self.app_data);
curr_idx += self.app_data.len(); curr_idx += self.app_data.len();
let mut digest = CRC_CCITT_FALSE.digest();
digest.update(&slice[0..curr_idx]);
slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
curr_idx += 2;
Ok(curr_idx) Ok(curr_idx)
} }
} }
@ -416,7 +428,7 @@ impl PusPacket for PusTcCreator<'_> {
} }
#[inline] #[inline]
fn crc16(&self) -> Option<u16> { fn opt_crc16(&self) -> Option<u16> {
Some(self.calc_own_crc16()) Some(self.calc_own_crc16())
} }
} }
@ -465,7 +477,22 @@ impl<'raw_data> PusTcReader<'raw_data> {
/// Create a [PusTcReader] instance from a raw slice. On success, it returns a tuple containing /// Create a [PusTcReader] instance from a raw slice. On success, it returns a tuple containing
/// the instance and the found byte length of the packet. This function also performs a CRC /// the instance and the found byte length of the packet. This function also performs a CRC
/// check and will return an appropriate [PusError] if the check fails. /// check and will return an appropriate [PusError] if the check fails.
pub fn new(slice: &'raw_data [u8]) -> Result<(Self, usize), PusError> { pub fn new(slice: &'raw_data [u8]) -> Result<Self, PusError> {
let pus_tc = Self::new_no_crc_check(slice)?;
verify_crc16_ccitt_false_from_raw_to_pus_error(pus_tc.raw_data(), pus_tc.crc16())?;
Ok(pus_tc)
}
/// Similar to [PusTcReader::new], but uses a table-less CRC16 algorithm which can reduce
/// binary size and memory usage.
pub fn new_crc_no_table(slice: &'raw_data [u8]) -> Result<Self, PusError> {
let pus_tc = Self::new_no_crc_check(slice)?;
verify_crc16_ccitt_false_from_raw_to_pus_error_no_table(pus_tc.raw_data(), pus_tc.crc16())?;
Ok(pus_tc)
}
/// Creates a new instance without performing a CRC check.
pub fn new_no_crc_check(slice: &'raw_data [u8]) -> Result<Self, 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(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {
@ -498,15 +525,13 @@ impl<'raw_data> PusTcReader<'raw_data> {
.map_err(|_| ByteConversionError::ZeroCopyFromError)?; .map_err(|_| 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 = Self { Ok(Self {
sp_header, sp_header,
sec_header: PusTcSecondaryHeader::try_from(sec_header).unwrap(), sec_header: PusTcSecondaryHeader::try_from(sec_header).unwrap(),
raw_data, raw_data,
app_data: user_data_from_raw(current_idx, total_len, slice)?, app_data: user_data_from_raw(current_idx, total_len, slice)?,
crc16: crc_from_raw_data(raw_data)?, crc16: crc_from_raw_data(raw_data)?,
}; })
verify_crc16_ccitt_false_from_raw_to_pus_error(raw_data, pus_tc.crc16)?;
Ok((pus_tc, total_len))
} }
#[inline] #[inline]
@ -528,6 +553,11 @@ impl<'raw_data> PusTcReader<'raw_data> {
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
#[inline]
pub fn crc16(&self) -> u16 {
self.crc16
}
} }
impl PartialEq for PusTcReader<'_> { impl PartialEq for PusTcReader<'_> {
@ -557,7 +587,7 @@ impl PusPacket for PusTcReader<'_> {
} }
#[inline] #[inline]
fn crc16(&self) -> Option<u16> { fn opt_crc16(&self) -> Option<u16> {
Some(self.crc16) Some(self.crc16)
} }
} }
@ -640,11 +670,37 @@ mod tests {
.expect("Error writing TC to buffer"); .expect("Error writing TC to buffer");
assert_eq!(size, 13); assert_eq!(size, 13);
assert_eq!( assert_eq!(
pus_tc.crc16().unwrap(), pus_tc.opt_crc16().unwrap(),
u16::from_be_bytes(test_buf[size - 2..size].try_into().unwrap()) u16::from_be_bytes(test_buf[size - 2..size].try_into().unwrap())
); );
} }
#[test]
fn test_serialization_crc_no_table() {
let pus_tc = base_ping_tc_simple_ctor();
let mut test_buf: [u8; 32] = [0; 32];
let size = pus_tc
.write_to_bytes_crc_no_table(test_buf.as_mut_slice())
.expect("error writing tc to buffer");
assert_eq!(size, 13);
assert_eq!(
pus_tc.opt_crc16().unwrap(),
u16::from_be_bytes(test_buf[size - 2..size].try_into().unwrap())
);
}
#[test]
fn test_serialization_no_crc() {
let pus_tc = base_ping_tc_simple_ctor();
let mut test_buf: [u8; 32] = [0; 32];
let size = pus_tc
.write_to_bytes_no_crc(test_buf.as_mut_slice())
.expect("error writing tc to buffer");
assert_eq!(size, 11);
assert_eq!(test_buf[11], 0);
assert_eq!(test_buf[12], 0);
}
#[test] #[test]
fn test_deserialization() { fn test_deserialization() {
let pus_tc = base_ping_tc_simple_ctor(); let pus_tc = base_ping_tc_simple_ctor();
@ -653,9 +709,26 @@ mod tests {
.write_to_bytes(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) = let tc_from_raw =
PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed"); PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
assert_eq!(tc_from_raw.total_len(), 13);
verify_test_tc_with_reader(&tc_from_raw, false, 13);
assert!(tc_from_raw.user_data().is_empty());
verify_test_tc_raw(&test_buf);
verify_crc_no_app_data(&test_buf);
}
#[test]
fn test_deserialization_no_table() {
let pus_tc = base_ping_tc_simple_ctor();
let mut test_buf: [u8; 32] = [0; 32];
let size = pus_tc
.write_to_bytes(test_buf.as_mut_slice())
.expect("Error writing TC to buffer");
assert_eq!(size, 13); assert_eq!(size, 13);
let tc_from_raw = PusTcReader::new_crc_no_table(&test_buf)
.expect("Creating PUS TC struct from raw buffer failed");
assert_eq!(tc_from_raw.total_len(), 13);
verify_test_tc_with_reader(&tc_from_raw, false, 13); verify_test_tc_with_reader(&tc_from_raw, false, 13);
assert!(tc_from_raw.user_data().is_empty()); assert!(tc_from_raw.user_data().is_empty());
verify_test_tc_raw(&test_buf); verify_test_tc_raw(&test_buf);
@ -667,9 +740,9 @@ mod tests {
let pus_tc = base_ping_tc_simple_ctor(); let pus_tc = base_ping_tc_simple_ctor();
let tc_vec = pus_tc.to_vec().expect("Error writing TC to buffer"); let tc_vec = pus_tc.to_vec().expect("Error writing TC to buffer");
assert_eq!(tc_vec.len(), 13); assert_eq!(tc_vec.len(), 13);
let (tc_from_raw, size) = PusTcReader::new(tc_vec.as_slice()) let tc_from_raw = PusTcReader::new(tc_vec.as_slice())
.expect("Creating PUS TC struct from raw buffer failed"); .expect("Creating PUS TC struct from raw buffer failed");
assert_eq!(size, 13); assert_eq!(tc_from_raw.total_len(), 13);
verify_test_tc_with_reader(&tc_from_raw, false, 13); verify_test_tc_with_reader(&tc_from_raw, false, 13);
assert!(tc_from_raw.user_data().is_empty()); assert!(tc_from_raw.user_data().is_empty());
verify_test_tc_raw(&tc_vec); verify_test_tc_raw(&tc_vec);
@ -692,15 +765,15 @@ mod tests {
.write_to_bytes(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) = let tc_from_raw =
PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed"); PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
assert_eq!(size, 16); assert_eq!(tc_from_raw.total_len(), 16);
verify_test_tc_with_reader(&tc_from_raw, true, 16); verify_test_tc_with_reader(&tc_from_raw, true, 16);
let user_data = tc_from_raw.user_data(); let user_data = tc_from_raw.user_data();
assert_eq!(tc_from_raw.user_data(), tc_from_raw.app_data()); assert_eq!(tc_from_raw.user_data(), tc_from_raw.app_data());
assert_eq!(tc_from_raw.raw_data(), &test_buf[..size]); assert_eq!(tc_from_raw.raw_data(), &test_buf[..size]);
assert_eq!( assert_eq!(
tc_from_raw.crc16().unwrap(), tc_from_raw.opt_crc16().unwrap(),
u16::from_be_bytes(test_buf[size - 2..size].try_into().unwrap()) u16::from_be_bytes(test_buf[size - 2..size].try_into().unwrap())
); );
assert_eq!(user_data[0], 1); assert_eq!(user_data[0], 1);
@ -715,9 +788,9 @@ mod tests {
pus_tc pus_tc
.write_to_bytes(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");
let (tc_from_raw_0, _) = let tc_from_raw_0 =
PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed"); PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
let (tc_from_raw_1, _) = let tc_from_raw_1 =
PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed"); PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
assert_eq!(tc_from_raw_0, tc_from_raw_1); assert_eq!(tc_from_raw_0, tc_from_raw_1);
} }
@ -869,7 +942,7 @@ mod tests {
assert_eq!(*tc.sp_header(), comp_header); assert_eq!(*tc.sp_header(), comp_header);
} }
fn verify_test_tc_generic(tc: &(impl CcsdsPacket + PusPacket + GenericPusTcSecondaryHeader)) { fn verify_test_tc_generic(tc: &(impl PusPacket + GenericPusTcSecondaryHeader)) {
assert_eq!(PusPacket::service(tc), 17); assert_eq!(PusPacket::service(tc), 17);
assert_eq!(GenericPusTcSecondaryHeader::service(tc), 17); assert_eq!(GenericPusTcSecondaryHeader::service(tc), 17);
assert_eq!(PusPacket::subservice(tc), 1); assert_eq!(PusPacket::subservice(tc), 1);
@ -933,8 +1006,8 @@ mod tests {
let pus_tc = base_ping_tc_simple_ctor(); let pus_tc = base_ping_tc_simple_ctor();
let mut buf = [0; 32]; let mut buf = [0; 32];
pus_tc.write_to_bytes(&mut buf).unwrap(); pus_tc.write_to_bytes(&mut buf).unwrap();
assert_eq!(pus_tc, PusTcReader::new(&buf).unwrap().0); assert_eq!(pus_tc, PusTcReader::new(&buf).unwrap());
assert_eq!(PusTcReader::new(&buf).unwrap().0, pus_tc); assert_eq!(PusTcReader::new(&buf).unwrap(), pus_tc);
} }
#[test] #[test]

View File

@ -35,13 +35,14 @@
//! println!("{:?}", &test_buf[0..written_size]); //! println!("{:?}", &test_buf[0..written_size]);
//! //!
//! // Deserialize from the raw byte representation //! // Deserialize from the raw byte representation
//! let (ping_tm_reader, read_size) = PusTmReader::new(&test_buf, 7).expect("Deserialization failed"); //! let ping_tm_reader = PusTmReader::new(&test_buf, 7).expect("Deserialization failed");
//! assert_eq!(written_size, read_size); //! assert_eq!(written_size, ping_tm_reader.total_len());
//! assert_eq!(ping_tm_reader.service(), 17); //! assert_eq!(ping_tm_reader.service(), 17);
//! assert_eq!(ping_tm_reader.subservice(), 2); //! assert_eq!(ping_tm_reader.subservice(), 2);
//! assert_eq!(ping_tm_reader.apid(), 0x02); //! assert_eq!(ping_tm_reader.apid(), 0x02);
//! assert_eq!(ping_tm_reader.timestamp(), &time_buf); //! assert_eq!(ping_tm_reader.timestamp(), &time_buf);
//! ``` //! ```
use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE};
use crate::ecss::{ use crate::ecss::{
calc_pus_crc16, ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw, calc_pus_crc16, ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw,
verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion, verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion,
@ -49,7 +50,7 @@ use crate::ecss::{
}; };
use crate::{ use crate::{
ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SpHeader, CCSDS_HEADER_LEN, ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SpHeader, CCSDS_HEADER_LEN,
CRC_CCITT_FALSE, MAX_APID, MAX_SEQ_COUNT, MAX_APID, MAX_SEQ_COUNT,
}; };
use core::mem::size_of; use core::mem::size_of;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -64,6 +65,8 @@ use crate::time::{TimeWriter, TimestampError};
use self::zc::PusTmSecHeaderWithoutTimestamp; use self::zc::PusTmSecHeaderWithoutTimestamp;
use super::verify_crc16_ccitt_false_from_raw_to_pus_error_no_table;
pub trait IsPusTelemetry {} pub trait IsPusTelemetry {}
/// Length without timestamp /// Length without timestamp
@ -277,13 +280,13 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
/// # Arguments /// # Arguments
/// ///
/// * `sp_header` - Space packet header information. The correct packet type and the secondary /// * `sp_header` - Space packet header information. The correct packet type and the secondary
/// header flag are set correctly by the constructor. /// header flag are set correctly by the constructor.
/// * `sec_header` - Information contained in the secondary header, including the service /// * `sec_header` - Information contained in the secondary header, including the service
/// and subservice type /// and subservice type
/// * `source_data` - Custom application data /// * `source_data` - Custom application data
/// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length /// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length
/// field. If this is not set to true, [Self::update_ccsds_data_len] can be called to set /// field. If this is not set to true, [Self::update_ccsds_data_len] can be called to set
/// the correct value to this field manually /// the correct value to this field manually
#[inline] #[inline]
pub fn new( pub fn new(
mut sp_header: SpHeader, mut sp_header: SpHeader,
@ -389,6 +392,29 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
/// Write the raw PUS byte representation to a provided buffer. /// Write the raw PUS byte representation to a provided buffer.
pub fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, ByteConversionError> { pub fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, ByteConversionError> {
let mut curr_idx = self.write_to_bytes_no_crc(slice)?;
let mut digest = CRC_CCITT_FALSE.digest();
digest.update(&slice[0..curr_idx]);
slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
curr_idx += 2;
Ok(curr_idx)
}
/// Write the raw PUS byte representation to a provided buffer.
pub fn write_to_bytes_crc_no_table(
&self,
slice: &mut [u8],
) -> Result<usize, ByteConversionError> {
let mut curr_idx = self.write_to_bytes_no_crc(slice)?;
let mut digest = CRC_CCITT_FALSE_NO_TABLE.digest();
digest.update(&slice[0..curr_idx]);
slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
curr_idx += 2;
Ok(curr_idx)
}
/// Write the raw PUS byte representation to a provided buffer.
pub fn write_to_bytes_no_crc(&self, slice: &mut [u8]) -> Result<usize, ByteConversionError> {
let mut curr_idx = 0; let mut curr_idx = 0;
let total_size = self.len_written(); let total_size = self.len_written();
if total_size > slice.len() { if total_size > slice.len() {
@ -411,10 +437,6 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
curr_idx += self.sec_header.timestamp.len(); curr_idx += self.sec_header.timestamp.len();
slice[curr_idx..curr_idx + self.source_data.len()].copy_from_slice(self.source_data); slice[curr_idx..curr_idx + self.source_data.len()].copy_from_slice(self.source_data);
curr_idx += self.source_data.len(); curr_idx += self.source_data.len();
let mut digest = CRC_CCITT_FALSE.digest();
digest.update(&slice[0..curr_idx]);
slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
curr_idx += 2;
Ok(curr_idx) Ok(curr_idx)
} }
@ -446,8 +468,8 @@ impl WritablePusPacket for PusTmCreator<'_, '_> {
+ self.source_data.len() + self.source_data.len()
} }
/// Write the raw PUS byte representation to a provided buffer. /// Write the raw PUS byte representation to a provided buffer.
fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> { fn write_to_bytes_no_crc(&self, slice: &mut [u8]) -> Result<usize, PusError> {
Ok(Self::write_to_bytes(self, slice)?) Ok(Self::write_to_bytes_no_crc(self, slice)?)
} }
} }
@ -479,7 +501,7 @@ impl PusPacket for PusTmCreator<'_, '_> {
} }
#[inline] #[inline]
fn crc16(&self) -> Option<u16> { fn opt_crc16(&self) -> Option<u16> {
Some(self.calc_own_crc16()) Some(self.calc_own_crc16())
} }
} }
@ -534,7 +556,26 @@ impl<'raw_data> PusTmReader<'raw_data> {
/// ///
/// This function will check the CRC-16 of the PUS packet and will return an appropriate /// This function will check the CRC-16 of the PUS packet and will return an appropriate
/// [PusError] if the check fails. /// [PusError] if the check fails.
pub fn new(slice: &'raw_data [u8], timestamp_len: usize) -> Result<(Self, usize), PusError> { pub fn new(slice: &'raw_data [u8], timestamp_len: usize) -> Result<Self, PusError> {
let tc = Self::new_no_crc_check(slice, timestamp_len)?;
verify_crc16_ccitt_false_from_raw_to_pus_error(tc.raw_data(), tc.crc16)?;
Ok(tc)
}
/// Like [PusTmReader::new] but uses a table-less CRC implementation.
pub fn new_crc_no_table(
slice: &'raw_data [u8],
timestamp_len: usize,
) -> Result<Self, PusError> {
let tc = Self::new_no_crc_check(slice, timestamp_len)?;
verify_crc16_ccitt_false_from_raw_to_pus_error_no_table(tc.raw_data(), tc.crc16)?;
Ok(tc)
}
pub fn new_no_crc_check(
slice: &'raw_data [u8],
timestamp_len: usize,
) -> Result<Self, PusError> {
let raw_data_len = slice.len(); let raw_data_len = slice.len();
if raw_data_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA { if raw_data_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA {
return Err(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {
@ -572,15 +613,13 @@ impl<'raw_data> PusTmReader<'raw_data> {
}; };
current_idx += timestamp_len; current_idx += timestamp_len;
let raw_data = &slice[0..total_len]; let raw_data = &slice[0..total_len];
let pus_tm = Self { Ok(Self {
sp_header, sp_header,
sec_header: PusTmSecondaryHeader::try_from(zc_sec_header_wrapper).unwrap(), sec_header: PusTmSecondaryHeader::try_from(zc_sec_header_wrapper).unwrap(),
raw_data: &slice[0..total_len], raw_data: &slice[0..total_len],
source_data: user_data_from_raw(current_idx, total_len, slice)?, source_data: user_data_from_raw(current_idx, total_len, slice)?,
crc16: crc_from_raw_data(raw_data)?, crc16: crc_from_raw_data(raw_data)?,
}; })
verify_crc16_ccitt_false_from_raw_to_pus_error(raw_data, pus_tm.crc16)?;
Ok((pus_tm, total_len))
} }
#[inline] #[inline]
@ -598,6 +637,11 @@ impl<'raw_data> PusTmReader<'raw_data> {
self.sec_header.timestamp self.sec_header.timestamp
} }
#[inline]
pub fn crc16(&self) -> u16 {
self.crc16
}
/// This function will return the slice [Self] was constructed from. /// This function will return the slice [Self] was constructed from.
#[inline] #[inline]
pub fn raw_data(&self) -> &[u8] { pub fn raw_data(&self) -> &[u8] {
@ -634,8 +678,8 @@ impl PusPacket for PusTmReader<'_> {
} }
#[inline] #[inline]
fn crc16(&self) -> Option<u16> { fn opt_crc16(&self) -> Option<u16> {
Some(self.crc16) Some(self.crc16())
} }
} }
@ -822,7 +866,7 @@ impl PusPacket for PusTmZeroCopyWriter<'_> {
} }
#[inline] #[inline]
fn crc16(&self) -> Option<u16> { fn opt_crc16(&self) -> Option<u16> {
Some(u16::from_be_bytes( Some(u16::from_be_bytes(
self.raw_tm[self.sp_header().total_len() - 2..self.sp_header().total_len()] self.raw_tm[self.sp_header().total_len() - 2..self.sp_header().total_len()]
.try_into() .try_into()
@ -918,7 +962,32 @@ mod tests {
.write_to_bytes(&mut buf) .write_to_bytes(&mut buf)
.expect("Serialization failed"); .expect("Serialization failed");
assert_eq!(ser_len, 22); assert_eq!(ser_len, 22);
verify_raw_ping_reply(pus_tm.crc16().unwrap(), &buf); verify_raw_ping_reply(pus_tm.opt_crc16().unwrap(), &buf);
}
#[test]
fn test_serialization_no_source_data_no_table() {
let timestamp = dummy_timestamp();
let pus_tm = base_ping_reply_full_ctor(timestamp);
let mut buf: [u8; 32] = [0; 32];
let ser_len = pus_tm
.write_to_bytes_crc_no_table(&mut buf)
.expect("Serialization failed");
assert_eq!(ser_len, 22);
verify_raw_ping_reply(pus_tm.opt_crc16().unwrap(), &buf);
}
#[test]
fn test_serialization_no_source_data_no_crc() {
let timestamp = dummy_timestamp();
let pus_tm = base_ping_reply_full_ctor(timestamp);
let mut buf: [u8; 32] = [0; 32];
let ser_len = pus_tm
.write_to_bytes_no_crc(&mut buf)
.expect("Serialization failed");
assert_eq!(ser_len, 20);
assert_eq!(buf[20], 0);
assert_eq!(buf[21], 0);
} }
#[test] #[test]
@ -955,9 +1024,9 @@ mod tests {
let pus_tm = base_ping_reply_full_ctor(timestamp); let pus_tm = base_ping_reply_full_ctor(timestamp);
let tm_vec = pus_tm.to_vec().expect("Serialization failed"); let tm_vec = pus_tm.to_vec().expect("Serialization failed");
assert_eq!(tm_vec.len(), 22); assert_eq!(tm_vec.len(), 22);
let (tm_deserialized, size) = let tm_deserialized =
PusTmReader::new(tm_vec.as_slice(), 7).expect("Deserialization failed"); PusTmReader::new(tm_vec.as_slice(), 7).expect("Deserialization failed");
assert_eq!(tm_vec.len(), size); assert_eq!(tm_vec.len(), tm_deserialized.total_len());
verify_ping_reply_with_reader(&tm_deserialized, false, 22, dummy_timestamp()); verify_ping_reply_with_reader(&tm_deserialized, false, 22, dummy_timestamp());
} }
#[test] #[test]
@ -969,13 +1038,32 @@ mod tests {
.write_to_bytes(&mut buf) .write_to_bytes(&mut buf)
.expect("Serialization failed"); .expect("Serialization failed");
assert_eq!(ser_len, 22); assert_eq!(ser_len, 22);
let (tm_deserialized, size) = PusTmReader::new(&buf, 7).expect("Deserialization failed"); let tm_deserialized = PusTmReader::new(&buf, 7).expect("Deserialization failed");
assert_eq!(ser_len, size); assert_eq!(ser_len, tm_deserialized.total_len());
assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data()); assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data());
assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]); assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]);
assert_eq!(tm_deserialized.crc16().unwrap(), pus_tm.crc16().unwrap()); assert_eq!(tm_deserialized.crc16(), pus_tm.opt_crc16().unwrap());
verify_ping_reply_with_reader(&tm_deserialized, false, 22, dummy_timestamp()); verify_ping_reply_with_reader(&tm_deserialized, false, 22, dummy_timestamp());
} }
#[test]
fn test_deserialization_no_table() {
let timestamp = dummy_timestamp();
let pus_tm = base_ping_reply_full_ctor(timestamp);
let mut buf: [u8; 32] = [0; 32];
let ser_len = pus_tm
.write_to_bytes(&mut buf)
.expect("Serialization failed");
assert_eq!(ser_len, 22);
let tm_deserialized =
PusTmReader::new_crc_no_table(&buf, 7).expect("Deserialization failed");
assert_eq!(ser_len, tm_deserialized.total_len());
assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data());
assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]);
assert_eq!(tm_deserialized.crc16(), pus_tm.opt_crc16().unwrap());
verify_ping_reply_with_reader(&tm_deserialized, false, 22, dummy_timestamp());
}
#[test] #[test]
fn test_deserialization_faulty_crc() { fn test_deserialization_faulty_crc() {
let timestamp = dummy_timestamp(); let timestamp = dummy_timestamp();
@ -1042,7 +1130,7 @@ mod tests {
let res = pus_tm.append_to_vec(&mut vec); let res = pus_tm.append_to_vec(&mut vec);
assert!(res.is_ok()); assert!(res.is_ok());
assert_eq!(res.unwrap(), 22); assert_eq!(res.unwrap(), 22);
verify_raw_ping_reply(pus_tm.crc16().unwrap(), vec.as_slice()); verify_raw_ping_reply(pus_tm.opt_crc16().unwrap(), vec.as_slice());
} }
#[test] #[test]
@ -1152,7 +1240,7 @@ mod tests {
let pus_tm = base_ping_reply_full_ctor(timestamp); let pus_tm = base_ping_reply_full_ctor(timestamp);
let mut buf = [0; 32]; let mut buf = [0; 32];
pus_tm.write_to_bytes(&mut buf).unwrap(); pus_tm.write_to_bytes(&mut buf).unwrap();
assert_eq!(pus_tm, PusTmReader::new(&buf, timestamp.len()).unwrap().0); assert_eq!(pus_tm, PusTmReader::new(&buf, timestamp.len()).unwrap());
} }
#[test] #[test]
@ -1172,9 +1260,8 @@ mod tests {
assert!(!writer.set_apid(MAX_SEQ_COUNT + 1)); assert!(!writer.set_apid(MAX_SEQ_COUNT + 1));
writer.finish(); writer.finish();
// This performs all necessary checks, including the CRC check. // This performs all necessary checks, including the CRC check.
let (tm_read_back, tm_size_read_back) = let tm_read_back = PusTmReader::new(&buf, 7).expect("Re-creating PUS TM failed");
PusTmReader::new(&buf, 7).expect("Re-creating PUS TM failed"); assert_eq!(tm_read_back.total_len(), tm_size);
assert_eq!(tm_size_read_back, tm_size);
assert_eq!(tm_read_back.msg_counter(), 100); assert_eq!(tm_read_back.msg_counter(), 100);
assert_eq!(tm_read_back.dest_id(), 55); assert_eq!(tm_read_back.dest_id(), 55);
assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT); assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT);
@ -1222,7 +1309,7 @@ mod tests {
assert_eq!(writer.sec_header_without_timestamp().msg_counter(), 100); assert_eq!(writer.sec_header_without_timestamp().msg_counter(), 100);
assert_eq!(writer.user_data(), DUMMY_DATA); assert_eq!(writer.user_data(), DUMMY_DATA);
// Need to check crc16 before finish, because finish will update the CRC. // Need to check crc16 before finish, because finish will update the CRC.
let crc16 = writer.crc16(); let crc16 = writer.opt_crc16();
assert!(crc16.is_some()); assert!(crc16.is_some());
assert_eq!(crc16.unwrap(), crc16_raw); assert_eq!(crc16.unwrap(), crc16_raw);
writer.finish(); writer.finish();
@ -1240,8 +1327,8 @@ mod tests {
let pus_tm = base_ping_reply_full_ctor(timestamp); let pus_tm = base_ping_reply_full_ctor(timestamp);
let mut buf = [0; 32]; let mut buf = [0; 32];
pus_tm.write_to_bytes(&mut buf).unwrap(); pus_tm.write_to_bytes(&mut buf).unwrap();
let (tm_0, _) = PusTmReader::new(&buf, timestamp.len()).unwrap(); let tm_0 = PusTmReader::new(&buf, timestamp.len()).unwrap();
let (tm_1, _) = PusTmReader::new(&buf, timestamp.len()).unwrap(); let tm_1 = PusTmReader::new(&buf, timestamp.len()).unwrap();
assert_eq!(tm_0, tm_1); assert_eq!(tm_0, tm_1);
} }
#[test] #[test]
@ -1313,7 +1400,7 @@ mod tests {
PusTmCreator::new_simple(sph, 17, 2, &time_provider, &mut stamp_buf, &[], true) PusTmCreator::new_simple(sph, 17, 2, &time_provider, &mut stamp_buf, &[], true)
.unwrap(); .unwrap();
let pus_tm_vec = pus_tm.to_vec().unwrap(); let pus_tm_vec = pus_tm.to_vec().unwrap();
let (tm_reader, _) = PusTmReader::new(&pus_tm_vec, time_provider.len_as_bytes()).unwrap(); let tm_reader = PusTmReader::new(&pus_tm_vec, time_provider.len_as_bytes()).unwrap();
let output = to_allocvec(&tm_reader).unwrap(); let output = to_allocvec(&tm_reader).unwrap();
let output_converted_back: PusTmReader = from_bytes(&output).unwrap(); let output_converted_back: PusTmReader = from_bytes(&output).unwrap();
assert_eq!(output_converted_back, tm_reader); assert_eq!(output_converted_back, tm_reader);

View File

@ -26,8 +26,8 @@
//! //!
//! - [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library. //! - [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library.
//! - [`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.
//! //!
//! ### Optional features //! ### Optional features
//! //!
@ -62,7 +62,6 @@ extern crate alloc;
extern crate std; extern crate std;
use core::{fmt::Debug, hash::Hash}; use core::{fmt::Debug, hash::Hash};
use crc::{Crc, CRC_16_IBM_3740};
use delegate::delegate; use delegate::delegate;
use zerocopy::{FromBytes, IntoBytes}; use zerocopy::{FromBytes, IntoBytes};
@ -70,6 +69,7 @@ use zerocopy::{FromBytes, IntoBytes};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub mod cfdp; pub mod cfdp;
pub mod crc;
pub mod ecss; pub mod ecss;
pub mod seq_count; pub mod seq_count;
pub mod time; pub mod time;
@ -81,9 +81,6 @@ mod private {
pub const CCSDS_HEADER_LEN: usize = core::mem::size_of::<crate::zc::SpHeader>(); pub const CCSDS_HEADER_LEN: usize = core::mem::size_of::<crate::zc::SpHeader>();
/// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard.
pub const CRC_CCITT_FALSE: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
pub const MAX_APID: u16 = 2u16.pow(11) - 1; pub const MAX_APID: u16 = 2u16.pow(11) - 1;
pub const MAX_SEQ_COUNT: u16 = 2u16.pow(14) - 1; pub const MAX_SEQ_COUNT: u16 = 2u16.pow(14) - 1;
@ -452,9 +449,9 @@ pub trait CcsdsPrimaryHeader {
/// # Arguments /// # Arguments
/// ///
/// * `version` - CCSDS version field, occupies the first 3 bits of the raw header. Will generally /// * `version` - CCSDS version field, occupies the first 3 bits of the raw header. Will generally
/// be set to 0b000 in all constructors provided by this crate. /// be set to 0b000 in all constructors provided by this crate.
/// * `packet_id` - Packet Identifier, which can also be used as a start marker. Occupies the last /// * `packet_id` - Packet Identifier, which can also be used as a start marker. Occupies the last
/// 13 bits of the first two bytes of the raw header /// 13 bits of the first two bytes of the raw header
/// * `psc` - Packet Sequence Control, occupies the third and fourth byte of the raw header /// * `psc` - Packet Sequence Control, occupies the third and fourth byte of the raw header
/// * `data_len` - Data length field occupies the fifth and the sixth byte of the raw header /// * `data_len` - Data length field occupies the fifth and the sixth byte of the raw header
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]