use std::fmt;
use thiserror::Error;
#[allow(unused_imports)]
use crate::padwing::map::TpcPwbPosition;
pub mod map;
pub const PWB_RATE: f64 = 62.5e6;
pub const PWB_MAX: i16 = 2047;
pub const PWB_MIN: i16 = -2048;
#[derive(Error, Debug)]
#[error("unknown parsing from board name `{input}` to BoardId")]
pub struct ParseBoardIdError {
input: String,
}
#[derive(Error, Debug)]
#[error("unknown conversion from mac address `{input:?}` to BoardId")]
pub struct TryBoardIdFromMacAddressError {
input: [u8; 6],
}
#[derive(Error, Debug)]
#[error("unknown conversion from unsigned `{input}` to BoardId")]
pub struct TryBoardIdFromUnsignedError {
input: u32,
}
const PADWING_BOARDS: [(&str, [u8; 6], u32); 71] = [
("00", [236, 40, 255, 135, 84, 2], 2281646316),
("01", [236, 40, 250, 162, 84, 2], 2734303468),
("02", [236, 40, 136, 108, 84, 2], 1820862700),
("03", [236, 40, 226, 49, 84, 2], 836905196),
("04", [236, 41, 12, 121, 84, 2], 2030840300),
("05", [236, 40, 211, 69, 84, 2], 1171466476),
("06", [236, 40, 218, 6, 84, 2], 114960620),
("07", [236, 40, 116, 164, 84, 2], 2759076076),
("08", [236, 40, 253, 139, 84, 2], 2348624108),
("10", [236, 40, 248, 75, 84, 2], 1274554604),
("11", [236, 40, 197, 187, 84, 2], 3150260460),
("12", [236, 41, 34, 206, 84, 2], 3458345452),
("13", [236, 40, 159, 252, 84, 2], 4238289132),
("14", [236, 41, 44, 52, 84, 2], 875309548),
("15", [236, 40, 219, 60, 84, 2], 1020995820),
("17", [236, 40, 153, 39, 84, 2], 664348908),
("18", [236, 40, 228, 87, 84, 2], 1474570476),
("19", [236, 40, 116, 173, 84, 2], 2910071020),
("20", [236, 40, 219, 80, 84, 2], 1356540140),
("21", [236, 40, 221, 26, 84, 2], 450701548),
("22", [236, 40, 113, 70, 84, 2], 1181821164),
("23", [236, 41, 39, 253, 84, 2], 4247202284),
("24", [236, 40, 226, 191, 84, 2], 3219269868),
("25", [236, 40, 212, 176, 84, 2], 2966694124),
("26", [236, 40, 188, 31, 84, 2], 532424940),
("27", [236, 40, 252, 239, 84, 2], 4026280172),
("29", [236, 40, 108, 189, 84, 2], 3177982188),
("33", [236, 40, 255, 150, 84, 2], 2533304556),
("34", [236, 40, 226, 52, 84, 2], 887236844),
("35", [236, 40, 137, 30, 84, 2], 512305388),
("36", [236, 40, 165, 153, 84, 2], 2577737964),
("37", [236, 41, 43, 61, 84, 2], 1026238956),
("39", [236, 41, 43, 253, 84, 2], 4247464428),
("40", [236, 40, 198, 81, 84, 2], 1371941100),
("41", [236, 40, 187, 198, 84, 2], 3334154476),
("42", [236, 41, 41, 188, 84, 2], 3156814316),
("44", [236, 40, 218, 198, 84, 2], 3336186092),
("45", [236, 41, 24, 143, 84, 2], 2400725484),
("46", [236, 40, 160, 64, 84, 2], 1084238060),
("49", [236, 40, 156, 87, 84, 2], 1469851884),
("52", [236, 41, 24, 28, 84, 2], 471345644),
("53", [236, 40, 183, 208, 84, 2], 3501664492),
("54", [236, 40, 113, 62, 84, 2], 1047603436),
("55", [236, 40, 255, 172, 84, 2], 2902403308),
("56", [236, 40, 135, 152, 84, 2], 2558994668),
("57", [236, 40, 128, 45, 84, 2], 763373804),
("58", [236, 41, 42, 70, 84, 2], 1177168364),
("60", [236, 40, 243, 36, 84, 2], 619915500),
("63", [236, 40, 108, 234, 84, 2], 3932956908),
("64", [236, 40, 110, 20, 84, 2], 342763756),
("65", [236, 40, 215, 15, 84, 2], 265758956),
("66", [236, 40, 197, 199, 84, 2], 3351587052),
("67", [236, 40, 183, 38, 84, 2], 649537772),
("68", [236, 40, 211, 91, 84, 2], 1540565228),
("69", [236, 40, 224, 249, 84, 2], 4192217324),
("70", [236, 40, 248, 99, 84, 2], 1677207788),
("71", [236, 40, 129, 16, 84, 2], 276900076),
("72", [236, 40, 241, 249, 84, 2], 4193331436),
("73", [236, 40, 113, 64, 84, 2], 1081157868),
("74", [236, 40, 252, 14, 84, 2], 251406572),
("75", [236, 41, 39, 26, 84, 2], 438774252),
("76", [236, 40, 244, 136, 84, 2], 2297702636),
("77", [236, 41, 17, 29, 84, 2], 487664108),
("78", [236, 41, 37, 14, 84, 2], 237316588),
("81", [236, 40, 137, 152, 84, 2], 2559125740),
("84", [236, 40, 135, 104, 84, 2], 1753688300),
("85", [236, 40, 216, 183, 84, 2], 3084396780),
("87", [236, 40, 244, 138, 84, 2], 2331257068),
("89", [57, 232, 246, 41, 216, 2], 704047161),
("90", [57, 232, 209, 204, 216, 2], 3436308537),
("91", [236, 40, 190, 114, 84, 2], 1925064940),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BoardId {
name: &'static str,
mac_address: [u8; 6],
device_id: u32,
}
impl TryFrom<&str> for BoardId {
type Error = ParseBoardIdError;
fn try_from(name: &str) -> Result<Self, Self::Error> {
for triplet in PADWING_BOARDS {
if name == triplet.0 {
return Ok(BoardId {
name: triplet.0,
mac_address: triplet.1,
device_id: triplet.2,
});
}
}
Err(ParseBoardIdError {
input: name.to_string(),
})
}
}
impl TryFrom<[u8; 6]> for BoardId {
type Error = TryBoardIdFromMacAddressError;
fn try_from(mac: [u8; 6]) -> Result<Self, Self::Error> {
for triplet in PADWING_BOARDS {
if mac == triplet.1 {
return Ok(BoardId {
name: triplet.0,
mac_address: triplet.1,
device_id: triplet.2,
});
}
}
Err(TryBoardIdFromMacAddressError { input: mac })
}
}
impl TryFrom<u32> for BoardId {
type Error = TryBoardIdFromUnsignedError;
fn try_from(device_id: u32) -> Result<Self, Self::Error> {
for triplet in PADWING_BOARDS {
if device_id == triplet.2 {
return Ok(BoardId {
name: triplet.0,
mac_address: triplet.1,
device_id: triplet.2,
});
}
}
Err(TryBoardIdFromUnsignedError { input: device_id })
}
}
impl BoardId {
pub fn name(&self) -> &str {
self.name
}
pub fn mac_address(&self) -> [u8; 6] {
self.mac_address
}
pub fn device_id(&self) -> u32 {
self.device_id
}
}
#[derive(Error, Debug)]
#[error("unknown conversion from unsigned `{input}` to AfterId")]
pub struct TryAfterIdFromUnsignedError {
input: u8,
}
#[derive(Error, Debug)]
#[error("unknown parsing from char `{input}` to AfterId")]
pub struct ParseAfterIdError {
input: char,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AfterId {
A,
B,
C,
D,
}
impl TryFrom<u8> for AfterId {
type Error = TryAfterIdFromUnsignedError;
fn try_from(num: u8) -> Result<Self, Self::Error> {
match num {
0 => Ok(Self::A),
1 => Ok(Self::B),
2 => Ok(Self::C),
3 => Ok(Self::D),
_ => Err(Self::Error { input: num }),
}
}
}
impl TryFrom<char> for AfterId {
type Error = ParseAfterIdError;
fn try_from(character: char) -> Result<Self, Self::Error> {
match character {
'A' => Ok(Self::A),
'B' => Ok(Self::B),
'C' => Ok(Self::C),
'D' => Ok(Self::D),
_ => Err(Self::Error { input: character }),
}
}
}
#[derive(Error, Debug)]
pub enum TryChunkFromSliceError {
#[error("incomplete slice (expected at least `{min_expected}` bytes, found `{found}`)")]
IncompleteSlice { found: usize, min_expected: usize },
#[error("unknown device id")]
UnknownDeviceId(#[from] TryBoardIdFromUnsignedError),
#[error("unknown channel id")]
UnknownChannelId(#[from] TryAfterIdFromUnsignedError),
#[error("unknown flags `{found:0>8b}`")]
UnknownFlags { found: u8 },
#[error("bad chunk length `{found}` (expected at least `{min}` and at most `{max}`)")]
BadChunkLength {
found: usize,
min: usize,
max: usize,
},
#[error("zero-bytes mismatch (found `{found:?}`)")]
ZeroMismatch { found: Vec<u8> },
#[error("header CRC-32C mismatch (expected `{expected}`, found `{found}`)")]
HeaderCRC32CMismatch { found: u32, expected: u32 },
#[error("payload CRC-32C mismatch (expected `{expected}`, found `{found}`)")]
PayloadCRC32CMismatch { found: u32, expected: u32 },
}
#[derive(Clone, Debug)]
pub struct Chunk {
device_id: u32,
packet_sequence: u32,
channel_sequence: u16,
channel_id: u8,
flags: u8,
chunk_id: u16,
payload: Vec<u8>,
}
impl fmt::Display for Chunk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Device ID: {}", self.device_id)?;
writeln!(f, "Packet sequence: {}", self.packet_sequence)?;
writeln!(f, "Channel sequence: {}", self.channel_sequence)?;
writeln!(f, "Channel ID: {}", self.channel_id)?;
writeln!(f, "Flags: {:0>8b}", self.flags)?;
writeln!(f, "Chunk ID: {}", self.chunk_id)?;
writeln!(f, "Chunk length: {}", self.payload.len())?;
writeln!(f, "Header CRC-32C: {}", self.header_crc32c())?;
write!(f, "Payload CRC-32C: {}", self.payload_crc32c())?;
Ok(())
}
}
impl Chunk {
pub fn board_id(&self) -> BoardId {
BoardId::try_from(self.device_id).unwrap()
}
pub fn packet_sequence(&self) -> u32 {
self.packet_sequence
}
pub fn channel_sequence(&self) -> u16 {
self.channel_sequence
}
pub fn after_id(&self) -> AfterId {
AfterId::try_from(self.channel_id).unwrap()
}
pub fn is_end_of_message(&self) -> bool {
self.flags & 1 == 1
}
pub fn chunk_id(&self) -> u16 {
self.chunk_id
}
pub fn header_crc32c(&self) -> u32 {
let slice: Vec<u8> = self
.device_id
.to_le_bytes()
.into_iter()
.chain(self.packet_sequence.to_le_bytes())
.chain(self.channel_sequence.to_le_bytes())
.chain(self.channel_id.to_le_bytes())
.chain(self.flags.to_le_bytes())
.chain(self.chunk_id.to_le_bytes())
.chain(u16::try_from(self.payload.len()).unwrap().to_le_bytes())
.collect();
!crc32c::crc32c(&slice[..])
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn payload_crc32c(&self) -> u32 {
let padding = match self.payload.len() % 4 {
0 => 0,
r => 4 - r,
};
let slice: Vec<u8> = self
.payload
.clone()
.into_iter()
.chain(std::iter::repeat(0).take(padding))
.collect();
!crc32c::crc32c(&slice[..])
}
}
impl TryFrom<&[u8]> for Chunk {
type Error = TryChunkFromSliceError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() < 28 {
return Err(Self::Error::IncompleteSlice {
found: slice.len(),
min_expected: 28,
});
}
if slice.len() % 4 != 0 {
return Err(Self::Error::IncompleteSlice {
found: slice.len(),
min_expected: slice.len() + 4 - slice.len() % 4,
});
}
let device_id = slice[..4].try_into().unwrap();
let device_id = u32::from_le_bytes(device_id);
BoardId::try_from(device_id)?;
let packet_sequence = slice[4..8].try_into().unwrap();
let packet_sequence = u32::from_le_bytes(packet_sequence);
let channel_sequence = slice[8..10].try_into().unwrap();
let channel_sequence = u16::from_le_bytes(channel_sequence);
let channel_id = slice[10];
AfterId::try_from(channel_id)?;
let flags = slice[11];
if flags != 0 && flags != 1 {
return Err(Self::Error::UnknownFlags { found: flags });
}
let chunk_id = slice[12..14].try_into().unwrap();
let chunk_id = u16::from_le_bytes(chunk_id);
let chunk_length = slice[14..16].try_into().unwrap();
let chunk_length = u16::from_le_bytes(chunk_length).into();
let max = slice.len() - 24;
let min = max - 3;
if chunk_length < min || chunk_length > max {
return Err(Self::Error::BadChunkLength {
found: chunk_length,
min,
max,
});
}
let header_crc = slice[16..20].try_into().unwrap();
let header_crc = u32::from_le_bytes(header_crc);
let expected_crc = !crc32c::crc32c(&slice[0..16]);
if header_crc != expected_crc {
return Err(Self::Error::HeaderCRC32CMismatch {
found: header_crc,
expected: expected_crc,
});
}
let payload = slice[20..][..chunk_length].to_vec();
let padding = slice[20 + chunk_length..slice.len() - 4].to_vec();
if padding.iter().any(|&x| x != 0) {
return Err(Self::Error::ZeroMismatch { found: padding });
}
let payload_crc = slice[slice.len() - 4..].try_into().unwrap();
let payload_crc = u32::from_le_bytes(payload_crc);
let expected_crc = !crc32c::crc32c(&slice[20..slice.len() - 4]);
if payload_crc != expected_crc {
return Err(Self::Error::PayloadCRC32CMismatch {
found: payload_crc,
expected: expected_crc,
});
}
Ok(Self {
device_id,
packet_sequence,
channel_sequence,
channel_id,
flags,
chunk_id,
payload,
})
}
}
#[derive(Error, Debug)]
#[error("unknown conversion from unsigned `{input}` to Compression")]
pub struct TryCompressionFromUnsignedError {
input: u8,
}
#[derive(Clone, Copy, Debug)]
pub enum Compression {
Raw,
}
impl TryFrom<u8> for Compression {
type Error = TryCompressionFromUnsignedError;
fn try_from(num: u8) -> Result<Self, Self::Error> {
match num {
0 => Ok(Self::Raw),
_ => Err(Self::Error { input: num }),
}
}
}
#[derive(Error, Debug)]
#[error("unknown conversion from unsigned `{input}` to Trigger")]
pub struct TryTriggerFromUnsignedError {
input: u8,
}
#[derive(Clone, Copy, Debug)]
pub enum Trigger {
External,
Manual,
InternalPulse,
}
impl TryFrom<u8> for Trigger {
type Error = TryTriggerFromUnsignedError;
fn try_from(num: u8) -> Result<Self, Self::Error> {
match num {
0 => Ok(Self::External),
1 => Ok(Self::Manual),
3 => Ok(Self::InternalPulse),
_ => Err(Self::Error { input: num }),
}
}
}
#[derive(Error, Debug)]
#[error("unknown conversion from unsigned `{input}` to ChannelId")]
pub struct TryChannelIdFromUnsignedError {
input: u16,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ResetChannelId(u16);
impl TryFrom<u16> for ResetChannelId {
type Error = TryChannelIdFromUnsignedError;
fn try_from(num: u16) -> Result<Self, Self::Error> {
match num {
1..=3 => Ok(Self(num)),
_ => Err(Self::Error { input: num }),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FpnChannelId(u16);
impl TryFrom<u16> for FpnChannelId {
type Error = TryChannelIdFromUnsignedError;
fn try_from(num: u16) -> Result<Self, Self::Error> {
match num {
1..=4 => Ok(Self(num)),
_ => Err(Self::Error { input: num }),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PadChannelId(u16);
impl TryFrom<u16> for PadChannelId {
type Error = TryChannelIdFromUnsignedError;
fn try_from(num: u16) -> Result<Self, Self::Error> {
match num {
1..=72 => Ok(Self(num)),
_ => Err(Self::Error { input: num }),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelId {
Reset(ResetChannelId),
Fpn(FpnChannelId),
Pad(PadChannelId),
}
impl TryFrom<u16> for ChannelId {
type Error = TryChannelIdFromUnsignedError;
fn try_from(readout_index: u16) -> Result<Self, Self::Error> {
match readout_index {
1..=3 => Ok(ChannelId::Reset(ResetChannelId::try_from(readout_index)?)),
16 => Ok(ChannelId::Fpn(FpnChannelId::try_from(1)?)),
29 => Ok(ChannelId::Fpn(FpnChannelId::try_from(2)?)),
54 => Ok(ChannelId::Fpn(FpnChannelId::try_from(3)?)),
67 => Ok(ChannelId::Fpn(FpnChannelId::try_from(4)?)),
4..=79 => Ok(ChannelId::Pad(PadChannelId::try_from({
let i = readout_index;
i - (i > 16) as u16 - (i > 29) as u16 - (i > 54) as u16 - (i > 67) as u16 - 3
})?)),
_ => Err(TryChannelIdFromUnsignedError {
input: readout_index,
}),
}
}
}
#[derive(Error, Debug)]
pub enum TryPwbPacketFromSliceError {
#[error("incomplete slice (expected at least `{min_expected}` bytes, found `{found}`)")]
IncompleteSlice { found: usize, min_expected: usize },
#[error("unknown packet version `{found}`")]
UnknownVersion { found: u8 },
#[error("unknown AFTER id")]
UnknownAfterId(#[from] ParseAfterIdError),
#[error("unknown compression")]
UnknownCompression(#[from] TryCompressionFromUnsignedError),
#[error("unknown trigger source")]
UnknownTrigger(#[from] TryTriggerFromUnsignedError),
#[error("unknown mac address")]
UnknownMac(#[from] TryBoardIdFromMacAddressError),
#[error("zero-bytes mismatch (found `{found:?}`)")]
ZeroMismatch { found: [u8; 2] },
#[error("bad last_sca_cell `{found}`")]
BadLastScaCell { found: u16 },
#[error("bad requested_samples `{found}`")]
BadScaSamples { found: usize },
#[error("bad channels sent bit mask")]
BadScaChannelsSent,
#[error("bad channels over threshold bit mask")]
BadScaChannelsThreshold,
#[error("unknown channel id")]
UnknownChannelId(#[from] TryChannelIdFromUnsignedError),
#[error("channel id mismatch (expected `{expected:?}`, found `{found:?}`)")]
ChannelIdMismatch {
found: ChannelId,
expected: ChannelId,
},
#[error("number of samples mismatch (expected `{expected}`, found `{found}`)")]
NumberOfSamplesMismatch { found: usize, expected: usize },
#[error("bad end-of-data marker `{found:?}`")]
BadEndOfDataMarker { found: [u8; 4] },
}
#[derive(Clone, Debug)]
pub struct PwbV2Packet {
after_id: AfterId,
compression: Compression,
trigger_source: Trigger,
board_id: BoardId,
trigger_delay: u16,
trigger_timestamp: u64,
last_sca_cell: u16,
requested_samples: usize,
channels_sent: Vec<ChannelId>,
channels_over_threshold: Vec<ChannelId>,
event_counter: u32,
fifo_max_depth: u16,
event_descriptor_write_depth: u8,
event_descriptor_read_depth: u8,
data: Vec<i16>,
}
impl fmt::Display for PwbV2Packet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Format revision: {}", self.packet_version())?;
writeln!(f, "AFTER ID: {:?}", self.after_id)?;
writeln!(f, "Compression: {:?}", self.compression)?;
writeln!(f, "Trigger source: {:?}", self.trigger_source)?;
let mac_address = self.board_id.mac_address();
writeln!(f, "MAC address: {mac_address:?}")?;
writeln!(f, "Trigger delay: {}", self.trigger_delay)?;
writeln!(f, "Trigger timestamp: {}", self.trigger_timestamp)?;
writeln!(f, "Last SCA cell: {}", self.last_sca_cell)?;
writeln!(f, "Requested samples: {}", self.requested_samples)?;
let channels_sent: Vec<u16> = (1..=79)
.filter(|i| {
let channel = ChannelId::try_from(*i).unwrap();
self.channels_sent.contains(&channel)
})
.collect();
writeln!(f, "Channels sent: {channels_sent:?}")?;
let channels_over_threshold: Vec<u16> = (1..=79)
.filter(|i| {
let channel = ChannelId::try_from(*i).unwrap();
self.channels_over_threshold.contains(&channel)
})
.collect();
writeln!(f, "Channels over threshold: {channels_over_threshold:?}")?;
writeln!(f, "Event counter: {}", self.event_counter)?;
writeln!(f, "FIFO max depth: {}", self.fifo_max_depth)?;
writeln!(
f,
"Event descriptor write depth: {}",
self.event_descriptor_write_depth
)?;
write!(
f,
"Event descriptor read depth: {}",
self.event_descriptor_read_depth
)?;
Ok(())
}
}
impl PwbV2Packet {
pub fn packet_version(&self) -> u8 {
2
}
pub fn after_id(&self) -> AfterId {
self.after_id
}
pub fn compression(&self) -> Compression {
self.compression
}
pub fn trigger_source(&self) -> Trigger {
self.trigger_source
}
pub fn board_id(&self) -> BoardId {
self.board_id
}
pub fn trigger_delay(&self) -> u16 {
self.trigger_delay
}
pub fn trigger_timestamp(&self) -> u64 {
self.trigger_timestamp
}
pub fn last_sca_cell(&self) -> u16 {
self.last_sca_cell
}
pub fn requested_samples(&self) -> usize {
self.requested_samples
}
pub fn channels_sent(&self) -> &[ChannelId] {
&self.channels_sent
}
pub fn channels_over_threshold(&self) -> &[ChannelId] {
&self.channels_over_threshold
}
pub fn event_counter(&self) -> u32 {
self.event_counter
}
pub fn fifo_max_depth(&self) -> u16 {
self.fifo_max_depth
}
pub fn event_descriptor_write_depth(&self) -> u8 {
self.event_descriptor_write_depth
}
pub fn event_descriptor_read_depth(&self) -> u8 {
self.event_descriptor_read_depth
}
pub fn waveform_at(&self, channel: ChannelId) -> Option<&[i16]> {
if let Some(index) = self.channels_sent.iter().position(|c| *c == channel) {
let samples_per_channel = if self.requested_samples % 2 == 0 {
2 + self.requested_samples
} else {
2 + self.requested_samples + 1
};
let index = samples_per_channel * index;
Some(&self.data[index + 2..][..self.requested_samples])
} else {
None
}
}
}
impl TryFrom<&[u8]> for PwbV2Packet {
type Error = TryPwbPacketFromSliceError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() < 56 {
return Err(Self::Error::IncompleteSlice {
found: slice.len(),
min_expected: 56,
});
}
if slice[0] != 2 {
return Err(Self::Error::UnknownVersion { found: slice[0] });
}
let after_id = AfterId::try_from(slice[1] as char)?;
let compression = Compression::try_from(slice[2])?;
let trigger_source = Trigger::try_from(slice[3])?;
let board_id: [u8; 6] = slice[4..10].try_into().unwrap();
let board_id = BoardId::try_from(board_id)?;
let trigger_delay = slice[10..12].try_into().unwrap();
let trigger_delay = u16::from_le_bytes(trigger_delay);
if slice[18..20] != [0, 0] {
return Err(Self::Error::ZeroMismatch {
found: slice[18..20].try_into().unwrap(),
});
}
let trigger_timestamp = slice[12..20].try_into().unwrap();
let trigger_timestamp = u64::from_le_bytes(trigger_timestamp);
let last_sca_cell = slice[20..22].try_into().unwrap();
let last_sca_cell = u16::from_le_bytes(last_sca_cell);
if last_sca_cell > 511 {
return Err(Self::Error::BadLastScaCell {
found: last_sca_cell,
});
}
let requested_samples = slice[22..24].try_into().unwrap();
let requested_samples = u16::from_le_bytes(requested_samples).into();
if requested_samples > 511 {
return Err(Self::Error::BadScaSamples {
found: requested_samples,
});
}
if slice[33] & 128 != 0 {
return Err(Self::Error::BadScaChannelsSent);
}
let mut num = {
let mut array = [0; 16];
array[..10].copy_from_slice(&slice[24..34]);
u128::from_le_bytes(array)
};
let mut channels_sent: Vec<u16> = Vec::new();
while num != 0 {
let bit = num.leading_zeros();
channels_sent.push((127 - bit).try_into().unwrap());
num ^= 1 << (127 - bit);
}
let channels_sent: Vec<ChannelId> = channels_sent
.into_iter()
.rev()
.map(|index| ChannelId::try_from(index + 1).unwrap())
.collect();
if slice[43] & 128 != 0 {
return Err(Self::Error::BadScaChannelsThreshold);
}
let mut num = {
let mut array = [0; 16];
array[..10].copy_from_slice(&slice[34..44]);
u128::from_le_bytes(array)
};
let mut channels_over_threshold: Vec<u16> = Vec::new();
while num != 0 {
let bit = num.leading_zeros();
channels_over_threshold.push((127 - bit).try_into().unwrap());
num ^= 1 << (127 - bit);
}
let channels_over_threshold: Vec<ChannelId> = channels_over_threshold
.into_iter()
.rev()
.map(|index| ChannelId::try_from(index + 1).unwrap())
.collect();
let event_counter = slice[44..48].try_into().unwrap();
let event_counter = u32::from_le_bytes(event_counter);
let fifo_max_depth = slice[48..50].try_into().unwrap();
let fifo_max_depth = u16::from_le_bytes(fifo_max_depth);
let event_descriptor_write_depth = slice[50];
let event_descriptor_read_depth = slice[51];
let data = &slice[52..];
let bytes_per_channel = if requested_samples % 2 == 0 {
4 + 2 * requested_samples
} else {
4 + 2 * requested_samples + 2
};
if bytes_per_channel * channels_sent.len() + 4 != data.len() {
return Err(Self::Error::IncompleteSlice {
found: slice.len(),
min_expected: 56 + bytes_per_channel * channels_sent.len(),
});
}
for (index, &channel) in channels_sent.iter().enumerate() {
let index = bytes_per_channel * index;
let found_channel = data[index..][..2].try_into().unwrap();
let found_channel = u16::from_le_bytes(found_channel);
let found_channel = ChannelId::try_from(found_channel)?;
if found_channel != channel {
return Err(Self::Error::ChannelIdMismatch {
found: found_channel,
expected: channel,
});
}
let found_size = data[index + 2..][..2].try_into().unwrap();
let found_size = u16::from_le_bytes(found_size).into();
if found_size != requested_samples {
return Err(Self::Error::NumberOfSamplesMismatch {
found: found_size,
expected: requested_samples,
});
}
if requested_samples % 2 != 0
&& data[index + 4 + 2 * requested_samples..][..2] != [0, 0]
{
return Err(Self::Error::ZeroMismatch {
found: data[index + 4 + 2 * requested_samples..][..2]
.try_into()
.unwrap(),
});
}
}
if data[data.len() - 4..] != [204, 204, 204, 204] {
return Err(Self::Error::BadEndOfDataMarker {
found: data[data.len() - 4..].try_into().unwrap(),
});
}
let data = data
.chunks_exact(2)
.map(|s| {
let s = s.try_into().unwrap();
i16::from_le_bytes(s)
})
.collect();
Ok(Self {
after_id,
compression,
trigger_source,
board_id,
trigger_delay,
trigger_timestamp,
last_sca_cell,
requested_samples,
channels_sent,
channels_over_threshold,
event_counter,
fifo_max_depth,
event_descriptor_write_depth,
event_descriptor_read_depth,
data,
})
}
}
#[derive(Error, Debug)]
pub enum TryPwbPacketFromChunksError {
#[error("device id mismatch (expected `{expected:?}`, found `{found:?}`)")]
DeviceIdMismatch { found: BoardId, expected: BoardId },
#[error("channel id mismatch (expected `{expected:?}`, found `{found:?}`)")]
ChannelIdMismatch { found: AfterId, expected: AfterId },
#[error("missing chunk with chunk id `{position}`")]
MissingChunk { position: usize },
#[error("last chunk does not have the `end_of_message` flag")]
MissingEndOfMessageChunk,
#[error("unexpected `end_of_message` found at chunk id `{position}`")]
MisplacedEndOfMessageChunk { position: usize },
#[error("payload length mismatch (expected `{expected}`, found `{found}`)")]
PayloadLengthMismatch { found: usize, expected: usize },
#[error("bad payload")]
BadPayload(#[from] TryPwbPacketFromSliceError),
}
impl TryFrom<Vec<Chunk>> for PwbV2Packet {
type Error = TryPwbPacketFromChunksError;
fn try_from(mut chunks: Vec<Chunk>) -> Result<Self, Self::Error> {
if chunks.is_empty() {
return Err(Self::Error::MissingChunk { position: 0 });
}
if let Some(index) = chunks
.iter()
.position(|c| c.board_id() != chunks[0].board_id())
{
return Err(Self::Error::DeviceIdMismatch {
found: chunks[index].board_id(),
expected: chunks[0].board_id(),
});
}
if let Some(index) = chunks
.iter()
.position(|c| c.after_id() != chunks[0].after_id())
{
return Err(Self::Error::ChannelIdMismatch {
found: chunks[index].after_id(),
expected: chunks[0].after_id(),
});
}
chunks.sort_unstable_by_key(|c| c.chunk_id);
if let Some(position) = chunks
.iter()
.enumerate()
.position(|(i, c)| usize::from(c.chunk_id) != i)
{
return Err(Self::Error::MissingChunk { position });
}
if !chunks.last().unwrap().is_end_of_message() {
return Err(Self::Error::MissingEndOfMessageChunk);
}
if let Some(position) = chunks
.iter()
.take(chunks.len() - 1)
.position(|c| c.is_end_of_message())
{
return Err(Self::Error::MisplacedEndOfMessageChunk { position });
}
if let Some(index) = chunks
.iter()
.take(chunks.len() - 1)
.position(|c| c.payload().len() != chunks[0].payload().len())
{
return Err(Self::Error::PayloadLengthMismatch {
found: chunks[index].payload().len(),
expected: chunks[0].payload().len(),
});
}
let max_items = chunks[0].payload().len() * chunks.len();
let payload = chunks
.into_iter()
.fold(Vec::with_capacity(max_items), |mut acc, item| {
acc.extend_from_slice(&item.payload);
acc
});
Ok(PwbV2Packet::try_from(&payload[..])?)
}
}
#[derive(Clone, Debug)]
pub enum PwbPacket {
V2(PwbV2Packet),
}
impl fmt::Display for PwbPacket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::V2(packet) => write!(f, "{packet}"),
}
}
}
impl PwbPacket {
pub fn packet_version(&self) -> u8 {
match self {
Self::V2(packet) => packet.packet_version(),
}
}
pub fn after_id(&self) -> AfterId {
match self {
Self::V2(packet) => packet.after_id(),
}
}
pub fn compression(&self) -> Compression {
match self {
Self::V2(packet) => packet.compression(),
}
}
pub fn trigger_source(&self) -> Trigger {
match self {
Self::V2(packet) => packet.trigger_source(),
}
}
pub fn board_id(&self) -> BoardId {
match self {
Self::V2(packet) => packet.board_id(),
}
}
pub fn trigger_delay(&self) -> u16 {
match self {
Self::V2(packet) => packet.trigger_delay(),
}
}
pub fn trigger_timestamp(&self) -> u64 {
match self {
Self::V2(packet) => packet.trigger_timestamp(),
}
}
pub fn last_sca_cell(&self) -> u16 {
match self {
Self::V2(packet) => packet.last_sca_cell(),
}
}
pub fn requested_samples(&self) -> usize {
match self {
Self::V2(packet) => packet.requested_samples(),
}
}
pub fn channels_sent(&self) -> &[ChannelId] {
match self {
Self::V2(packet) => packet.channels_sent(),
}
}
pub fn channels_over_threshold(&self) -> &[ChannelId] {
match self {
Self::V2(packet) => packet.channels_over_threshold(),
}
}
pub fn event_counter(&self) -> Option<u32> {
match self {
Self::V2(packet) => Some(packet.event_counter()),
}
}
pub fn fifo_max_depth(&self) -> Option<u16> {
match self {
Self::V2(packet) => Some(packet.fifo_max_depth()),
}
}
pub fn event_descriptor_write_depth(&self) -> Option<u8> {
match self {
Self::V2(packet) => Some(packet.event_descriptor_write_depth()),
}
}
pub fn event_descriptor_read_depth(&self) -> Option<u8> {
match self {
Self::V2(packet) => Some(packet.event_descriptor_read_depth()),
}
}
pub fn waveform_at(&self, channel: ChannelId) -> Option<&[i16]> {
match self {
Self::V2(packet) => packet.waveform_at(channel),
}
}
pub fn is_v2(&self) -> bool {
matches!(self, Self::V2(_))
}
}
impl TryFrom<&[u8]> for PwbPacket {
type Error = TryPwbPacketFromSliceError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
Ok(PwbPacket::V2(PwbV2Packet::try_from(slice)?))
}
}
impl TryFrom<Vec<Chunk>> for PwbPacket {
type Error = TryPwbPacketFromChunksError;
fn try_from(chunks: Vec<Chunk>) -> Result<Self, Self::Error> {
Ok(PwbPacket::V2(PwbV2Packet::try_from(chunks)?))
}
}
#[derive(Error, Debug)]
#[error("short slice (expected at least 68 samples, found `{found}`)")]
pub struct CalculateSuppressionBaselineError {
found: usize,
}
pub fn suppression_baseline(
_run_number: u32,
waveform: &[i16],
) -> Result<Option<i16>, CalculateSuppressionBaselineError> {
if waveform.len() < 68 {
return Err(CalculateSuppressionBaselineError {
found: waveform.len(),
});
}
let num = waveform[4..][..64]
.iter()
.map(|n| i32::from(*n))
.sum::<i32>();
Ok(Some((num / 64).try_into().unwrap()))
}
#[cfg(test)]
mod tests;