use abi_stable::std_types::vec; use bytemuck::cast_slice; use nom::{ bytes::complete::take, combinator::map, number::complete::{le_f32, le_i16, le_i64, le_u16, le_u32}, sequence::tuple, IResult, }; use zip::read::ZipArchive; use std::{ collections::HashMap, io::{self, Cursor, Read, Seek, SeekFrom}, process::id, }; use std::{fs::File, path::Path}; const SUPPORTED_TYPE: [u32; 7] = [2, 3, 4, 7, 9, 10, 11]; #[derive(Debug)] pub struct RadarData { pub datetime: i64, pub els: Vec, pub azs: Vec, pub ranges: Vec, pub datas: HashMap<&'static str, Vec>, } #[derive(Debug)] struct SiteConfig { site_code: String, site_name: String, latitude: f32, longitude: f32, antenna_height: u32, } #[derive(Debug)] struct TaskConfig { task_name: String, task_description: String, polarization_type: u32, scan_type: u32, scan_beam_number: u32, cut_number: u32, ray_order: u32, scan_start_time: i64, } #[derive(Debug)] struct BeamConfig { cut_index: i16, tx_beam_index: i16, rx_beam_elevation: f32, tx_beam_gain: f32, rx_beam_width_h: f32, rx_beam_width_v: f32, rx_beam_gain: f32, process_mode: String, wave_form: String, n1_prf1: f32, n1_prf2: f32, n2_prf1: f32, n2_prf2: f32, dealiasing_mode: u32, azimuth: f32, start_angle: f32, end_angle: f32, angular_resolution: f32, scan_speed: f32, log_resolution: f32, doppler_resolution: f32, maximum_range1: u32, maximum_range2: u32, start_range: u32, sample1: u32, sample2: u32, phase_mode: u32, atmospheric_loss: f32, nyquist_speed: f32, moments_mask: i64, moments_size_mask: i64, misc_filter_mask: u32, sqi_threshold: f32, sig_threshold: f32, csr_threshold: f32, log_threshold: f32, cpa_threshold: f32, pmi_threshold: f32, dplog_threshold: f32, thresholds_r: [u8; 4], dbt_mask: u32, dbz_mask: u32, velocity_mask: u32, spectrum_width_mask: u32, dp_mask: u32, direction: u32, ground_clutter_classifier_type: i16, ground_clutter_filter_type: i16, ground_clutter_filter_notch_width: i16, ground_clutter_filter_window: i16, } #[derive(Debug)] struct TypeHeader { data_type: u32, scale: u32, offset: u32, bin_length: u16, flags: u16, length: u32, } #[derive(Debug)] struct RadialData { radial_state: u32, spot_blank: u32, sequence_number: u32, radial_number: u32, elevation_number: u32, azimuth: f32, elevation: f32, seconds: i64, microseconds: u32, length_of_data: u32, moment_number: u32, scan_beam_index: i16, horizontal_estimated_noise: i16, vertical_estimated_noise: i16, prf_flag: u32, } #[derive(Debug)] struct GenericHeader { magic_number: u32, major_version: u16, minor_version: u16, generic_type: u32, product_type: u32, reserved: [u8; 16], } #[derive(Debug)] struct SubPulseConfig { strategy: u32, modulation: u32, frequency: f32, bandwidth: f32, width: u32, horizontal_noise: f32, vertical_noise: f32, horizontal_calibration: f32, vertical_calibration: f32, horizontal_noise_temperature: f32, vertical_noise_temperature: f32, zdr_calibration: f32, phidp_calibration: f32, ldr_calibration: f32, pulse_points: i16, } #[derive(Debug)] struct ScanConfig { beam_index: u32, beam_type: u32, sub_pulse_number: u32, tx_beam_direction: f32, tx_beam_width_h: f32, tx_beam_width_v: f32, tx_beam_gain: f32, sub_pulse_configs: Vec, } fn parse_scan_config(input: &[u8]) -> IResult<&[u8], ScanConfig> { let (input, beam_index) = le_u32(input)?; let (input, beam_type) = le_u32(input)?; let (input, sub_pulse_number) = le_u32(input)?; let (input, tx_beam_direction) = le_f32(input)?; let (input, tx_beam_width_h) = le_f32(input)?; let (input, tx_beam_width_v) = le_f32(input)?; let (input, tx_beam_gain) = le_f32(input)?; let (input, _) = take(100usize)(input)?; // Skip reserved bytes // Parse the SubPulseConfigs let mut sub_pulse_configs = Vec::new(); let mut input = input; for _ in 0..4 { let (new_input, sub_pulse_config) = parse_sub_pulse_config(input)?; input = new_input; sub_pulse_configs.push(sub_pulse_config); } let scan_config = ScanConfig { beam_index, beam_type, sub_pulse_number, tx_beam_direction, tx_beam_width_h, tx_beam_width_v, tx_beam_gain, sub_pulse_configs, }; Ok((input, scan_config)) } fn parse_sub_pulse_config(input: &[u8]) -> IResult<&[u8], SubPulseConfig> { let (input, strategy) = le_u32(input)?; let (input, modulation) = le_u32(input)?; let (input, frequency) = le_f32(input)?; let (input, bandwidth) = le_f32(input)?; let (input, width) = le_u32(input)?; let (input, horizontal_noise) = le_f32(input)?; let (input, vertical_noise) = le_f32(input)?; let (input, horizontal_calibration) = le_f32(input)?; let (input, vertical_calibration) = le_f32(input)?; let (input, horizontal_noise_temperature) = le_f32(input)?; let (input, vertical_noise_temperature) = le_f32(input)?; let (input, zdr_calibration) = le_f32(input)?; let (input, phidp_calibration) = le_f32(input)?; let (input, ldr_calibration) = le_f32(input)?; let (input, pulse_points) = nom::number::complete::le_i16(input)?; let (input, _) = take(70usize)(input)?; // Skip reserved bytes let sub_pulse_config = SubPulseConfig { strategy, modulation, frequency, bandwidth, width, horizontal_noise, vertical_noise, horizontal_calibration, vertical_calibration, horizontal_noise_temperature, vertical_noise_temperature, zdr_calibration, phidp_calibration, ldr_calibration, pulse_points, }; Ok((input, sub_pulse_config)) } pub fn parse_raw_data>(path: P) -> io::Result { let path = path.as_ref(); if path.extension().unwrap() == "zip" { let file = File::open(path).unwrap(); let mut archive = ZipArchive::new(file).unwrap(); let mut file = archive.by_index(0).unwrap(); let mut buf = Vec::new(); file.read_to_end(&mut buf).unwrap(); if &buf[0..4] == b"SSTM" || &buf[16..20] == b"STDM" || &buf[0..4] == b"RSTM" { return get_radar_data(buf); } else { panic!("Invalid file format"); } } else { let mut file = File::open(path).unwrap(); let mut buf = Vec::new(); file.read_to_end(&mut buf).unwrap(); if &buf[0..4] == b"SSTM" || &buf[16..20] == b"STDM" || &buf[0..4] == b"RSTM" { return get_radar_data(buf); } else { panic!("Invalid file format"); } } } fn get_radar_data(data: Vec) -> io::Result { let total_length = data.len() as u64; // 使用 Cursor 来处理 Vec let mut cursor = Cursor::new(data); // 读取 GENERIC_HEADER let mut buffer = vec![0u8; 32]; cursor.read_exact(&mut buffer)?; let (_, generic_header) = parse_generic_header(&buffer).unwrap(); // 读取 SITE_CONFIG let mut buffer = vec![0u8; 128]; cursor.read_exact(&mut buffer)?; let (_, site_config) = parse_site_config(&buffer).unwrap(); // 读取 TASK_CONFIG let mut buffer = vec![0u8; 256]; cursor.read_exact(&mut buffer)?; let (_, task_config) = parse_task_config(&buffer).unwrap(); // 读取 SCAN_CONFIG let mut scan_configs = Vec::new(); for _ in 0..task_config.scan_beam_number { let mut buffer = vec![0u8; 640]; cursor.read_exact(&mut buffer)?; let (_, scan_config) = parse_scan_config(&buffer).unwrap(); scan_configs.push(scan_config); } // 读取 BEAM_CONFIG let mut beam_configs = Vec::new(); for _ in 0..task_config.cut_number { let mut buffer = vec![0u8; 256]; cursor.read_exact(&mut buffer)?; let (_, beam_config) = parse_beam_config(&buffer).unwrap(); beam_configs.push(beam_config); } // 读取 RADIAL_DATA let (datas, els, azs, ranges) = parse_final_data(total_length, task_config.cut_number as usize, cursor)?; // 构建并返回 RadarData 结构体 let radar_data = RadarData { datetime: task_config.scan_start_time, els, azs, ranges, datas, }; Ok(radar_data) } fn parse_generic_header(input: &[u8]) -> IResult<&[u8], GenericHeader> { let (input, magic_number) = le_u32(input)?; let (input, major_version) = le_u16(input)?; let (input, minor_version) = le_u16(input)?; let (input, generic_type) = le_u32(input)?; let (input, product_type) = le_u32(input)?; let (input, reserved) = take(16usize)(input)?; let generic_header = GenericHeader { magic_number, major_version, minor_version, generic_type, product_type, reserved: reserved.try_into().unwrap(), }; Ok((input, generic_header)) } fn parse_site_config(input: &[u8]) -> IResult<&[u8], SiteConfig> { let (input, site_code) = take(8usize)(input)?; let (input, site_name) = take(32usize)(input)?; let (input, latitude) = le_f32(input)?; let (input, longitude) = le_f32(input)?; let (input, antenna_height) = le_u32(input)?; let (input, _) = take(4usize)(input)?; // Skip reserved bytes let site_config = SiteConfig { site_code: String::from_utf8(site_code.to_vec()) .unwrap() .trim_end_matches('\0') .to_string(), site_name: String::from_utf8(site_name.to_vec()) .unwrap() .trim_end_matches('\0') .to_string(), latitude, longitude, antenna_height, }; Ok((input, site_config)) } fn parse_task_config(input: &[u8]) -> IResult<&[u8], TaskConfig> { let (input, task_name) = take(32usize)(input)?; let (input, task_description) = take(128usize)(input)?; let (input, polarization_type) = le_u32(input)?; let (input, scan_type) = le_u32(input)?; let (input, scan_beam_number) = le_u32(input)?; let (input, cut_number) = le_u32(input)?; let (input, ray_order) = le_u32(input)?; let (input, scan_start_time) = le_i64(input)?; let (input, _) = take(68usize)(input)?; // Skip reserved bytes let task_config = TaskConfig { task_name: String::from_utf8_lossy(task_name).to_string(), task_description: String::from_utf8_lossy(task_description).to_string(), polarization_type, scan_type, scan_beam_number, cut_number, ray_order, scan_start_time, }; Ok((input, task_config)) } fn single_beam_block(raw: &[u8], bin_length: usize) -> io::Result> { let raw = match bin_length { 1 => { let new_raw: Vec = raw.into_iter().map(|v| *v as f64).collect(); new_raw } 2 => { let new_raw: Vec = cast_slice(&raw) .into_iter() .map(|v: &u16| *v as f64) .collect(); new_raw } 4 => { let new_raw: Vec = cast_slice(&raw) .into_iter() .map(|v: &u32| *v as f64) .collect(); new_raw } 8 => { let new_raw: Vec = cast_slice(&raw) .into_iter() .map(|v: &u64| *v as f64) .collect(); new_raw } _ => { let new_raw: Vec = raw.into_iter().map(|v| *v as f64).collect(); new_raw } }; Ok(raw) } fn beam_block_exact(cursor: &mut Cursor>, header: &TypeHeader) -> io::Result> { let mut buf = vec![0u8; header.length as usize]; cursor.read_exact(&mut buf)?; Ok(buf) } fn data_type(typ: u32) -> &'static str { match typ { 2 => "DBZ", 3 => "VEL", 4 => "SW", 7 => "ZDR", 9 => "CC", 10 => "PHIDP", 11 => "KDP", _ => "Unknown", } } fn parse_final_data( all_length: u64, cut_number: usize, mut cursor: Cursor>, ) -> io::Result<( HashMap<&'static str, Vec>, Vec, Vec, Vec, )> { let position = cursor.position(); let mut radial_buffer = vec![0u8; 128]; let mut header_buffer = vec![0u8; 32]; cursor.read_exact(&mut radial_buffer)?; // First Block let (_, radial_data) = parse_radial_data(&radial_buffer).unwrap(); // Radial_number: I don't know why not use it. // let radial_number = radial_data.radial_number as usize; // 变量类型 let type_number = radial_data.moment_number as usize; let mut gate_len = vec![0; type_number]; let mut types = vec!["Unknown"; type_number]; // final datas let mut datas = HashMap::new(); for idx in 0..type_number { cursor.read_exact(&mut header_buffer)?; let (_, header) = parse_type_header(&header_buffer).unwrap(); let first_beam_block = beam_block_exact(&mut cursor, &header)?; let first_beam = single_beam_block(&first_beam_block, header.bin_length as usize)?; gate_len[idx] = first_beam.len(); let data_type = data_type(header.data_type); if data_type == "Unknown" { continue; } types[idx] = data_type; } let first_block_position = cursor.position(); let roll_num = (all_length - position) / (first_block_position - position); let azm_number = (roll_num / cut_number as u64) as usize; for idx in 0..type_number { if types[idx] == "Unknown" { continue; } datas.insert( types[idx], vec![0f64; cut_number * azm_number * gate_len[idx]], ); } // Reset Cursor cursor.set_position(position); let mut els = vec![0f64; cut_number]; let mut azs = vec![0f64; azm_number]; for e in 0..cut_number { for a in 0..azm_number { cursor.read_exact(&mut radial_buffer)?; let (_, radial_data) = parse_radial_data(&radial_buffer).unwrap(); els[e] = radial_data.elevation as f64; azs[a] = radial_data.azimuth as f64; let type_number = radial_data.moment_number as usize; for typ in 0..type_number { cursor.read_exact(&mut header_buffer)?; let (_, header) = parse_type_header(&header_buffer).unwrap(); let beam_block = beam_block_exact(&mut cursor, &header)?; let mut beam = single_beam_block(&beam_block, header.bin_length as usize)?; beam.iter_mut() .for_each(|v| *v = (*v - header.offset as f64) / header.scale as f64); let typ_name = types.get(typ).unwrap(); if typ_name == &"Unknown" { continue; } let e = radial_data.elevation_number as usize - 1; datas.get_mut(typ_name).unwrap()[e * (azm_number * gate_len[typ]) + a * gate_len[typ] ..e * (azm_number * gate_len[typ]) + (a + 1) * gate_len[typ]] .copy_from_slice(&beam); } } } let mut ranges = vec![0f64; gate_len[0]]; ranges .iter_mut() .enumerate() .for_each(|(idx, r)| *r = idx as f64 * 30.0); Ok((datas, els, azs, ranges)) } fn parse_beam_config(input: &[u8]) -> IResult<&[u8], BeamConfig> { let (input, cut_index) = le_i16(input)?; let (input, tx_beam_index) = le_i16(input)?; let (input, rx_beam_elevation) = le_f32(input)?; let (input, tx_beam_gain) = le_f32(input)?; let (input, rx_beam_width_h) = le_f32(input)?; let (input, rx_beam_width_v) = le_f32(input)?; let (input, rx_beam_gain) = le_f32(input)?; let (input, process_mode) = map(le_u32, |v| match v { 1 => "PPP".to_string(), 2 => "FFT".to_string(), _ => "Unknown".to_string(), })(input)?; let (input, wave_form) = map(le_u32, |v| match v { 0 => "CS连续监测".to_string(), 1 => "CD连续多普勒".to_string(), 2 => "CDX多普勒扩展".to_string(), 3 => "Rx Test".to_string(), 4 => "BATCH批模式".to_string(), 5 => "Dual PRF双PRF".to_string(), 6 => "Staggered PRT 参差PRT".to_string(), _ => "Unknown".to_string(), })(input)?; let (input, n1_prf1) = le_f32(input)?; let (input, n1_prf2) = le_f32(input)?; let (input, n2_prf1) = le_f32(input)?; let (input, n2_prf2) = le_f32(input)?; let (input, dealiasing_mode) = le_u32(input)?; let (input, azimuth) = le_f32(input)?; let (input, start_angle) = le_f32(input)?; let (input, end_angle) = le_f32(input)?; let (input, angular_resolution) = le_f32(input)?; let (input, scan_speed) = le_f32(input)?; let (input, log_resolution) = le_f32(input)?; let (input, doppler_resolution) = le_f32(input)?; let (input, maximum_range1) = le_u32(input)?; let (input, maximum_range2) = le_u32(input)?; let (input, start_range) = le_u32(input)?; let (input, sample1) = le_u32(input)?; let (input, sample2) = le_u32(input)?; let (input, phase_mode) = le_u32(input)?; let (input, atmospheric_loss) = le_f32(input)?; let (input, nyquist_speed) = le_f32(input)?; let (input, moments_mask) = le_i64(input)?; let (input, moments_size_mask) = le_i64(input)?; let (input, misc_filter_mask) = le_u32(input)?; let (input, sqi_threshold) = le_f32(input)?; let (input, sig_threshold) = le_f32(input)?; let (input, csr_threshold) = le_f32(input)?; let (input, log_threshold) = le_f32(input)?; let (input, cpa_threshold) = le_f32(input)?; let (input, pmi_threshold) = le_f32(input)?; let (input, dplog_threshold) = le_f32(input)?; let (input, thresholds_r) = take(4usize)(input)?; let (input, dbt_mask) = le_u32(input)?; let (input, dbz_mask) = le_u32(input)?; let (input, velocity_mask) = le_u32(input)?; let (input, spectrum_width_mask) = le_u32(input)?; let (input, dp_mask) = le_u32(input)?; let (input, direction) = le_u32(input)?; let (input, ground_clutter_classifier_type) = le_i16(input)?; let (input, ground_clutter_filter_type) = le_i16(input)?; let (input, ground_clutter_filter_notch_width) = le_i16(input)?; let (input, ground_clutter_filter_window) = le_i16(input)?; let (input, _) = take(44usize)(input)?; let beam_config = BeamConfig { cut_index, tx_beam_index, rx_beam_elevation, tx_beam_gain, rx_beam_width_h, rx_beam_width_v, rx_beam_gain, process_mode, wave_form, n1_prf1, n1_prf2, n2_prf1, n2_prf2, dealiasing_mode, azimuth, start_angle, end_angle, angular_resolution, scan_speed, log_resolution, doppler_resolution, maximum_range1, maximum_range2, start_range, sample1, sample2, phase_mode, atmospheric_loss, nyquist_speed, moments_mask, moments_size_mask, misc_filter_mask, sqi_threshold, sig_threshold, csr_threshold, log_threshold, cpa_threshold, pmi_threshold, dplog_threshold, thresholds_r: thresholds_r.try_into().unwrap(), dbt_mask, dbz_mask, velocity_mask, spectrum_width_mask, dp_mask, direction, ground_clutter_classifier_type, ground_clutter_filter_type, ground_clutter_filter_notch_width, ground_clutter_filter_window, }; Ok((input, beam_config)) } fn parse_radial_data(input: &[u8]) -> IResult<&[u8], RadialData> { let (input, radial_state) = le_u32(input)?; let (input, spot_blank) = le_u32(input)?; let (input, sequence_number) = le_u32(input)?; let (input, radial_number) = le_u32(input)?; let (input, elevation_number) = le_u32(input)?; let (input, azimuth) = le_f32(input)?; let (input, elevation) = le_f32(input)?; let (input, seconds) = le_i64(input)?; let (input, microseconds) = le_u32(input)?; let (input, length_of_data) = le_u32(input)?; let (input, moment_number) = le_u32(input)?; let (input, scan_beam_index) = le_i16(input)?; let (input, horizontal_estimated_noise) = le_i16(input)?; let (input, vertical_estimated_noise) = le_i16(input)?; let (input, prf_flag) = le_u32(input)?; let (input, _) = take(70usize)(input)?; // Skip reserved bytes let radial_data = RadialData { radial_state, spot_blank, sequence_number, radial_number, elevation_number, azimuth, elevation, seconds, microseconds, length_of_data, moment_number, scan_beam_index, horizontal_estimated_noise, vertical_estimated_noise, prf_flag, }; Ok((input, radial_data)) } fn parse_type_header(input: &[u8]) -> IResult<&[u8], TypeHeader> { let (input, data_type) = le_u32(input)?; let (input, scale) = le_u32(input)?; let (input, offset) = le_u32(input)?; let (input, bin_length) = le_u16(input)?; let (input, flags) = le_u16(input)?; let (input, length) = le_u32(input)?; let (input, _) = take(12usize)(input)?; // Skip reserved bytes let type_header = TypeHeader { data_type, scale, offset, bin_length, flags, length, }; Ok((input, type_header)) } mod test { use crate::raw::parse_raw_data; use super::get_radar_data; #[test] fn test() { use std::fs::File; let radar_data = parse_raw_data( "/Users/tsuki/Desktop/Z_RADR_I_X5775_20230726180000_O_DOR-XPD-CAP-FMT.BIN.zip", ) .unwrap(); } }