API update
Rust/spacepackets/pipeline/head There was a failure building this commit Details

This commit is contained in:
Robin Müller 2023-07-05 19:07:31 +02:00
parent f406957752
commit c85177ece9
Signed by: muellerr
GPG Key ID: A649FB78196E3849
6 changed files with 114 additions and 21 deletions

View File

@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `UnsignedByteField` as a type-erased helper.
- Added `SerializablePusPacket` as a generic abstraction for PUS packets which are
writable.
- Added new `PusTmZeroCopyWriter` class which allows to set fields on a raw TM packet,
which might be more efficient that modification and re-writing a packet with the
`PusTm` object.
## Changed

View File

@ -598,12 +598,12 @@ mod tests {
// No record boundary preservation
assert_eq!((buf[3] >> 7) & 1, pdu_conf.seg_ctrl as u8);
// Entity ID length raw value is actual number of octets - 1 => 0
let entity_id_len = pdu_conf.pdu_conf.source_entity_id.len();
let entity_id_len = pdu_conf.pdu_conf.source_entity_id.size();
assert_eq!((buf[3] >> 4) & 0b111, entity_id_len as u8 - 1);
// No segment metadata
assert_eq!((buf[3] >> 3) & 0b1, pdu_conf.seg_metadata_flag as u8);
// Transaction Sequence ID length raw value is actual number of octets - 1 => 0
let seq_num_len = pdu_conf.pdu_conf.transaction_seq_num.len();
let seq_num_len = pdu_conf.pdu_conf.transaction_seq_num.size();
assert_eq!(buf[3] & 0b111, seq_num_len as u8 - 1);
let mut current_idx = 4;
let mut byte_field_check = |field_len: usize, ubf: &UnsignedByteField| {

View File

@ -257,16 +257,22 @@ pub(crate) fn user_data_from_raw(
}
}
pub(crate) fn verify_crc16_ccitt_false_from_raw(
pub(crate) fn verify_crc16_ccitt_false_from_raw_to_pus_error(
raw_data: &[u8],
crc16: u16,
) -> Result<(), PusError> {
verify_crc16_ccitt_false_from_raw(raw_data)
.then(|| ())
.ok_or(PusError::IncorrectCrc(crc16))
}
pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool {
let mut digest = CRC_CCITT_FALSE.digest();
digest.update(raw_data);
if digest.finalize() == 0 {
return Ok(());
return true;
}
Err(PusError::IncorrectCrc(crc16))
false
}
macro_rules! ccsds_impl {

View File

@ -33,7 +33,7 @@
//! ```
use crate::ecss::{
ccsds_impl, crc_from_raw_data, crc_procedure, sp_header_impls, user_data_from_raw,
verify_crc16_ccitt_false_from_raw, CrcType, PusError, PusPacket, PusVersion,
verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion,
SerializablePusPacket,
};
use crate::{
@ -394,7 +394,10 @@ impl<'raw_data> PusTc<'raw_data> {
calc_crc_on_serialization: false,
crc16: Some(crc_from_raw_data(raw_data)?),
};
verify_crc16_ccitt_false_from_raw(raw_data, pus_tc.crc16.expect("CRC16 invalid"))?;
verify_crc16_ccitt_false_from_raw_to_pus_error(
raw_data,
pus_tc.crc16.expect("CRC16 invalid"),
)?;
Ok((pus_tc, total_len))
}
@ -500,7 +503,7 @@ impl GenericPusTcSecondaryHeader for PusTc<'_> {
#[cfg(all(test, feature = "std"))]
mod tests {
use crate::ecss::PusVersion::PusC;
use crate::ecss::{PusError, PusPacket};
use crate::ecss::{PusError, PusPacket, SerializablePusPacket};
use crate::tc::ACK_ALL;
use crate::tc::{GenericPusTcSecondaryHeader, PusTc, PusTcSecondaryHeader};
use crate::{ByteConversionError, SpHeader};

View File

@ -1,13 +1,13 @@
//! This module contains all components required to create a ECSS PUS C telemetry 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/).
use crate::ecss::{
ccsds_impl, crc_from_raw_data, crc_procedure, sp_header_impls, user_data_from_raw,
verify_crc16_ccitt_false_from_raw, CrcType, PusError, PusPacket, PusVersion,
SerializablePusPacket,
calc_pus_crc16, ccsds_impl, crc_from_raw_data, crc_procedure, sp_header_impls,
user_data_from_raw, verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError,
PusPacket, PusVersion, SerializablePusPacket,
};
use crate::{
ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SizeMissmatch, SpHeader,
CCSDS_HEADER_LEN, CRC_CCITT_FALSE,
CCSDS_HEADER_LEN, CRC_CCITT_FALSE, MAX_SEQ_COUNT,
};
use core::mem::size_of;
#[cfg(feature = "serde")]
@ -388,7 +388,10 @@ impl<'raw_data> PusTm<'raw_data> {
calc_crc_on_serialization: false,
crc16: Some(crc_from_raw_data(raw_data)?),
};
verify_crc16_ccitt_false_from_raw(raw_data, pus_tm.crc16.expect("CRC16 invalid"))?;
verify_crc16_ccitt_false_from_raw_to_pus_error(
raw_data,
pus_tm.crc16.expect("CRC16 invalid"),
)?;
Ok((pus_tm, total_len))
}
@ -399,6 +402,62 @@ impl<'raw_data> PusTm<'raw_data> {
}
}
/// This is a helper class to update certain fields in a raw PUS telemetry packet directly in place.
/// This can be more efficient than creating a full [PusTm], modifying the fields and then writing
/// it back to another buffer.
///
/// Please note that the [Self::finish] method has to be called for the PUS TM CRC16 to be valid
/// after changing fields of the TM packet. Furthermore, the constructor of this class will not
/// do any checks except a length check to ensure that all relevant fields can be updated without
/// a panic. If a full validity check of the PUS TM packet is required, it is recommended
/// to construct a full [PusTm] object from the raw bytestream first.
pub struct PusTmZeroCopyWriter<'raw> {
raw_tm: &'raw mut [u8],
}
impl<'raw> PusTmZeroCopyWriter<'raw> {
/// This function will not do any other checks on the raw data other than a length check
/// for all internal fields which can be updated. It is the responsibility of the user to ensure
/// the raw slice contains a valid telemetry packet. The slice should have the exact length
/// of the telemetry packet for this class to work properly.
pub fn new(raw_tm: &'raw mut [u8]) -> Option<Self> {
if raw_tm.len() < 13 {
return None;
}
Some(Self { raw_tm })
}
/// This function sets the message counter in the PUS TM secondary header.
pub fn set_msg_count(&mut self, msg_count: u16) {
self.raw_tm[9..11].copy_from_slice(&msg_count.to_be_bytes());
}
/// This function sets the destination ID in the PUS TM secondary header.
pub fn set_destination_id(&mut self, dest_id: u16) {
self.raw_tm[11..13].copy_from_slice(&dest_id.to_be_bytes())
}
/// Set the sequence count. Returns false and does not update the value if the passed value
/// exceeds [MAX_SEQ_COUNT].
pub fn set_seq_count_in_place(&mut self, seq_count: u16) -> bool {
if seq_count > MAX_SEQ_COUNT {
return false;
}
let new_psc =
(u16::from_be_bytes(self.raw_tm[2..4].try_into().unwrap()) & 0xC000) | seq_count;
self.raw_tm[2..4].copy_from_slice(&new_psc.to_be_bytes());
true
}
/// This method has to be called after modifying fields to ensure the CRC16 of the telemetry
/// packet remains valid.
pub fn finish(self) {
let slice_len = self.raw_tm.len();
let crc16 = calc_pus_crc16(&self.raw_tm[..slice_len - 2]);
self.raw_tm[slice_len - 2..].copy_from_slice(&crc16.to_be_bytes());
}
}
impl SerializablePusPacket for PusTm<'_> {
fn len_packed(&self) -> usize {
let mut length = PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA;
@ -714,4 +773,26 @@ mod tests {
pus_tm.write_to_bytes(&mut buf).unwrap();
assert_eq!(pus_tm, PusTm::from_bytes(&buf, timestamp.len()).unwrap().0);
}
#[test]
fn test_zero_copy_writer() {
let ping_tm = base_ping_reply_full_ctor(dummy_timestamp());
let mut buf: [u8; 64] = [0; 64];
let tm_size = ping_tm
.write_to_bytes(&mut buf)
.expect("writing PUS ping TM failed");
let mut writer = PusTmZeroCopyWriter::new(&mut buf[..tm_size])
.expect("Creating zero copy writer failed");
writer.set_destination_id(55);
writer.set_msg_count(100);
writer.set_seq_count_in_place(MAX_SEQ_COUNT - 1);
writer.finalize();
// This performs all necessary checks, including the CRC check.
let (tm_read_back, tm_size_read_back) =
PusTm::from_bytes(&buf, 7).expect("Re-creating PUS TM failed");
assert_eq!(tm_size_read_back, tm_size);
assert_eq!(tm_read_back.msg_counter(), 100);
assert_eq!(tm_read_back.dest_id(), 55);
assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT - 1);
}
}

View File

@ -317,7 +317,7 @@ pub mod tests {
#[test]
fn test_simple_u8() {
let u8 = UnsignedByteFieldU8::new(5);
assert_eq!(u8.len(), 1);
assert_eq!(u8.size(), 1);
let mut buf: [u8; 8] = [0; 8];
let len = u8
.write_to_be_bytes(&mut buf)
@ -332,7 +332,7 @@ pub mod tests {
#[test]
fn test_simple_u16() {
let u16 = UnsignedByteFieldU16::new(3823);
assert_eq!(u16.len(), 2);
assert_eq!(u16.size(), 2);
let mut buf: [u8; 8] = [0; 8];
let len = u16
.write_to_be_bytes(&mut buf)
@ -348,7 +348,7 @@ pub mod tests {
#[test]
fn test_simple_u32() {
let u32 = UnsignedByteFieldU32::new(80932);
assert_eq!(u32.len(), 4);
assert_eq!(u32.size(), 4);
let mut buf: [u8; 8] = [0; 8];
let len = u32
.write_to_be_bytes(&mut buf)
@ -364,7 +364,7 @@ pub mod tests {
#[test]
fn test_simple_u64() {
let u64 = UnsignedByteFieldU64::new(5999999);
assert_eq!(u64.len(), 8);
assert_eq!(u64.size(), 8);
let mut buf: [u8; 8] = [0; 8];
let len = u64
.write_to_be_bytes(&mut buf)
@ -493,7 +493,7 @@ pub mod tests {
#[test]
fn type_erased_u8_write() {
let u8 = UnsignedByteField::new(1, 5);
assert_eq!(u8.len(), 1);
assert_eq!(u8.size(), 1);
let mut buf: [u8; 8] = [0; 8];
u8.write_to_be_bytes(&mut buf)
.expect("writing to raw buffer failed");
@ -506,7 +506,7 @@ pub mod tests {
#[test]
fn type_erased_u16_write() {
let u16 = UnsignedByteField::new(2, 3823);
assert_eq!(u16.len(), 2);
assert_eq!(u16.size(), 2);
let mut buf: [u8; 8] = [0; 8];
u16.write_to_be_bytes(&mut buf)
.expect("writing to raw buffer failed");
@ -520,7 +520,7 @@ pub mod tests {
#[test]
fn type_erased_u32_write() {
let u32 = UnsignedByteField::new(4, 80932);
assert_eq!(u32.len(), 4);
assert_eq!(u32.size(), 4);
let mut buf: [u8; 8] = [0; 8];
u32.write_to_be_bytes(&mut buf)
.expect("writing to raw buffer failed");
@ -534,7 +534,7 @@ pub mod tests {
#[test]
fn type_erased_u64_write() {
let u64 = UnsignedByteField::new(8, 5999999);
assert_eq!(u64.len(), 8);
assert_eq!(u64.size(), 8);
let mut buf: [u8; 8] = [0; 8];
u64.write_to_be_bytes(&mut buf)
.expect("writing to raw buffer failed");