use smol_str::SmolStr;
#[derive(Debug, Clone)]
pub enum DigitalSamples {
I8(Vec<i8>),
I16(Vec<i16>),
}
impl DigitalSamples {
pub fn len(&self) -> usize {
match self {
DigitalSamples::I8(values) => values.len(),
DigitalSamples::I16(values) => values.len(),
}
}
pub fn is_empty(&self) -> bool {
match self {
DigitalSamples::I8(values) => values.is_empty(),
DigitalSamples::I16(values) => values.is_empty(),
}
}
pub fn get_nth_bitstream(&self, bitstream_number: usize) -> Option<Vec<u8>> {
fn extract_bitstream<T>(
values: &[T],
bitstream_number: usize,
width_bits: usize,
) -> Option<Vec<u8>>
where
T: Copy + Into<i64>,
{
if bitstream_number >= width_bits {
return None;
}
let byte_index = bitstream_number / 8;
let bit_in_byte = bitstream_number % 8;
let mut output = Vec::with_capacity(values.len());
for &value in values {
let val_i64: i64 = value.into();
let byte = (val_i64 >> (8 * byte_index)) & 0xFF;
let bit = (byte >> (7 - bit_in_byte)) & 1;
output.push(bit as u8);
}
Some(output)
}
match self {
DigitalSamples::I8(values) => extract_bitstream(values, bitstream_number, 8),
DigitalSamples::I16(values) => extract_bitstream(values, bitstream_number, 16),
}
}
pub fn as_scope_digital8(&self) -> Option<Vec<u8>> {
const BIT_MAP: [usize; 8] = [7, 5, 3, 1, 15, 13, 11, 9]; let values = match self {
DigitalSamples::I16(values) => values,
_ => return None,
};
let mut mapped = Vec::with_capacity(values.len());
for value in values {
let bytes = value.to_le_bytes();
let mut out = 0u8;
for (d_index, &bit) in BIT_MAP.iter().enumerate() {
let byte_index = bit / 8;
let bit_in_byte = bit % 8;
let raw_byte = bytes[byte_index];
let bit_value = (raw_byte >> (7 - bit_in_byte)) & 1;
out |= bit_value << (7 - d_index);
}
mapped.push(out);
}
Some(mapped)
}
#[inline]
pub fn iter_normalized_values(&self) -> Box<dyn ExactSizeIterator<Item = Vec<u8>> + '_> {
match self {
DigitalSamples::I8(values) => Box::new(values.iter().map(|&value| {
let byte = value as u8;
(0..8).map(|bit| (byte >> (7 - bit)) & 1).collect()
})),
DigitalSamples::I16(values) => Box::new(values.iter().map(|&value| {
let value = value as u32;
let mut bits = Vec::with_capacity(16);
for bit in 0..8 {
bits.push(((value & 0xFF) as u8 >> (7 - bit)) & 1);
}
for bit in 0..8 {
bits.push((((value >> 8) & 0xFF) as u8 >> (7 - bit)) & 1);
}
bits
})),
}
}
#[inline]
pub fn iter_normalized_values_flat(&self) -> Box<dyn Iterator<Item = u8> + '_> {
match self {
DigitalSamples::I8(values) => Box::new(values.iter().flat_map(|&value| {
let byte = value as u8;
(0..8).map(move |bit| (byte >> (7 - bit)) & 1)
})),
DigitalSamples::I16(values) => Box::new(values.iter().flat_map(|&value| {
let value = value as u32;
let low_bits = (0..8).map(move |bit| ((value & 0xFF) as u8 >> (7 - bit)) & 1);
let high_bits =
(0..8).map(move |bit| (((value >> 8) & 0xFF) as u8 >> (7 - bit)) & 1);
low_bits.chain(high_bits)
})),
}
}
}
#[derive(Debug, Clone)]
pub struct DigitalWaveform {
pub source_name: SmolStr,
pub y_axis_bytes: DigitalSamples,
pub y_axis_units: SmolStr,
pub x_axis_spacing: f64,
pub x_axis_units: SmolStr,
pub trigger_index: f64,
}
impl DigitalWaveform {
pub fn record_length(&self) -> usize {
self.y_axis_bytes.len()
}
#[inline]
pub fn iter_normalized_values(&self) -> impl ExactSizeIterator<Item = Vec<u8>> {
self.y_axis_bytes.iter_normalized_values()
}
#[inline]
pub fn iter_normalized_values_flat(&self) -> impl Iterator<Item = u8> {
self.y_axis_bytes.iter_normalized_values_flat()
}
pub fn as_scope_digital8(&self) -> Option<Vec<u8>> {
self.y_axis_bytes.as_scope_digital8()
}
pub fn get_nth_bitstream(&self, bitstream_number: usize) -> Option<Vec<u8>> {
self.y_axis_bytes.get_nth_bitstream(bitstream_number)
}
}
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
mod tests {
use super::{DigitalSamples, DigitalWaveform};
use crate::data::Waveform;
use smol_str::SmolStr;
#[test]
fn record_length_digital_uses_sample_len() {
let waveform = DigitalWaveform {
source_name: SmolStr::new("d0"),
y_axis_bytes: DigitalSamples::I8(vec![1, 2, 3, 4]),
y_axis_units: SmolStr::new("NONE"),
x_axis_spacing: 1.0,
x_axis_units: SmolStr::new("s"),
trigger_index: 0.0,
};
assert_eq!(waveform.record_length(), 4);
assert_eq!(Waveform::Digital(waveform).record_length(), 4);
}
#[test]
fn digital_normalized_vertical_values_unpack_bits() {
let waveform = DigitalWaveform {
source_name: SmolStr::new("d0"),
y_axis_bytes: DigitalSamples::I8(vec![-95]),
y_axis_units: SmolStr::new("NONE"),
x_axis_spacing: 1.0,
x_axis_units: SmolStr::new("s"),
trigger_index: 0.0,
};
let values: Vec<Vec<u8>> = waveform.iter_normalized_values().collect();
assert_eq!(values, vec![vec![1, 0, 1, 0, 0, 0, 0, 1]]);
}
#[test]
fn digital_normalized_vertical_values_unpack_bits_i16() {
let waveform = DigitalWaveform {
source_name: SmolStr::new("dall"),
y_axis_bytes: DigitalSamples::I16(vec![0x1234]),
y_axis_units: SmolStr::new("NONE"),
x_axis_spacing: 1.0,
x_axis_units: SmolStr::new("s"),
trigger_index: 0.0,
};
let values: Vec<Vec<u8>> = waveform.iter_normalized_values().collect();
assert_eq!(
values,
vec![vec![0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0]]
);
}
#[test]
fn digital_i16_maps_to_scope_d0_d7() {
let samples = DigitalSamples::I16(vec![0x0010]);
let mapped = samples.as_scope_digital8().expect("mapping expected");
assert_eq!(mapped, vec![0x20]);
}
#[test]
fn digital_i8_get_nth_bitstream() {
let samples = DigitalSamples::I8(vec![-95]); let bit0 = samples.get_nth_bitstream(0).expect("bitstream expected");
let bit1 = samples.get_nth_bitstream(1).expect("bitstream expected");
let bit7 = samples.get_nth_bitstream(7).expect("bitstream expected");
assert_eq!(bit0, vec![1]);
assert_eq!(bit1, vec![0]);
assert_eq!(bit7, vec![1]);
}
#[test]
fn digital_i16_get_nth_bitstream() {
let samples = DigitalSamples::I16(vec![0x1234]);
let bit2 = samples.get_nth_bitstream(2).expect("bitstream expected");
let bit8 = samples.get_nth_bitstream(8).expect("bitstream expected");
assert_eq!(bit2, vec![1]);
assert_eq!(bit8, vec![0]);
}
#[test]
fn digital_get_nth_bitstream_out_of_range() {
let samples = DigitalSamples::I8(vec![0]);
assert!(samples.get_nth_bitstream(8).is_none());
let samples = DigitalSamples::I16(vec![0]);
assert!(samples.get_nth_bitstream(16).is_none());
}
#[test]
fn digital_normalized_values_flat_i8_matches_nested() {
let samples = DigitalSamples::I8(vec![-95]); let flat: Vec<u8> = samples.iter_normalized_values_flat().collect();
let nested_flat: Vec<u8> = samples
.iter_normalized_values()
.into_iter()
.flatten()
.collect();
assert_eq!(flat, nested_flat);
}
#[test]
fn digital_normalized_values_flat_i16_matches_nested() {
let samples = DigitalSamples::I16(vec![0x1234]);
let flat: Vec<u8> = samples.iter_normalized_values_flat().collect();
let nested_flat: Vec<u8> = samples
.iter_normalized_values()
.into_iter()
.flatten()
.collect();
assert_eq!(flat, nested_flat);
}
#[test]
fn digital_samples_is_empty_covers_i8_and_i16() {
assert!(DigitalSamples::I8(Vec::new()).is_empty());
assert!(DigitalSamples::I16(Vec::new()).is_empty());
assert!(!DigitalSamples::I8(vec![1]).is_empty());
assert!(!DigitalSamples::I16(vec![1]).is_empty());
}
#[test]
fn digital_waveform_iterators_and_scope_mapping() {
let waveform = DigitalWaveform {
source_name: SmolStr::new("d0"),
y_axis_bytes: DigitalSamples::I8(vec![0x01]),
y_axis_units: SmolStr::new("NONE"),
x_axis_spacing: 1.0,
x_axis_units: SmolStr::new("s"),
trigger_index: 0.0,
};
let flat: Vec<u8> = waveform.iter_normalized_values_flat().collect();
assert_eq!(flat.len(), 8);
assert!(waveform.as_scope_digital8().is_none());
assert!(waveform.get_nth_bitstream(0).is_some());
}
}