From 05f24ca21fd52e332e04f80a832fe4ef64e9c343 Mon Sep 17 00:00:00 2001 From: core Date: Sun, 5 Nov 2023 10:27:08 -0500 Subject: [PATCH] entire file loads now --- nexrad2/src/lib.rs | 87 +++++++++++++++++++++++++++++++++++++--- nexrad2/src/message31.rs | 80 ++++++++++++++++++++++++++++-------- 2 files changed, 144 insertions(+), 23 deletions(-) diff --git a/nexrad2/src/lib.rs b/nexrad2/src/lib.rs index 6831f37..b45cd12 100644 --- a/nexrad2/src/lib.rs +++ b/nexrad2/src/lib.rs @@ -1,6 +1,7 @@ //! # nexrad2 //! A parser for the NEXRAD II raw datafile format. +use std::collections::HashMap; use std::error::Error; use std::fmt::{Display, Formatter}; use std::io; @@ -10,7 +11,7 @@ use log::{debug, info, Record, trace, warn}; use crate::bzip::BzipDecoder; use crate::message::{FromBody, LEGACY_CTM_HEADER_LEN, Message, MESSAGE_BODY_SIZE, MESSAGE_HEADER_SIZE, MessageHeader}; use crate::message2::Msg02RDAStatusData; -use crate::message31::{Message31, Message31Header, MSG31_HEADER_LENGTH, VOLUME_DATA_LENGTH, VolumeData}; +use crate::message31::{DataMoment, ELEVATION_DATA_LENGTH, ElevationData, GenericDataMoment, Message31, Message31Header, MSG31_HEADER_LENGTH, RADIAL_DATA_LENGTH, RadialData, VOLUME_DATA_LENGTH, VolumeData}; pub mod message; pub mod bzip; @@ -228,6 +229,9 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result = None; + let mut elevation_data: Option = None; + let mut radial_data: Option = None; + let mut data_map = HashMap::new(); for db_pointer in &data_block_pointers { decompressed.seek(SeekFrom::Start(offset_pos + db_pointer)).map_err(|e| NexradParseError::Msg31DatablockSeekFailed(e))?; @@ -263,10 +267,79 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result { + trace!("reading elevation data"); + let mut elev_buf = [0u8; ELEVATION_DATA_LENGTH]; + decompressed.read_exact(&mut elev_buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?; + elevation_data = Some(ElevationData { + datablock_type: std::str::from_utf8(&elev_buf[0..1]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?.to_string(), + data_name: std::str::from_utf8(&elev_buf[1..4]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?.to_string(), + lrtup: u16::from_be_bytes(elev_buf[4..6].try_into().unwrap()), + atmos: elev_buf[6..8].try_into().unwrap(), + calib_const: f32::from_be_bytes(elev_buf[8..12].try_into().unwrap()), + }); + if elevation_data.as_ref().expect("unreachable").lrtup != ELEVATION_DATA_LENGTH as u16 { + let mut extra_buf = vec![0u8; (elevation_data.as_ref().expect("unreachable").lrtup as usize) - ELEVATION_DATA_LENGTH]; + decompressed.read_exact(&mut extra_buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?; + } + }, + "RAD" => { + trace!("reading radial data"); + let mut rad_buf = [0u8; RADIAL_DATA_LENGTH]; + decompressed.read_exact(&mut rad_buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?; + radial_data = Some(RadialData { + datablock_type: std::str::from_utf8(&rad_buf[0..1]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?.to_string(), + data_name: std::str::from_utf8(&rad_buf[1..4]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?.to_string(), + lrtup: u16::from_be_bytes(rad_buf[4..6].try_into().unwrap()), + unambiguous_range: u16::from_be_bytes(rad_buf[6..8].try_into().unwrap()), + noise_level_horz: f32::from_be_bytes(rad_buf[8..12].try_into().unwrap()), + noise_level_vert: f32::from_be_bytes(rad_buf[12..16].try_into().unwrap()), + nyquist_velocity: u16::from_be_bytes(rad_buf[16..18].try_into().unwrap()), + radial_flags: u16::from_be_bytes(rad_buf[18..20].try_into().unwrap()), + calib_const_horz_channel: f32::from_be_bytes(rad_buf[20..24].try_into().unwrap()), + calib_const_vert_channel: f32::from_be_bytes(rad_buf[24..28].try_into().unwrap()), + }); + if radial_data.as_ref().expect("unreachable").lrtup != RADIAL_DATA_LENGTH as u16 { + let mut extra_buf = vec![0u8; (radial_data.as_ref().expect("unreachable").lrtup as usize) - RADIAL_DATA_LENGTH]; + decompressed.read_exact(&mut extra_buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?; + } + }, + "REF" | "VEL" | "CFP" | "SW " | "ZDR" | "PHI" | "RHO" => { + trace!("reading {}", block_name); + let mut gdm_buf = [0u8; RADIAL_DATA_LENGTH]; + decompressed.read_exact(&mut gdm_buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?; + let gdm = GenericDataMoment { + datablock_type: std::str::from_utf8(&gdm_buf[0..1]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?.to_string(), + data_name: std::str::from_utf8(&gdm_buf[1..4]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?.to_string(), + reserved: u32::from_be_bytes(gdm_buf[4..8].try_into().unwrap()), + data_moment_gate_count: u16::from_be_bytes(gdm_buf[8..10].try_into().unwrap()), + data_moment_range: u16::from_be_bytes(gdm_buf[10..12].try_into().unwrap()), + data_moment_range_sample_interval: u16::from_be_bytes(gdm_buf[12..14].try_into().unwrap()), + tover: u16::from_be_bytes(gdm_buf[14..16].try_into().unwrap()), + snr_threshold: u16::from_be_bytes(gdm_buf[16..18].try_into().unwrap()), + control_flags: gdm_buf[18], + data_word_size: gdm_buf[19], + scale: f32::from_be_bytes(gdm_buf[20..24].try_into().unwrap()), + offset: f32::from_be_bytes(gdm_buf[24..28].try_into().unwrap()), + }; + + let ldm = ((gdm.data_moment_gate_count as usize) * (gdm.data_word_size as usize))/8; + + let mut buf = vec![0u8; ldm]; + + decompressed.read_exact(&mut buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?; + + data_map.insert(gdm.data_name.clone(), DataMoment { + gdm, + data: buf + }); }, - "ELV" => trace!("reading elevation data"), - "RAD" => trace!("reading radial data"), - "REF" | "VEL" | "CFP" | "SW " | "ZDR" | "PHI" | "RHO" => trace!("reading {}", block_name), unknown => warn!("unknown block type {}", unknown) } } @@ -274,13 +347,15 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result +} + +impl Debug for DataMoment { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DataMoment {{gdm: {:?}, data: <{} bytes omitted>}}", self.gdm, self.data.len()) + } +} + #[derive(Debug)] pub struct Message31 { pub header: Message31Header, - pub volume_info: Option + pub volume_info: Option, + pub elevation_info: Option, + pub radial_info: Option, + pub available_data: HashMap } \ No newline at end of file