spacepackets/src/ecss.rs

137 lines
3.8 KiB
Rust
Raw Normal View History

2022-07-31 13:31:14 +02:00
//! Common definitions and helpers required to create PUS TMTC packets according to
//! [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/)
2022-06-18 22:48:51 +02:00
use crate::{CcsdsPacket, PacketError};
use core::mem::size_of;
use crc::{Crc, CRC_16_IBM_3740};
use serde::{Deserialize, Serialize};
2022-07-31 02:27:27 +02:00
pub type CrcType = u16;
2022-07-31 13:32:11 +02:00
/// CRC algorithm used by the PUS standard.
2022-06-18 22:48:51 +02:00
pub const CRC_CCITT_FALSE: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
pub const CCSDS_HEADER_LEN: usize = size_of::<crate::zc::SpHeader>();
2022-07-31 13:32:11 +02:00
/// All PUS versions. Only PUS C is supported by this library.
2022-06-18 22:48:51 +02:00
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize, Debug)]
pub enum PusVersion {
EsaPus = 0,
PusA = 1,
PusC = 2,
2022-07-31 02:27:27 +02:00
Invalid = 0b1111,
}
impl TryFrom<u8> for PusVersion {
type Error = ();
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(()),
}
}
2022-06-18 22:48:51 +02:00
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PusError {
VersionNotSupported(PusVersion),
IncorrectCrc(u16),
RawDataTooShort(usize),
NoRawData,
/// CRC16 needs to be calculated first
CrcCalculationMissing,
OtherPacketError(PacketError),
}
pub trait PusPacket: CcsdsPacket {
const PUS_VERSION: PusVersion = PusVersion::PusC;
2022-07-31 02:27:27 +02:00
fn pus_version(&self) -> PusVersion;
2022-06-18 22:48:51 +02:00
fn service(&self) -> u8;
fn subservice(&self) -> u8;
fn user_data(&self) -> Option<&[u8]>;
fn crc16(&self) -> Option<u16>;
}
2022-07-31 02:27:27 +02:00
2022-07-31 13:31:14 +02:00
pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, PusError> {
if raw_data.len() < 2 {
return Err(PusError::RawDataTooShort(raw_data.len()));
2022-07-31 02:27:27 +02:00
}
2022-07-31 13:31:14 +02:00
Ok(u16::from_be_bytes(
raw_data[raw_data.len() - 2..raw_data.len()]
.try_into()
.unwrap(),
))
2022-07-31 02:27:27 +02:00
}
pub(crate) fn calc_pus_crc16(bytes: &[u8]) -> u16 {
let mut digest = CRC_CCITT_FALSE.digest();
digest.update(bytes);
digest.finalize()
}
pub(crate) fn crc_procedure(
calc_on_serialization: bool,
cached_crc16: &Option<u16>,
curr_idx: usize,
slice: &[u8],
) -> Result<u16, PusError> {
let crc16;
if calc_on_serialization {
crc16 = calc_pus_crc16(&slice[0..curr_idx])
} else if cached_crc16.is_none() {
return Err(PusError::CrcCalculationMissing);
} else {
crc16 = cached_crc16.unwrap();
}
Ok(crc16)
}
2022-07-31 13:31:14 +02:00
pub(crate) fn user_data_from_raw(
current_idx: usize,
total_len: usize,
raw_data_len: usize,
slice: &[u8],
) -> Result<Option<&[u8]>, PusError> {
match current_idx {
_ if current_idx == total_len - 2 => Ok(None),
_ if current_idx > total_len - 2 => Err(PusError::RawDataTooShort(raw_data_len)),
_ => Ok(Some(&slice[current_idx..total_len - 2])),
}
}
pub(crate) fn verify_crc16_from_raw(raw_data: &[u8], crc16: u16) -> Result<(), PusError> {
let mut digest = CRC_CCITT_FALSE.digest();
digest.update(raw_data);
if digest.finalize() == 0 {
return Ok(());
}
Err(PusError::IncorrectCrc(crc16))
}
macro_rules! ccsds_impl {
() => {
delegate!(to self.sp_header {
fn ccsds_version(&self) -> u8;
fn packet_id(&self) -> crate::PacketId;
fn psc(&self) -> crate::PacketSequenceCtrl;
fn data_len(&self) -> u16;
});
}
}
2022-07-31 14:24:16 +02:00
macro_rules! sp_header_impls {
() => {
delegate!(to self.sp_header {
pub fn set_apid(&mut self, apid: u16) -> bool;
pub fn set_seq_count(&mut self, seq_count: u16) -> bool;
pub fn set_seq_flags(&mut self, seq_flag: SequenceFlags);
});
}
}
2022-07-31 13:31:14 +02:00
pub(crate) use ccsds_impl;
2022-07-31 14:24:16 +02:00
pub(crate) use sp_header_impls;