use crate::codec::{ChannelParams, CodecId};
pub fn pick_codec(sample: &[f32], params: &ChannelParams) -> CodecId {
if sample.is_empty() {
return CodecId::LosslessF32BssZstd;
}
if let Some(bits) = params.adc_bits {
if (1..=32).contains(&bits) && params.range > 0 && all_quantized(sample, params.range, bits)
{
return CodecId::AdcBitpack;
}
}
CodecId::LosslessF32BssZstd
}
fn all_quantized(values: &[f32], range: u32, adc_bits: u8) -> bool {
let denom = if adc_bits >= 32 {
u32::MAX as f64 + 1.0
} else {
(1u32 << adc_bits) as f64
};
let scale = range as f64 / denom;
if scale == 0.0 || !scale.is_finite() {
return false;
}
let tolerance = 1e-3_f64;
values.iter().all(|&x| {
let q = (x as f64) / scale;
(q - q.round()).abs() <= tolerance
})
}
#[cfg(test)]
mod tests {
use super::*;
fn params_with(range: u32, adc_bits: Option<u8>) -> ChannelParams {
ChannelParams {
name: "ch".into(),
stored_bits: 32,
range,
log_decades: (0.0, 0.0),
adc_bits,
signed: false,
}
}
#[test]
fn picks_adc_bitpack_for_quantized_data() {
let scale = (1u32 << 22) as f64 / (1u32 << 22) as f64; let sample: Vec<f32> = (0..256).map(|i| (i as f64 * scale) as f32).collect();
let p = params_with(1 << 22, Some(22));
assert_eq!(pick_codec(&sample, &p), CodecId::AdcBitpack);
}
#[test]
fn falls_back_to_bss_for_unmixed_data() {
let sample: Vec<f32> = (0..256)
.map(|i| (i as f32) * 0.123_456 + 1.7)
.collect();
let p = params_with(1 << 22, Some(22));
assert_eq!(pick_codec(&sample, &p), CodecId::LosslessF32BssZstd);
}
#[test]
fn falls_back_when_adc_bits_unknown() {
let sample: Vec<f32> = (0..32).map(|i| i as f32).collect();
let p = params_with(1 << 22, None);
assert_eq!(pick_codec(&sample, &p), CodecId::LosslessF32BssZstd);
}
#[test]
fn empty_sample_picks_lossless_default() {
let p = params_with(1 << 22, Some(22));
assert_eq!(pick_codec(&[], &p), CodecId::LosslessF32BssZstd);
}
}