use std::fmt;
use thiserror::Error;
#[allow(unused_imports)]
use crate::alpha16::aw_map::TpcWirePosition;
pub mod aw_map;
pub const ADC16_RATE: f64 = 100e6;
pub const ADC32_RATE: f64 = 62.5e6;
pub const ADC_MAX: i16 = 32764;
pub const ADC_MIN: i16 = -32768;
#[derive(Error, Debug)]
#[error("unknown conversion from unsigned `{input}` to ChannelId")]
pub struct TryChannelIdFromUnsignedError {
input: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Adc16ChannelId(u8);
impl TryFrom<u8> for Adc16ChannelId {
type Error = TryChannelIdFromUnsignedError;
fn try_from(num: u8) -> Result<Self, Self::Error> {
if num > 15 {
Err(TryChannelIdFromUnsignedError { input: num })
} else {
Ok(Adc16ChannelId(num))
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Adc32ChannelId(u8);
impl TryFrom<u8> for Adc32ChannelId {
type Error = TryChannelIdFromUnsignedError;
fn try_from(num: u8) -> Result<Self, Self::Error> {
if num > 31 {
Err(TryChannelIdFromUnsignedError { input: num })
} else {
Ok(Adc32ChannelId(num))
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum ChannelId {
A16(Adc16ChannelId),
A32(Adc32ChannelId),
}
#[derive(Error, Debug)]
#[error("unknown conversion from unsigned `{input}` to ModuleId")]
pub struct TryModuleIdFromUnsignedError {
input: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ModuleId(u8);
impl TryFrom<u8> for ModuleId {
type Error = TryModuleIdFromUnsignedError;
fn try_from(num: u8) -> Result<Self, Self::Error> {
if num > 7 {
Err(TryModuleIdFromUnsignedError { input: num })
} else {
Ok(ModuleId(num))
}
}
}
#[derive(Error, Debug)]
#[error("unknown conversion from mac address `{input:?}` to BoardId")]
pub struct TryBoardIdFromMacAddressError {
input: [u8; 6],
}
#[derive(Error, Debug)]
#[error("unknown parsing from board name `{input}` to BoardId")]
pub struct ParseBoardIdError {
input: String,
}
const ALPHA16BOARDS: [(&str, [u8; 6]); 8] = [
("09", [216, 128, 57, 104, 55, 76]),
("10", [216, 128, 57, 104, 170, 37]),
("11", [216, 128, 57, 104, 172, 127]),
("12", [216, 128, 57, 104, 79, 167]),
("13", [216, 128, 57, 104, 202, 166]),
("14", [216, 128, 57, 104, 142, 130]),
("16", [216, 128, 57, 104, 111, 162]),
("18", [216, 128, 57, 104, 142, 82]),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BoardId {
name: &'static str,
mac_address: [u8; 6],
}
impl TryFrom<&str> for BoardId {
type Error = ParseBoardIdError;
fn try_from(name: &str) -> Result<Self, Self::Error> {
for pair in ALPHA16BOARDS {
if name == pair.0 {
return Ok(BoardId {
name: pair.0,
mac_address: pair.1,
});
}
}
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 pair in ALPHA16BOARDS {
if mac == pair.1 {
return Ok(BoardId {
name: pair.0,
mac_address: pair.1,
});
}
}
Err(TryBoardIdFromMacAddressError { input: mac })
}
}
impl BoardId {
pub fn name(&self) -> &str {
self.name
}
pub fn mac_address(&self) -> [u8; 6] {
self.mac_address
}
}
#[derive(Error, Debug)]
pub enum TryAdcPacketFromSliceError {
#[error("incomplete slice (expected at least `{min_expected}` bytes, found `{found}`)")]
IncompleteSlice { found: usize, min_expected: usize },
#[error("unknown packet type `{found}`")]
UnknownType { found: u8 },
#[error("unknown packet version `{found}`")]
UnknownVersion { found: u8 },
#[error("unknown module id")]
UnknownModuleId(#[from] TryModuleIdFromUnsignedError),
#[error("unknown channel number")]
UnknownChannelId(#[from] TryChannelIdFromUnsignedError),
#[error("zero-bytes mismatch (found `{found:?}`)")]
ZeroMismatch { found: [u8; 2] },
#[error("unknown mac address")]
UnknownMac(#[from] TryBoardIdFromMacAddressError),
#[error("suppression baseline mismatch (expected `{expected}`, found `{found}`)")]
BaselineMismatch { found: i16, expected: i16 },
#[error("bad keep_last `{found}` (limit was `{limit}`)")]
BadKeepLast { found: usize, limit: usize },
#[error("keep_bit mismatch (found `{found}`)")]
KeepBitMismatch { found: bool },
#[error("bad number of samples `{found}` (expected at least `{min}` and at most `{max}`)")]
BadNumberOfSamples {
found: usize,
min: usize,
max: usize,
},
}
#[derive(Clone, Debug)]
pub struct AdcV3Packet {
accepted_trigger: u16,
module_id: ModuleId,
channel_id: ChannelId,
requested_samples: usize,
event_timestamp: u64,
board_id: Option<BoardId>,
trigger_offset: Option<i32>,
build_timestamp: Option<u32>,
waveform: Vec<i16>,
suppression_baseline: i16,
keep_last: usize,
keep_bit: bool,
suppression_enabled: bool,
}
impl fmt::Display for AdcV3Packet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Packet type: {}", self.packet_type())?;
writeln!(f, "Packet version: {}", self.packet_version())?;
writeln!(f, "Accepted trigger: {}", self.accepted_trigger)?;
writeln!(f, "Module ID: {:?}", self.module_id)?;
let channel_id = match self.channel_id {
ChannelId::A16(channel) => format!("{channel:?}"),
ChannelId::A32(channel) => format!("{channel:?}"),
};
writeln!(f, "Channel ID: {channel_id}")?;
writeln!(f, "Requested samples: {}", self.requested_samples)?;
writeln!(f, "Event timestamp: {}", self.event_timestamp)?;
let mac_address = self
.board_id
.map_or("None".to_string(), |b| format!("{:?}", b.mac_address()));
writeln!(f, "MAC address: {mac_address}")?;
let trigger_offset = self
.trigger_offset
.map_or("None".to_string(), |v| v.to_string());
writeln!(f, "Trigger offset: {trigger_offset}",)?;
let build_timestamp = self
.build_timestamp
.map_or("None".to_string(), |v| v.to_string());
writeln!(f, "Build timestamp: {build_timestamp}",)?;
writeln!(f, "Waveform samples: {}", self.waveform.len())?;
writeln!(f, "Suppression baseline: {}", self.suppression_baseline)?;
writeln!(f, "Keep last: {}", self.keep_last)?;
writeln!(f, "Keep bit: {}", self.keep_bit)?;
write!(f, "Suppression enabled: {}", self.suppression_enabled)?;
Ok(())
}
}
impl AdcV3Packet {
pub fn packet_type(&self) -> u8 {
1
}
pub fn packet_version(&self) -> u8 {
3
}
pub fn accepted_trigger(&self) -> u16 {
self.accepted_trigger
}
pub fn module_id(&self) -> ModuleId {
self.module_id
}
pub fn channel_id(&self) -> ChannelId {
self.channel_id
}
pub fn requested_samples(&self) -> usize {
self.requested_samples
}
pub fn event_timestamp(&self) -> u64 {
self.event_timestamp
}
pub fn board_id(&self) -> Option<BoardId> {
self.board_id
}
pub fn trigger_offset(&self) -> Option<i32> {
self.trigger_offset
}
pub fn build_timestamp(&self) -> Option<u32> {
self.build_timestamp
}
pub fn waveform(&self) -> &[i16] {
&self.waveform
}
pub fn suppression_baseline(&self) -> i16 {
self.suppression_baseline
}
pub fn keep_last(&self) -> usize {
self.keep_last
}
pub fn keep_bit(&self) -> bool {
self.keep_bit
}
pub fn is_suppression_enabled(&self) -> bool {
self.suppression_enabled
}
}
const BASELINE_SAMPLES: usize = 64;
const MIN_KEEP_LAST: usize = (BASELINE_SAMPLES + 2) / 2 + 1;
impl TryFrom<&[u8]> for AdcV3Packet {
type Error = TryAdcPacketFromSliceError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() < 16 {
return Err(Self::Error::IncompleteSlice {
found: slice.len(),
min_expected: 16,
});
}
if slice[0] != 1 {
return Err(Self::Error::UnknownType { found: slice[0] });
}
if slice[1] != 3 {
return Err(Self::Error::UnknownVersion { found: slice[1] });
}
let accepted_trigger = slice[2..4].try_into().unwrap();
let accepted_trigger = u16::from_be_bytes(accepted_trigger);
let module_id = ModuleId::try_from(slice[4])?;
let channel_id = slice[5];
let channel_id = if channel_id < 128 {
ChannelId::A16(channel_id.try_into()?)
} else {
ChannelId::A32((channel_id - 128).try_into()?)
};
let requested_samples = slice[6..8].try_into().unwrap();
let requested_samples = u16::from_be_bytes(requested_samples).into();
let lsw_event_timestamp = slice[8..12].try_into().unwrap();
let suppression_baseline = slice[slice.len() - 2..].try_into().unwrap();
let suppression_baseline = i16::from_be_bytes(suppression_baseline);
let footer = slice[slice.len() - 4..][..2].try_into().unwrap();
let footer = u16::from_be_bytes(footer);
let keep_last = usize::from(footer & 0xFFF);
let keep_bit = (footer >> 12) & 1 == 1;
let suppression_enabled = (footer >> 13) & 1 == 1;
if slice.len() == 16 {
if !suppression_enabled {
return Err(Self::Error::IncompleteSlice {
found: 16,
min_expected: 36,
});
}
if keep_bit {
return Err(Self::Error::KeepBitMismatch { found: keep_bit });
}
if keep_last != 0 {
return Err(Self::Error::BadKeepLast {
found: keep_last,
limit: 0,
});
}
return Ok(AdcV3Packet {
accepted_trigger,
module_id,
channel_id,
requested_samples,
event_timestamp: u32::from_be_bytes(lsw_event_timestamp).into(),
board_id: None,
trigger_offset: None,
build_timestamp: None,
waveform: Vec::new(),
keep_last,
suppression_baseline,
keep_bit,
suppression_enabled,
});
}
if slice.len() < 36 {
return Err(Self::Error::IncompleteSlice {
found: slice.len(),
min_expected: 36,
});
}
if slice[12..14] != [0, 0] {
return Err(Self::Error::ZeroMismatch {
found: slice[12..14].try_into().unwrap(),
});
}
let board_id: [u8; 6] = slice[14..20].try_into().unwrap();
let board_id = BoardId::try_from(board_id)?;
let msw_event_timestamp = slice[20..24].try_into().unwrap();
let event_timestamp = [msw_event_timestamp, lsw_event_timestamp].concat();
let event_timestamp = event_timestamp.try_into().unwrap();
let event_timestamp = u64::from_be_bytes(event_timestamp);
let trigger_offset = slice[24..28].try_into().unwrap();
let trigger_offset = i32::from_be_bytes(trigger_offset);
let build_timestamp = slice[28..32].try_into().unwrap();
let build_timestamp = u32::from_be_bytes(build_timestamp);
let waveform_bytes = slice.len() - 36;
if waveform_bytes % 2 != 0 {
return Err(Self::Error::IncompleteSlice {
found: waveform_bytes + 36,
min_expected: waveform_bytes + 37,
});
}
let waveform: Vec<i16> = slice[32..][..waveform_bytes]
.chunks_exact(2)
.map(|b| i16::from_be_bytes(b.try_into().unwrap()))
.collect();
if waveform.len() < BASELINE_SAMPLES {
return Err(Self::Error::BadNumberOfSamples {
found: waveform.len(),
min: BASELINE_SAMPLES,
max: requested_samples - 2,
});
}
let data_baseline = {
let num = waveform[..BASELINE_SAMPLES]
.iter()
.map(|n| i32::from(*n))
.sum::<i32>();
let d = num / 64;
if num % 64 < 0 {
d - 1
} else {
d
}
};
if data_baseline != suppression_baseline.into() {
return Err(Self::Error::BaselineMismatch {
found: suppression_baseline,
expected: data_baseline.try_into().unwrap(),
});
}
if suppression_enabled {
if !keep_bit {
return Err(Self::Error::KeepBitMismatch { found: keep_bit });
}
if keep_last < MIN_KEEP_LAST {
return Err(Self::Error::BadKeepLast {
found: keep_last,
limit: MIN_KEEP_LAST,
});
}
let last_index = (keep_last - 1) * 2 - 2;
if waveform.len() <= last_index {
return Err(Self::Error::BadNumberOfSamples {
found: waveform.len(),
min: last_index + 1,
max: requested_samples - 2,
});
}
if waveform.len() > requested_samples - 2 {
return Err(Self::Error::BadNumberOfSamples {
found: waveform.len(),
min: last_index + 1,
max: requested_samples - 2,
});
}
} else {
if keep_bit {
if keep_last < MIN_KEEP_LAST {
return Err(Self::Error::BadKeepLast {
found: keep_last,
limit: MIN_KEEP_LAST,
});
}
let last_index = (keep_last - 1) * 2 - 2;
if waveform.len() <= last_index {
return Err(Self::Error::BadNumberOfSamples {
found: waveform.len(),
min: last_index + 1,
max: requested_samples - 2,
});
}
} else if keep_last != 0 {
return Err(Self::Error::BadKeepLast {
found: keep_last,
limit: 0,
});
}
if waveform.len() != requested_samples - 2 {
return Err(Self::Error::BadNumberOfSamples {
found: waveform.len(),
min: requested_samples - 2,
max: requested_samples - 2,
});
}
}
Ok(AdcV3Packet {
accepted_trigger,
module_id,
channel_id,
requested_samples,
event_timestamp,
board_id: Some(board_id),
trigger_offset: Some(trigger_offset),
build_timestamp: Some(build_timestamp),
waveform,
keep_last,
suppression_baseline,
keep_bit,
suppression_enabled,
})
}
}
#[derive(Clone, Debug)]
pub enum AdcPacket {
V3(AdcV3Packet),
}
impl fmt::Display for AdcPacket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::V3(packet) => write!(f, "{packet}"),
}
}
}
impl AdcPacket {
pub fn packet_type(&self) -> u8 {
match self {
Self::V3(packet) => packet.packet_type(),
}
}
pub fn packet_version(&self) -> u8 {
match self {
Self::V3(packet) => packet.packet_version(),
}
}
pub fn accepted_trigger(&self) -> u16 {
match self {
Self::V3(packet) => packet.accepted_trigger(),
}
}
pub fn module_id(&self) -> ModuleId {
match self {
Self::V3(packet) => packet.module_id(),
}
}
pub fn channel_id(&self) -> ChannelId {
match self {
Self::V3(packet) => packet.channel_id(),
}
}
pub fn requested_samples(&self) -> usize {
match self {
Self::V3(packet) => packet.requested_samples(),
}
}
pub fn event_timestamp(&self) -> u64 {
match self {
Self::V3(packet) => packet.event_timestamp(),
}
}
pub fn board_id(&self) -> Option<BoardId> {
match self {
Self::V3(packet) => packet.board_id(),
}
}
pub fn trigger_offset(&self) -> Option<i32> {
match self {
Self::V3(packet) => packet.trigger_offset(),
}
}
pub fn build_timestamp(&self) -> Option<u32> {
match self {
Self::V3(packet) => packet.build_timestamp(),
}
}
pub fn waveform(&self) -> &[i16] {
match self {
Self::V3(packet) => packet.waveform(),
}
}
pub fn suppression_baseline(&self) -> Option<i16> {
match self {
Self::V3(packet) => Some(packet.suppression_baseline()),
}
}
pub fn keep_last(&self) -> Option<usize> {
match self {
Self::V3(packet) => Some(packet.keep_last()),
}
}
pub fn keep_bit(&self) -> Option<bool> {
match self {
Self::V3(packet) => Some(packet.keep_bit()),
}
}
pub fn is_suppression_enabled(&self) -> Option<bool> {
match self {
Self::V3(packet) => Some(packet.is_suppression_enabled()),
}
}
pub fn is_v3(&self) -> bool {
matches!(self, Self::V3(_))
}
}
impl TryFrom<&[u8]> for AdcPacket {
type Error = TryAdcPacketFromSliceError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
Ok(AdcPacket::V3(AdcV3Packet::try_from(slice)?))
}
}
#[cfg(test)]
mod tests;