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-06-18 22:48:51 +02:00
|
|
|
/// CRC algorithm used by the PUS standard
|
|
|
|
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>();
|
|
|
|
|
|
|
|
/// All PUS versions. Only PUS C is supported by this library
|
|
|
|
#[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
|
|
|
|
|
|
|
pub(crate) fn crc_from_raw_data(raw_data: Option<&[u8]>) -> Result<u16, PusError> {
|
|
|
|
if let Some(raw_data) = raw_data {
|
|
|
|
if raw_data.len() < 2 {
|
|
|
|
return Err(PusError::RawDataTooShort(raw_data.len()));
|
|
|
|
}
|
|
|
|
return Ok(u16::from_be_bytes(
|
|
|
|
raw_data[raw_data.len() - 2..raw_data.len()]
|
|
|
|
.try_into()
|
|
|
|
.unwrap(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
Err(PusError::NoRawData)
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|