use crate::utils::convert::{
read_grib1_sign_magnitude_i16_from_bytes, read_ibm_f32_from_bytes, read_u24_from_bytes,
};
#[derive(Debug, Clone)]
pub struct Grib1BinaryDataSection {
data: Vec<u8>,
}
impl Grib1BinaryDataSection {
pub fn from_data(data: &[u8]) -> Result<Self, String> {
if data.len() < 11 {
return Err("BDS too short".to_string());
}
Ok(Grib1BinaryDataSection {
data: data.to_vec(),
})
}
pub fn length(&self) -> usize {
read_u24_from_bytes(&self.data, 0).unwrap_or(0) as usize
}
pub fn flags(&self) -> u8 {
self.data[3]
}
pub fn is_spherical_harmonics(&self) -> bool {
(self.flags() & 0x80) != 0
}
pub fn is_complex_packing(&self) -> bool {
(self.flags() & 0x40) != 0
}
pub fn is_integer_packing(&self) -> bool {
(self.flags() & 0x20) != 0
}
pub fn has_additional_flags(&self) -> bool {
(self.flags() & 0x10) != 0
}
pub fn binary_scale_factor(&self) -> i16 {
read_grib1_sign_magnitude_i16_from_bytes(&self.data, 4).unwrap_or(0)
}
pub fn reference_value(&self) -> f32 {
read_ibm_f32_from_bytes(&self.data, 6).unwrap_or(0.0)
}
pub fn num_bits(&self) -> u8 {
self.data[10]
}
pub fn unpack_data(
&self,
num_values: usize,
bitmap: Option<&[bool]>,
) -> Result<Vec<f64>, String> {
if self.is_spherical_harmonics() || self.is_complex_packing() {
return Err("Complex/spherical packing not yet supported".to_string());
}
let num_bits = self.num_bits() as usize;
if num_bits == 0 {
return Ok(vec![self.reference_value() as f64; num_values]);
}
if num_bits > 32 {
return Err(format!("Invalid number of bits: {}", num_bits));
}
let binary_scale = self.binary_scale_factor();
let reference = self.reference_value() as f64;
let scale_factor = 2.0f64.powi(binary_scale as i32);
let data_offset = 11;
let num_packed_values = if let Some(bmap) = bitmap {
bmap.iter().filter(|&&v| v).count()
} else {
num_values
};
let mut packed_values = Vec::with_capacity(num_packed_values);
let mut bit_offset = 0;
for _ in 0..num_packed_values {
let value = read_bits(&self.data, data_offset * 8 + bit_offset, num_bits);
packed_values.push(value);
bit_offset += num_bits;
}
let unpacked_values: Vec<f64> = packed_values
.into_iter()
.map(|x| reference + (x as f64 * scale_factor))
.collect();
if let Some(bmap) = bitmap {
let mut result = Vec::with_capacity(num_values);
let mut packed_idx = 0;
for &is_valid in bmap {
if is_valid {
result.push(unpacked_values[packed_idx]);
packed_idx += 1;
} else {
result.push(f64::NAN);
}
}
Ok(result)
} else {
Ok(unpacked_values)
}
}
}
fn read_bits(data: &[u8], bit_offset: usize, num_bits: usize) -> u32 {
if num_bits == 0 || num_bits > 32 {
return 0;
}
let byte_offset = bit_offset / 8;
let bit_in_byte = bit_offset % 8;
let bytes_needed = (bit_in_byte + num_bits + 7) / 8;
if byte_offset + bytes_needed > data.len() {
return 0;
}
let mut value = 0u64;
for i in 0..bytes_needed.min(8) {
value = (value << 8) | (data[byte_offset + i] as u64);
}
let total_bits = bytes_needed * 8;
let shift = total_bits - bit_in_byte - num_bits;
value >>= shift;
let mask = (1u64 << num_bits) - 1;
(value & mask) as u32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_read_bits() {
let data = vec![0xD3, 0xAC];
assert_eq!(read_bits(&data, 0, 4), 0b1101);
assert_eq!(read_bits(&data, 4, 4), 0b0011);
assert_eq!(read_bits(&data, 4, 8), 0b00111010);
assert_eq!(read_bits(&data, 5, 3), 0b011);
}
fn ieee_to_ibm_bytes(value: f32) -> [u8; 4] {
if value == 0.0 {
return [0x00, 0x00, 0x00, 0x00];
}
let sign = if value < 0.0 { 0x80 } else { 0x00 };
let abs_value = value.abs() as f64;
let exponent = ((abs_value.log2() / 4.0).ceil() as i32 + 64) as u8;
let mantissa = (abs_value / 16_f64.powi(exponent as i32 - 64) * (2_f64.powi(24))) as u32;
[
sign | (exponent & 0x7F),
((mantissa >> 16) & 0xFF) as u8,
((mantissa >> 8) & 0xFF) as u8,
(mantissa & 0xFF) as u8,
]
}
#[test]
fn test_bds_parsing() {
let mut data = vec![0u8; 20];
data[0..3].copy_from_slice(&[0x00, 0x00, 0x14]); data[3] = 0x00; data[4..6].copy_from_slice(&[0x00, 0x00]); data[6..10].copy_from_slice(&ieee_to_ibm_bytes(100.0)); data[10] = 8;
let bds = Grib1BinaryDataSection::from_data(&data).unwrap();
assert_eq!(bds.binary_scale_factor(), 0);
assert!((bds.reference_value() - 100.0).abs() < 0.01);
assert_eq!(bds.num_bits(), 8);
assert!(!bds.is_complex_packing());
}
#[test]
fn test_binary_scale_sign_magnitude_negative() {
let mut data = vec![0u8; 20];
data[0..3].copy_from_slice(&[0x00, 0x00, 0x14]); data[3] = 0x00; data[4..6].copy_from_slice(&[0x80, 0x05]); data[6..10].copy_from_slice(&ieee_to_ibm_bytes(0.0));
data[10] = 8;
let bds = Grib1BinaryDataSection::from_data(&data).unwrap();
assert_eq!(bds.binary_scale_factor(), -5);
}
#[test]
fn test_simple_unpacking() {
let mut data = vec![0u8; 14];
data[0..3].copy_from_slice(&[0x00, 0x00, 0x0E]); data[3] = 0x00; data[4..6].copy_from_slice(&[0x00, 0x00]); data[6..10].copy_from_slice(&ieee_to_ibm_bytes(0.0)); data[10] = 8; data[11] = 1;
data[12] = 2;
data[13] = 3;
let bds = Grib1BinaryDataSection::from_data(&data).unwrap();
let unpacked = bds.unpack_data(3, None).unwrap();
assert_eq!(unpacked.len(), 3);
assert!((unpacked[0] - 1.0).abs() < 0.01);
assert!((unpacked[1] - 2.0).abs() < 0.01);
assert!((unpacked[2] - 3.0).abs() < 0.01);
}
}