volume data loading
This commit is contained in:
parent
8dacc8a9ba
commit
015c265b76
|
@ -122,6 +122,23 @@ let selected_mode = "INOP";
|
||||||
|
|
||||||
let delay_string = "DELAY UNAVAILABLE";
|
let delay_string = "DELAY UNAVAILABLE";
|
||||||
|
|
||||||
|
// dataserver status:
|
||||||
|
// 0. disconnected
|
||||||
|
// 1. disconnected, one-off
|
||||||
|
// 2. track initializing
|
||||||
|
// 3. tracked
|
||||||
|
// 4. track lost
|
||||||
|
|
||||||
|
let data_status = 0;
|
||||||
|
|
||||||
|
function statusString() {
|
||||||
|
if (data_status === 0) { return "DISCONNECTED"; }
|
||||||
|
else if (data_status === 1) { return "ONEOFF"; }
|
||||||
|
else if (data_status === 2) { return "TRACK INITIALIZING"; }
|
||||||
|
else if (data_status === 3) { return "TRACKING"; }
|
||||||
|
else if (data_status === 4) { return "TRACK LOST"; }
|
||||||
|
}
|
||||||
|
|
||||||
let display_buf = [];
|
let display_buf = [];
|
||||||
|
|
||||||
function recalcDisplayBuf() {
|
function recalcDisplayBuf() {
|
||||||
|
@ -160,6 +177,7 @@ async function load() {
|
||||||
cmd_err("");
|
cmd_err("");
|
||||||
|
|
||||||
ar2 = loaded;
|
ar2 = loaded;
|
||||||
|
data_status = 1;
|
||||||
});
|
});
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
}
|
}
|
||||||
|
@ -284,6 +302,7 @@ function reRender() {
|
||||||
ctx.fillText("RADAR SITE", xfull - 75, y0 + 50);
|
ctx.fillText("RADAR SITE", xfull - 75, y0 + 50);
|
||||||
ctx.fillText(`${site_string}`, xfull - 75, y0 + 50 + preferences.FCS);
|
ctx.fillText(`${site_string}`, xfull - 75, y0 + 50 + preferences.FCS);
|
||||||
ctx.fillText(`${delay_string}`, xfull - 75, y0 + 50 + preferences.FCS * 2);
|
ctx.fillText(`${delay_string}`, xfull - 75, y0 + 50 + preferences.FCS * 2);
|
||||||
|
ctx.fillText(`DATA SERVER ${statusString()}`, xfull - 75, y0 + 50 + preferences.FCS * 3);
|
||||||
|
|
||||||
ctx.fillText("MODE", xfull - 75, y0 + canvas.height / 3);
|
ctx.fillText("MODE", xfull - 75, y0 + canvas.height / 3);
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,12 @@ 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};
|
||||||
|
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod bzip;
|
pub mod bzip;
|
||||||
pub mod message2;
|
pub mod message2;
|
||||||
|
pub mod message31;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
@ -61,7 +63,13 @@ pub enum NexradParseError {
|
||||||
TcmChunkReadFailed(io::Error),
|
TcmChunkReadFailed(io::Error),
|
||||||
FailedToReadFile(io::Error),
|
FailedToReadFile(io::Error),
|
||||||
LdmReadFailed(io::Error),
|
LdmReadFailed(io::Error),
|
||||||
MissingMsg02
|
MissingMsg02,
|
||||||
|
Msg31HeaderReadFailed(io::Error),
|
||||||
|
Msg31HeaderInvalidRadarID(Utf8Error),
|
||||||
|
Msg31HeaderPointerReadFailed(io::Error),
|
||||||
|
Msg31DatablockSeekFailed(io::Error),
|
||||||
|
Msg31MomentReadFailed(io::Error),
|
||||||
|
Msg31MomentStringInvalid(Utf8Error)
|
||||||
}
|
}
|
||||||
impl Display for NexradParseError {
|
impl Display for NexradParseError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
@ -78,7 +86,13 @@ impl Display for NexradParseError {
|
||||||
Self::TcmChunkReadFailed(e) => write!(f, "tcm chunk read failed: {}", e),
|
Self::TcmChunkReadFailed(e) => write!(f, "tcm chunk read failed: {}", e),
|
||||||
Self::FailedToReadFile(e) => write!(f, "failed to read file: {}", e),
|
Self::FailedToReadFile(e) => write!(f, "failed to read file: {}", e),
|
||||||
Self::LdmReadFailed(e) => write!(f, "ldm read failed: {}", e),
|
Self::LdmReadFailed(e) => write!(f, "ldm read failed: {}", e),
|
||||||
Self::MissingMsg02 => write!(f, "missing RDA status data (message type 02) in metadata header")
|
Self::MissingMsg02 => write!(f, "missing RDA status data (message type 02) in metadata header"),
|
||||||
|
Self::Msg31HeaderReadFailed(e) => write!(f, "type 31 header read failed: {}", e),
|
||||||
|
Self::Msg31HeaderInvalidRadarID(e) => write!(f, "type 31 header invalid: invalid radar ID: {}", e),
|
||||||
|
Self::Msg31HeaderPointerReadFailed(e) => write!(f, "type 31 header invalid: pointer read failed: {}", e),
|
||||||
|
Self::Msg31DatablockSeekFailed(e) => write!(f, "type 31 datablock invalid: failed to seek: {}", e),
|
||||||
|
Self::Msg31MomentReadFailed(e) => write!(f, "type 31 datablock invalid: moment read failed: {}", e),
|
||||||
|
Self::Msg31MomentStringInvalid(e) => write!(f, "type 31 moment invalid: invalid string: {}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,6 +187,102 @@ 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 == 31 {
|
||||||
|
// msg31 fun
|
||||||
|
let offset_pos = decompressed.position();
|
||||||
|
|
||||||
|
let mut header = [0u8; MSG31_HEADER_LENGTH];
|
||||||
|
decompressed.read_exact(&mut header).map_err(|e| NexradParseError::Msg31HeaderReadFailed(e))?;
|
||||||
|
|
||||||
|
let radar_identifier_bytes = &header[0..4];
|
||||||
|
let radar_identifier = std::str::from_utf8(radar_identifier_bytes).map_err(|e| NexradParseError::Msg31HeaderInvalidRadarID(e))?;
|
||||||
|
|
||||||
|
let header_struct = Message31Header {
|
||||||
|
radar_identifier: radar_identifier.to_string(),
|
||||||
|
collection_time: u32::from_be_bytes(header[4..8].try_into().unwrap()),
|
||||||
|
date: u16::from_be_bytes(header[8..10].try_into().unwrap()),
|
||||||
|
azimuth_number: u16::from_be_bytes(header[10..12].try_into().unwrap()),
|
||||||
|
azimuth_angle: f32::from_be_bytes(header[12..16].try_into().unwrap()),
|
||||||
|
compression_indicator: header[16],
|
||||||
|
spare: header[17],
|
||||||
|
radial_length: u16::from_be_bytes(header[18..20].try_into().unwrap()),
|
||||||
|
azimuth_resolution_spacing: header[20],
|
||||||
|
radial_status: header[21],
|
||||||
|
elevation_number: header[22],
|
||||||
|
cut_sector_number: header[23],
|
||||||
|
elevation_angle: f32::from_be_bytes(header[24..28].try_into().unwrap()),
|
||||||
|
radial_spot_blanking_status: header[28],
|
||||||
|
azimuth_indexing_mode: header[29],
|
||||||
|
data_block_count: u16::from_be_bytes(header[30..32].try_into().unwrap()),
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("{:#?}", header_struct);
|
||||||
|
trace!("loading {} datablocks", header_struct.data_block_count);
|
||||||
|
|
||||||
|
let mut data_block_pointers = vec![];
|
||||||
|
let mut pointer_buf = [0u8; 4];
|
||||||
|
|
||||||
|
for _ in 0..header_struct.data_block_count {
|
||||||
|
decompressed.read_exact(&mut pointer_buf).map_err(|e| NexradParseError::Msg31HeaderPointerReadFailed(e))?;
|
||||||
|
data_block_pointers.push(u32::from_be_bytes(pointer_buf) as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut volume_data: Option<VolumeData> = None;
|
||||||
|
|
||||||
|
for db_pointer in &data_block_pointers {
|
||||||
|
decompressed.seek(SeekFrom::Start(offset_pos + db_pointer)).map_err(|e| NexradParseError::Msg31DatablockSeekFailed(e))?;
|
||||||
|
|
||||||
|
// get the type and name and use it to determine what to read
|
||||||
|
let mut type_name_buf = [0u8; 4];
|
||||||
|
decompressed.read_exact(&mut type_name_buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?;
|
||||||
|
|
||||||
|
decompressed.seek(SeekFrom::Current(-4)).map_err(|e| NexradParseError::Msg31DatablockSeekFailed(e))?;
|
||||||
|
|
||||||
|
let block_name = std::str::from_utf8(&type_name_buf[1..]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?;
|
||||||
|
|
||||||
|
match block_name {
|
||||||
|
"VOL" => {
|
||||||
|
trace!("reading volume data");
|
||||||
|
let mut volume_buf = [0u8; VOLUME_DATA_LENGTH];
|
||||||
|
decompressed.read_exact(&mut volume_buf).map_err(|e| NexradParseError::Msg31MomentReadFailed(e))?;
|
||||||
|
volume_data = Some(VolumeData {
|
||||||
|
datablock_type: std::str::from_utf8(&volume_buf[0..1]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?.to_string(),
|
||||||
|
data_name: std::str::from_utf8(&volume_buf[1..4]).map_err(|e| NexradParseError::Msg31MomentStringInvalid(e))?.to_string(),
|
||||||
|
lrtup: u16::from_be_bytes(volume_buf[4..6].try_into().unwrap()),
|
||||||
|
version_major: volume_buf[6],
|
||||||
|
version_minor: volume_buf[7],
|
||||||
|
lat: f32::from_be_bytes(volume_buf[8..12].try_into().unwrap()),
|
||||||
|
long: f32::from_be_bytes(volume_buf[12..16].try_into().unwrap()),
|
||||||
|
site_height: u16::from_be_bytes(volume_buf[16..18].try_into().unwrap()),
|
||||||
|
feedhorn_height: u16::from_be_bytes(volume_buf[18..20].try_into().unwrap()),
|
||||||
|
calibration_constant: f32::from_be_bytes(volume_buf[20..24].try_into().unwrap()),
|
||||||
|
shvtx_power_hor: f32::from_be_bytes(volume_buf[24..28].try_into().unwrap()),
|
||||||
|
shvtx_power_vert: f32::from_be_bytes(volume_buf[28..32].try_into().unwrap()),
|
||||||
|
system_diff_ref: f32::from_be_bytes(volume_buf[32..36].try_into().unwrap()),
|
||||||
|
initial_system_diff_phase: f32::from_be_bytes(volume_buf[36..40].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()),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = Message::Msg31(Message31 {
|
||||||
|
header: header_struct,
|
||||||
|
volume_info: volume_data,
|
||||||
|
});
|
||||||
|
|
||||||
|
trace!("{:#?}", message);
|
||||||
|
|
||||||
|
messages.push(message);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
let mut body_buf = vec![0u8; body_size];
|
let mut body_buf = vec![0u8; body_size];
|
||||||
|
@ -188,7 +298,7 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
|
||||||
Message::Msg02(Msg02RDAStatusData::from_body(body_buf[0..68].try_into().unwrap())?)
|
Message::Msg02(Msg02RDAStatusData::from_body(body_buf[0..68].try_into().unwrap())?)
|
||||||
},
|
},
|
||||||
unknown => {
|
unknown => {
|
||||||
trace!("unrecognized message type {}", unknown);
|
trace!("ignoring message type {}", unknown);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -200,7 +310,7 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
|
||||||
|
|
||||||
records.push(messages);
|
records.push(messages);
|
||||||
|
|
||||||
break;
|
// break;
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Extracting meta records");
|
info!("Extracting meta records");
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::message2::Msg02RDAStatusData;
|
use crate::message2::Msg02RDAStatusData;
|
||||||
|
use crate::message31::Message31;
|
||||||
use crate::NexradParseError;
|
use crate::NexradParseError;
|
||||||
|
|
||||||
pub const MSG_RDA_PERFORMANCE_DATA: u8 = 3;
|
pub const MSG_RDA_PERFORMANCE_DATA: u8 = 3;
|
||||||
|
@ -34,5 +35,6 @@ pub trait FromBody<const BODY_SIZE: usize> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
Msg02(Msg02RDAStatusData)
|
Msg02(Msg02RDAStatusData),
|
||||||
|
Msg31(Message31)
|
||||||
}
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
pub const MSG31_HEADER_LENGTH: usize = 4 + 4 + 2 + 2 + 4 + 1 + 1 + 2 + 1 + 1 + 1 + 1 + 4 + 1 + 1 + 2;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Message31Header {
|
||||||
|
pub radar_identifier: String,
|
||||||
|
pub collection_time: u32,
|
||||||
|
pub date: u16,
|
||||||
|
pub azimuth_number: u16,
|
||||||
|
pub azimuth_angle: f32,
|
||||||
|
pub compression_indicator: u8,
|
||||||
|
pub spare: u8,
|
||||||
|
pub radial_length: u16,
|
||||||
|
pub azimuth_resolution_spacing: u8,
|
||||||
|
pub radial_status: u8,
|
||||||
|
pub elevation_number: u8,
|
||||||
|
pub cut_sector_number: u8,
|
||||||
|
pub elevation_angle: f32,
|
||||||
|
pub radial_spot_blanking_status: u8,
|
||||||
|
pub azimuth_indexing_mode: u8,
|
||||||
|
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;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VolumeData {
|
||||||
|
pub datablock_type: String,
|
||||||
|
pub data_name: String,
|
||||||
|
pub lrtup: u16,
|
||||||
|
pub version_major: u8,
|
||||||
|
pub version_minor: u8,
|
||||||
|
pub lat: f32,
|
||||||
|
pub long: f32,
|
||||||
|
pub site_height: u16,
|
||||||
|
pub feedhorn_height: u16,
|
||||||
|
pub calibration_constant: f32,
|
||||||
|
pub shvtx_power_hor: f32,
|
||||||
|
pub shvtx_power_vert: f32,
|
||||||
|
pub system_diff_ref: f32,
|
||||||
|
pub initial_system_diff_phase: f32,
|
||||||
|
pub vcp: u16,
|
||||||
|
pub processing_status: u16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Message31 {
|
||||||
|
pub header: Message31Header,
|
||||||
|
pub volume_info: Option<VolumeData>
|
||||||
|
}
|
Loading…
Reference in New Issue