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::{Cursor, Read, Seek, SeekFrom};
use std::str::Utf8Error;
use log::{debug, trace};
use log::{debug, info, Record, trace, warn};
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 bzip;
pub mod message2;
#[derive(Debug)]
pub struct Nexrad2Chunk {
pub volume_header_record: VolumeHeaderRecord,
pub chunks: Vec<Vec<Message>>
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -106,10 +109,18 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
trace!("Loaded - {:#?}", header);
let mut records: Vec<Vec<Message>> = vec![];
loop {
// LDM records
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;
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 messages = 0;
let mut messages = vec![];
loop {
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];
match decompressed.read_exact(&mut message_header) {
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))
}
@ -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()),
};
if message_header.message_type == 0 {
debug!("Reached EOF on chunk");
break;
}
debug!("Message: {:#?}", message_header);
let mut body_buf = vec![0u8; MESSAGE_BODY_SIZE];
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 {
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_VOLUME_COVERAGE_DATA: u8 = 5;
pub const MSG_RDA_CLUTTER_FILTER_BYPASS_MAP: u8 = 13;
@ -21,14 +23,14 @@ pub struct MessageHeader {
pub julian_date: u16,
pub millis_after_midnight: u32,
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 {
/// Type02
RDAStatusData {
},
Msg02(Msg02RDAStatusData)
}

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(),
})
}
}