This commit is contained in:
core 2023-11-04 15:41:31 -04:00
parent 33ca20410e
commit b813294ea5
Signed by: core
GPG Key ID: FDBF740DADDCEECF
4 changed files with 2428 additions and 17 deletions

2302
log.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,16 +6,19 @@ use std::fmt::{Display, Formatter};
use std::io; use std::io;
use std::io::{Cursor, Read, Seek, SeekFrom}; use std::io::{Cursor, Read, Seek, SeekFrom};
use std::str::Utf8Error; use std::str::Utf8Error;
use log::{debug, trace}; use log::{debug, info, Record, trace, warn};
use crate::bzip::BzipDecoder; use crate::bzip::BzipDecoder;
use crate::message::{LEGACY_CTM_HEADER_LEN, 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;
pub mod message; pub mod message;
pub mod bzip; pub mod bzip;
pub mod message2;
#[derive(Debug)] #[derive(Debug)]
pub struct Nexrad2Chunk { pub struct Nexrad2Chunk {
pub volume_header_record: VolumeHeaderRecord, pub volume_header_record: VolumeHeaderRecord,
pub chunks: Vec<Vec<Message>>
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -106,10 +109,18 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
trace!("Loaded - {:#?}", header); trace!("Loaded - {:#?}", header);
let mut records: Vec<Vec<Message>> = vec![];
loop { loop {
// LDM records // LDM records
let mut ldm_size_bytes = [0u8; 4]; let mut ldm_size_bytes = [0u8; 4];
cursor.read_exact(&mut ldm_size_bytes).map_err(|e| NexradParseError::LdmReadFailed(e))?; match cursor.read_exact(&mut ldm_size_bytes) {
Ok(_) => (),
Err(e) if matches!(e.kind(), io::ErrorKind::UnexpectedEof) => {
break;
},
Err(e) => return Err(NexradParseError::LdmReadFailed(e))
}
let ldm_size = i32::from_be_bytes(ldm_size_bytes).abs() as usize; let ldm_size = i32::from_be_bytes(ldm_size_bytes).abs() as usize;
trace!("Reading LDM record - {} bytes compressed", ldm_size); trace!("Reading LDM record - {} bytes compressed", ldm_size);
@ -132,7 +143,7 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
let mut decompressed = Cursor::new(decompressed_buf); let mut decompressed = Cursor::new(decompressed_buf);
let mut messages = 0; let mut messages = vec![];
loop { loop {
decompressed.seek(SeekFrom::Current(LEGACY_CTM_HEADER_LEN as i64)).map_err(|e| NexradParseError::TcmChunkReadFailed(e))?; decompressed.seek(SeekFrom::Current(LEGACY_CTM_HEADER_LEN as i64)).map_err(|e| NexradParseError::TcmChunkReadFailed(e))?;
@ -140,7 +151,9 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
let mut message_header = [0u8; MESSAGE_HEADER_SIZE]; let mut message_header = [0u8; MESSAGE_HEADER_SIZE];
match decompressed.read_exact(&mut message_header) { match decompressed.read_exact(&mut message_header) {
Ok(_) => (), Ok(_) => (),
Err(e) if matches!(e.kind(), io::ErrorKind::UnexpectedEof) => { break; }, Err(e) if matches!(e.kind(), io::ErrorKind::UnexpectedEof) => {
break;
},
Err(e) => return Err(NexradParseError::TcmChunkReadFailed(e)) Err(e) => return Err(NexradParseError::TcmChunkReadFailed(e))
} }
@ -155,22 +168,45 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
message_segment_num: u16::from_be_bytes(message_header[14..].try_into().unwrap()), message_segment_num: u16::from_be_bytes(message_header[14..].try_into().unwrap()),
}; };
if message_header.message_type == 0 {
debug!("Reached EOF on chunk");
break;
}
debug!("Message: {:#?}", message_header); debug!("Message: {:#?}", message_header);
let mut body_buf = vec![0u8; MESSAGE_BODY_SIZE]; let mut body_buf = vec![0u8; MESSAGE_BODY_SIZE];
decompressed.read_exact(&mut body_buf).map_err(|e| NexradParseError::TcmChunkReadFailed(e))?; decompressed.read_exact(&mut body_buf).map_err(|e| NexradParseError::TcmChunkReadFailed(e))?;
messages += 1; let message = match message_header.message_type {
2 => {
Message::Msg02(Msg02RDAStatusData::from_body(body_buf[0..68].try_into().unwrap())?)
},
unknown => {
warn!("unrecognized message type {}", unknown);
continue;
}
};
messages.push(message);
} }
debug!("{} messages loaded from chunk", messages); debug!("{} messages loaded from chunk", messages.len());
break; records.push(messages);
} }
let mut messages = 0;
for chunk in &records {
messages += chunk.len();
}
info!("File loaded successfully! {} messages loaded in {} chunks", messages, records.len());
Ok(Nexrad2Chunk { Ok(Nexrad2Chunk {
volume_header_record: header volume_header_record: header,
chunks: records
}) })
} }

View File

@ -1,4 +1,6 @@
pub const MSG_RDA_STATUS_DATA: u8 = 2; use crate::message2::Msg02RDAStatusData;
use crate::NexradParseError;
pub const MSG_RDA_PERFORMANCE_DATA: u8 = 3; pub const MSG_RDA_PERFORMANCE_DATA: u8 = 3;
pub const MSG_RDA_VOLUME_COVERAGE_DATA: u8 = 5; pub const MSG_RDA_VOLUME_COVERAGE_DATA: u8 = 5;
pub const MSG_RDA_CLUTTER_FILTER_BYPASS_MAP: u8 = 13; pub const MSG_RDA_CLUTTER_FILTER_BYPASS_MAP: u8 = 13;
@ -21,14 +23,14 @@ pub struct MessageHeader {
pub julian_date: u16, pub julian_date: u16,
pub millis_after_midnight: u32, pub millis_after_midnight: u32,
pub num_of_message_segments: u16, pub num_of_message_segments: u16,
pub message_segment_num: u16 pub message_segment_num: u16,
} }
pub trait FromBody<const BODY_SIZE: usize> {
fn from_body(body: [u8; BODY_SIZE]) -> Result<Self, NexradParseError> where Self: Sized;
}
#[derive(Debug)]
pub enum Message { pub enum Message {
/// Type02 Msg02(Msg02RDAStatusData)
RDAStatusData {
},
} }

71
nexrad2/src/message2.rs Normal file
View File

@ -0,0 +1,71 @@
use crate::message::FromBody;
use crate::NexradParseError;
pub const MSG_RDA_STATUS_DATA: u8 = 2;
#[derive(Debug)]
pub struct Msg02RDAStatusData {
rda_status: u16,
operability_status: u16,
control_status: u16,
aux_power_state: u16,
average_transmitter_power_watts: u16,
horiz_ref_calib_corr: u16,
data_tx_enabled: u16,
vcp: i16,
rdaca: u16,
rda_build_no: u16,
opmode: u16,
super_resolution_status: u16,
clutter_mitigation_status: u16,
rda_scan_data_flags: u16,
rda_alarm_summary: u16,
command_ack: u16,
channel_con_stat: u16,
spot_blank_stat: u16,
bypass_map_gen_date: u16,
bypass_map_gen_time: u16,
clutter_map_gen_date: u16,
clutter_map_gen_time: u16,
vert_ref_calib_corr: u16,
tps_status: u16,
rms_control_status: u16,
perf_check_status: u16,
alarm: u16,
extra: [u8; 8],
}
impl FromBody<68> for Msg02RDAStatusData {
fn from_body(body: [u8; 68]) -> Result<Self, NexradParseError> {
Ok(Self {
rda_status: u16::from_be_bytes(body[0..2].try_into().unwrap()),
operability_status: u16::from_be_bytes(body[2..4].try_into().unwrap()),
control_status: u16::from_be_bytes(body[4..6].try_into().unwrap()),
aux_power_state: u16::from_be_bytes(body[6..8].try_into().unwrap()),
average_transmitter_power_watts: u16::from_be_bytes(body[8..10].try_into().unwrap()),
horiz_ref_calib_corr: u16::from_be_bytes(body[10..12].try_into().unwrap()),
data_tx_enabled: u16::from_be_bytes(body[12..14].try_into().unwrap()),
vcp: i16::from_be_bytes(body[14..16].try_into().unwrap()),
rdaca: u16::from_be_bytes(body[16..18].try_into().unwrap()),
rda_build_no: u16::from_be_bytes(body[20..22].try_into().unwrap()),
opmode: u16::from_be_bytes(body[22..24].try_into().unwrap()),
super_resolution_status: u16::from_be_bytes(body[24..26].try_into().unwrap()),
clutter_mitigation_status: u16::from_be_bytes(body[26..28].try_into().unwrap()),
rda_scan_data_flags: u16::from_be_bytes(body[30..32].try_into().unwrap()),
rda_alarm_summary: u16::from_be_bytes(body[32..34].try_into().unwrap()),
command_ack: u16::from_be_bytes(body[34..36].try_into().unwrap()),
channel_con_stat: u16::from_be_bytes(body[36..38].try_into().unwrap()),
spot_blank_stat: u16::from_be_bytes(body[38..40].try_into().unwrap()),
bypass_map_gen_date: u16::from_be_bytes(body[40..42].try_into().unwrap()),
bypass_map_gen_time: u16::from_be_bytes(body[42..44].try_into().unwrap()),
clutter_map_gen_date: u16::from_be_bytes(body[44..46].try_into().unwrap()),
clutter_map_gen_time: u16::from_be_bytes(body[46..48].try_into().unwrap()),
vert_ref_calib_corr: u16::from_be_bytes(body[50..52].try_into().unwrap()),
tps_status: u16::from_be_bytes(body[52..54].try_into().unwrap()),
rms_control_status: u16::from_be_bytes(body[54..56].try_into().unwrap()),
perf_check_status: u16::from_be_bytes(body[56..58].try_into().unwrap()),
alarm: u16::from_be_bytes(body[58..60].try_into().unwrap()),
extra: body[60..].try_into().unwrap(),
})
}
}