use super::las::{LASF_SPEC, LasHeader};
use crate::{Error, Result};
use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
pub struct WavePacketDescriptor {
pub index: u8,
pub bits_per_sample: u8,
pub compression_type: u8,
pub number_of_samples: u32,
pub temporal_sample_spacing: u32,
pub digitizer_gain: f64,
pub digitizer_offset: f64,
}
impl WavePacketDescriptor {
pub fn from_header(h: &LasHeader) -> Vec<Self> {
let mut out: Vec<Self> = h
.all_vlrs()
.filter(|v| v.user_id == LASF_SPEC && (100..=354).contains(&v.record_id) && v.data.len() >= 26)
.map(|v| {
let b = &v.data;
let u32_at = |o: usize| u32::from_le_bytes(b[o..o + 4].try_into().unwrap_or_default());
let f64_at = |o: usize| f64::from_le_bytes(b[o..o + 8].try_into().unwrap_or_default());
Self {
index: (v.record_id - 99).min(255) as u8,
bits_per_sample: b[0],
compression_type: b[1],
number_of_samples: u32_at(2),
temporal_sample_spacing: u32_at(6),
digitizer_gain: f64_at(10),
digitizer_offset: f64_at(18),
}
})
.collect();
out.sort_by_key(|d| d.index);
out
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Waveform {
pub descriptor: WavePacketDescriptor,
pub samples: Vec<u32>,
}
impl Waveform {
pub fn decode(descriptor: WavePacketDescriptor, bytes: &[u8]) -> Result<Self> {
let bits = descriptor.bits_per_sample as usize;
if bits == 0 || bits > 32 {
return Err(Error::InvalidData(format!("{bits} bits per waveform sample")));
}
if descriptor.compression_type != 0 {
return Err(Error::Unsupported(format!("waveform compression type {}", descriptor.compression_type)));
}
let n = (descriptor.number_of_samples as usize).min(bytes.len() * 8 / bits);
let samples = match bits {
8 => bytes[..n].iter().map(|b| *b as u32).collect(),
16 => bytes.as_chunks::<2>().0.iter().take(n).map(|c| u16::from_le_bytes(*c) as u32).collect(),
32 => bytes.as_chunks::<4>().0.iter().take(n).map(|c| u32::from_le_bytes(*c)).collect(),
_ => (0..n)
.map(|i| {
let mut v = 0u64;
for k in 0..bits {
let bit = i * bits + k;
v |= (((bytes[bit / 8] >> (bit % 8)) & 1) as u64) << k;
}
v as u32
})
.collect(),
};
Ok(Self { descriptor, samples })
}
pub fn volts(&self) -> Vec<f64> {
self.samples.iter().map(|s| self.descriptor.digitizer_offset + self.descriptor.digitizer_gain * *s as f64).collect()
}
pub fn times(&self) -> Vec<f64> {
(0..self.samples.len()).map(|i| i as f64 * self.descriptor.temporal_sample_spacing as f64).collect()
}
}