entire file loads now
This commit is contained in:
parent
015c265b76
commit
05f24ca21f
|
@ -1,6 +1,7 @@
|
||||||
//! # nexrad2
|
//! # nexrad2
|
||||||
//! A parser for the NEXRAD II raw datafile format.
|
//! A parser for the NEXRAD II raw datafile format.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -10,7 +11,7 @@ use log::{debug, info, Record, trace, warn};
|
||||||
use crate::bzip::BzipDecoder;
|
use crate::bzip::BzipDecoder;
|
||||||
use crate::message::{FromBody, LEGACY_CTM_HEADER_LEN, Message, MESSAGE_BODY_SIZE, MESSAGE_HEADER_SIZE, MessageHeader};
|
use crate::message::{FromBody, LEGACY_CTM_HEADER_LEN, Message, MESSAGE_BODY_SIZE, MESSAGE_HEADER_SIZE, MessageHeader};
|
||||||
use crate::message2::Msg02RDAStatusData;
|
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 message;
|
||||||
pub mod bzip;
|
pub mod bzip;
|
||||||
|
@ -228,6 +229,9 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut volume_data: Option<VolumeData> = None;
|
let mut volume_data: Option<VolumeData> = None;
|
||||||
|
let mut elevation_data: Option<ElevationData> = None;
|
||||||
|
let mut radial_data: Option<RadialData> = None;
|
||||||
|
let mut data_map = HashMap::new();
|
||||||
|
|
||||||
for db_pointer in &data_block_pointers {
|
for db_pointer in &data_block_pointers {
|
||||||
decompressed.seek(SeekFrom::Start(offset_pos + db_pointer)).map_err(|e| NexradParseError::Msg31DatablockSeekFailed(e))?;
|
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<Nexrad2Chunk,
|
||||||
vcp: u16::from_be_bytes(volume_buf[40..42].try_into().unwrap()),
|
vcp: u16::from_be_bytes(volume_buf[40..42].try_into().unwrap()),
|
||||||
processing_status: u16::from_be_bytes(volume_buf[42..44].try_into().unwrap()),
|
processing_status: u16::from_be_bytes(volume_buf[42..44].try_into().unwrap()),
|
||||||
});
|
});
|
||||||
|
// read in any extra bytes we are currently ignoring
|
||||||
|
if volume_data.as_ref().expect("unreachable").lrtup != VOLUME_DATA_LENGTH as u16 {
|
||||||
|
let mut extra_buf = vec![0u8; (volume_data.as_ref().expect("unreachable").lrtup as usize) - VOLUME_DATA_LENGTH];
|
||||||
|
decompressed.read_exact(&mut extra_buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ELV" => {
|
||||||
|
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)
|
unknown => warn!("unknown block type {}", unknown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,13 +347,15 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
|
||||||
let message = Message::Msg31(Message31 {
|
let message = Message::Msg31(Message31 {
|
||||||
header: header_struct,
|
header: header_struct,
|
||||||
volume_info: volume_data,
|
volume_info: volume_data,
|
||||||
|
elevation_info: elevation_data,
|
||||||
|
radial_info: radial_data,
|
||||||
|
available_data: data_map
|
||||||
});
|
});
|
||||||
|
|
||||||
trace!("{:#?}", message);
|
trace!("{:#?}", message);
|
||||||
|
|
||||||
messages.push(message);
|
messages.push(message);
|
||||||
|
continue;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let body_size = MESSAGE_BODY_SIZE.max(message_header.message_size as usize);
|
let body_size = MESSAGE_BODY_SIZE.max(message_header.message_size as usize);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
pub const MSG31_HEADER_LENGTH: usize = 4 + 4 + 2 + 2 + 4 + 1 + 1 + 2 + 1 + 1 + 1 + 1 + 4 + 1 + 1 + 2;
|
pub const MSG31_HEADER_LENGTH: usize = 4 + 4 + 2 + 2 + 4 + 1 + 1 + 2 + 1 + 1 + 1 + 1 + 4 + 1 + 1 + 2;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -20,22 +23,6 @@ pub struct Message31Header {
|
||||||
pub data_block_count: u16
|
pub data_block_count: u16
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Message31Datablock {
|
|
||||||
pub datablock_type: String,
|
|
||||||
pub data_moment_name: String,
|
|
||||||
pub reserved: u32,
|
|
||||||
pub data_moment_gate_count: u16,
|
|
||||||
pub data_moment_range: u16,
|
|
||||||
pub data_moment_range_sample_interval: u16,
|
|
||||||
pub tover: u16,
|
|
||||||
pub snr_threshold: i16,
|
|
||||||
pub control_flags: u8,
|
|
||||||
pub data_word_size: u8,
|
|
||||||
pub scale: f32,
|
|
||||||
pub offset: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const VOLUME_DATA_LENGTH: usize = 1 + 3 + 2 + 1 + 1 + 4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 2 + 2;
|
pub const VOLUME_DATA_LENGTH: usize = 1 + 3 + 2 + 1 + 1 + 4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 2 + 2;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -58,8 +45,67 @@ pub struct VolumeData {
|
||||||
pub processing_status: u16
|
pub processing_status: u16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const ELEVATION_DATA_LENGTH: usize = 1 + 3 + 2 + 2 + 4;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ElevationData {
|
||||||
|
pub datablock_type: String,
|
||||||
|
pub data_name: String,
|
||||||
|
pub lrtup: u16,
|
||||||
|
pub atmos: [u8; 2],
|
||||||
|
pub calib_const: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const RADIAL_DATA_LENGTH: usize = 1 + 3 + 2 + 2 + 4 + 4 + 2 + 2 + 4 + 4;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RadialData {
|
||||||
|
pub datablock_type: String,
|
||||||
|
pub data_name: String,
|
||||||
|
pub lrtup: u16,
|
||||||
|
pub unambiguous_range: u16,
|
||||||
|
pub noise_level_horz: f32,
|
||||||
|
pub noise_level_vert: f32,
|
||||||
|
pub nyquist_velocity: u16,
|
||||||
|
pub radial_flags: u16,
|
||||||
|
pub calib_const_horz_channel: f32,
|
||||||
|
pub calib_const_vert_channel: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const GENERIC_DATA_MOMENT_LEN: usize = 1 + 3 + 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 4 + 4;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GenericDataMoment {
|
||||||
|
pub datablock_type: String,
|
||||||
|
pub data_name: String,
|
||||||
|
pub reserved: u32,
|
||||||
|
pub data_moment_gate_count: u16,
|
||||||
|
pub data_moment_range: u16,
|
||||||
|
pub data_moment_range_sample_interval: u16,
|
||||||
|
pub tover: u16,
|
||||||
|
pub snr_threshold: u16,
|
||||||
|
pub control_flags: u8,
|
||||||
|
pub data_word_size: u8,
|
||||||
|
pub scale: f32,
|
||||||
|
pub offset: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DataMoment {
|
||||||
|
pub gdm: GenericDataMoment,
|
||||||
|
pub data: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Message31 {
|
pub struct Message31 {
|
||||||
pub header: Message31Header,
|
pub header: Message31Header,
|
||||||
pub volume_info: Option<VolumeData>
|
pub volume_info: Option<VolumeData>,
|
||||||
|
pub elevation_info: Option<ElevationData>,
|
||||||
|
pub radial_info: Option<RadialData>,
|
||||||
|
pub available_data: HashMap<String, DataMoment>
|
||||||
}
|
}
|
Loading…
Reference in New Issue