commit pds parser

This commit is contained in:
sleptworld 2022-07-29 23:58:36 +08:00
parent 88de7f0d9b
commit a603af9869

View File

@ -1,10 +1,10 @@
use crate::parm_tables::{self, *}; use crate::parm_tables;
use chrono::prelude::*; use chrono::prelude::*;
use chrono::{Duration, TimeZone}; use nom::bytes::complete::{tag, take};
use nom::bytes::complete::tag; use nom::multi::count;
use nom::number::complete::{u24, u8}; use nom::number::complete::{i16, u16, u24, u8};
use nom::number::Endianness::Little; use nom::number::Endianness::Little;
use nom::sequence::{preceded, tuple}; use nom::sequence::{preceded, tuple, Tuple};
use nom::IResult; use nom::IResult;
use super::parm_tables::Parm; use super::parm_tables::Parm;
@ -30,24 +30,21 @@ fn isParser(input: &[u8]) -> IResult<&[u8], IS> {
} }
struct PDS { struct PDS {
length: usize,
center_identification: Center, center_identification: Center,
generating_process_id: u8, generating_process_id: u8,
grid_identification: GridID, grid_identification: u8,
pub gds: bool, gds_or_bms: (bool, bool),
pub bms: bool,
unit: Parm, unit: Parm,
level_type_and_value: String, level_type_and_value: String,
datetime: DateTime<Utc>, datetime: DateTime<Utc>,
forecast_time_unit: Duration, time_range: String,
time_range: usize, average_or_missing_number: i16,
average_number: u16, decimal_scale: i32,
missing_number: u8,
decimal_scale: u16,
sub_center: SubCenter, sub_center: SubCenter,
missing: u8,
} }
#[derive(Eq)] #[derive(Clone)]
enum Center { enum Center {
WMC(u8), WMC(u8),
RSMC(u8), RSMC(u8),
@ -58,7 +55,7 @@ enum Center {
Other(u8), Other(u8),
} }
#[derive(Eq)] #[derive(Clone)]
enum SubCenter { enum SubCenter {
NCEPReAnalysis, NCEPReAnalysis,
NCEPEnsemble, NCEPEnsemble,
@ -77,6 +74,7 @@ enum SubCenter {
NorthAmericaRegionalRA, NorthAmericaRegionalRA,
SpaceWeather, SpaceWeather,
ESRLGlobalSystem, ESRLGlobalSystem,
Other,
} }
enum GridID { enum GridID {
@ -84,14 +82,27 @@ enum GridID {
Mocator, Mocator,
} }
fn center_parser(input: &[u8]) -> IResult<&[u8], Center> { fn center_parser(input: &[u8]) -> IResult<&[u8], (Center, PreConfParaTable)> {
let (next, value) = u8(input)?; let (next, value) = u8(input)?;
match value { match value {
1..=9 => Ok((next, Center::WMC(value))), // WMC
10..=15 | 28 | 29 | 41..=44 | 51 | 69 | 70 => Ok((next, Center::RsmcAndRafc(value))), 1..=6 => Ok((next, (Center::WMC(value), PreConfParaTable::NONE))),
16..=19 | 21 | 24..=27 | 30..=35 | 38 | 39 | 53 | 54 | 65..=67 | 71 | 74 | 75 | 78..=81 => { 7 => Ok((next, (Center::WMC(value), PreConfParaTable::NMC))),
Ok((next, Center::RSMC(value))) 8..=9 => Ok((next, (Center::WMC(value), PreConfParaTable::NONE))),
// RSMC/RAFC
10..=15 | 28 | 29 | 41..=44 | 51 | 69 | 70 => {
Ok((next, (Center::RsmcAndRafc(value), PreConfParaTable::NONE)))
} }
// RSMC
16..=19 | 21 | 24..=27 | 30..=35 | 38 | 39 | 53 | 65..=67 | 71 | 74 | 75 | 79..=81 => {
Ok((next, (Center::RSMC(value), PreConfParaTable::NONE)))
}
54 => Ok((next, (Center::RSMC(value), PreConfParaTable::CMC))),
78 => Ok((next, (Center::RSMC(value), PreConfParaTable::DWD))),
// NMC
23 23
| 47..=50 | 47..=50
| 73 | 73
@ -104,15 +115,21 @@ fn center_parser(input: &[u8]) -> IResult<&[u8], Center> {
| 162..=165 | 162..=165
| 167..=171 | 167..=171
| 190..=198 | 190..=198
| 200..=203 | 201..=203
| 222..=232 | 222..=232
| 234..=238 | 234..=238
| 240 | 240
| 242..=246 => Ok((next, Center::NMC(value))), | 242..=246 => Ok((next, (Center::NMC(value), PreConfParaTable::NONE))),
200 => Ok((next, (Center::NMC(value), PreConfParaTable::LAMI))),
93 => Ok((next, Center::WAFC(value))), // WAFC
20 | 92 => Ok((next, Center::RAFC(value))), 93 => Ok((next, (Center::WAFC(value), PreConfParaTable::NONE))),
_ => Ok((next, Center::Other(value))),
// Other
146 => Ok((next, (Center::Other(value), PreConfParaTable::CHM))),
46 => Ok((next, (Center::Other(value), PreConfParaTable::CPTEC))),
20 | 92 => Ok((next, (Center::RAFC(value), PreConfParaTable::NONE))),
_ => Ok((next, (Center::Other(value), PreConfParaTable::NONE))),
} }
} }
@ -124,7 +141,7 @@ fn gds_or_bms_parser(input: &[u8]) -> IResult<&[u8], (bool, bool)> {
Ok((next, (gds, bms))) Ok((next, (gds, bms)))
} }
fn levels(layer_indicator: u8, center: u8, key_value: u32) -> String { fn levels(layer_indicator: u8, center: &PreConfParaTable, key_value: u16) -> String {
let o11 = key_value / 256; let o11 = key_value / 256;
let o12 = key_value % 256; let o12 = key_value % 256;
@ -169,7 +186,7 @@ fn levels(layer_indicator: u8, center: u8, key_value: u32) -> String {
121 => format!("{}-{} mb", 1100 - o11, 1100 - o12), 121 => format!("{}-{} mb", 1100 - o11, 1100 - o12),
125 => format!("{} cm above gnd", key_value), 125 => format!("{} cm above gnd", key_value),
126 => { 126 => {
if center == 7 { if let c = PreConfParaTable::NMC {
format!("{:.2} mb", key_value as f32 * 0.01) format!("{:.2} mb", key_value as f32 * 0.01)
} else { } else {
String::from("None") String::from("None")
@ -192,7 +209,7 @@ fn levels(layer_indicator: u8, center: u8, key_value: u32) -> String {
209 => String::from("boundary layer cloud bottom"), 209 => String::from("boundary layer cloud bottom"),
210 => { 210 => {
if center == 7 { if let c = PreConfParaTable::NMC {
String::from("boundary-layer cloud top") String::from("boundary-layer cloud top")
} else { } else {
format!("{:.2} mb", key_value as f32 * 0.01) format!("{:.2} mb", key_value as f32 * 0.01)
@ -246,34 +263,309 @@ fn levels(layer_indicator: u8, center: u8, key_value: u32) -> String {
}; };
} }
#[derive(Clone)]
enum PreConfParaTable {
NMC,
ECMWF,
DWD,
CMC,
CPTEC,
CHM,
LAMI,
NONE,
}
fn unit_parser( fn unit_parser(
input: &[u8], value: u8,
p_table: u8, p_table: u8,
center: Center, center: &PreConfParaTable,
sub_center: SubCenter, sub_center: &SubCenter,
para_version: u8,
process: u8, process: u8,
) -> IResult<&[u8], Parm> { ) -> Parm {
let mut para_table: &'static [Parm; 256];
match *center {
PreConfParaTable::NMC => {
if p_table <= 3 {
if let sub_center = SubCenter::NCEPReAnalysis {
para_table = &parm_tables::nceptable_reanal::NCEP_REANAL_PARM_TABLE;
}
if let sub_center = SubCenter::NWSMeteorologialDevLab {
para_table = &parm_tables::nceptable_mdl::NCEP_TABLE_MDL_PARM_TABLE;
}
if (process != 80 && process != 180) || (p_table != 1 && p_table != 2) {
para_table = &parm_tables::nceptable_opn::NCEP_OPN_PARM_TABLE;
}
} else {
match p_table {
128 => para_table = &parm_tables::nceptab_128::NCEP_128,
129 => para_table = &parm_tables::nceptab_129::NCEP_129,
130 => para_table = &parm_tables::nceptab_130::NCEP_130,
131 => para_table = &parm_tables::nceptab_131::NCEP_131,
133 => para_table = &parm_tables::nceptab_133::NCEP_133,
140 => para_table = &parm_tables::nceptab_140::NCEP_140,
141 => para_table = &parm_tables::nceptab_141::NCEP_141,
}
}
}
PreConfParaTable::ECMWF => {}
PreConfParaTable::DWD | PreConfParaTable::CHM => {}
PreConfParaTable::CPTEC => {}
_ => {}
}
para_table[value as usize]
}
fn time_unit(input: u8) -> String {
// MINUTE 0
// HOUR 1
// DAY 2
// MONTH 3
// YEAR 4
// DECADE 5
// NORMAL 6
// CENTURY 7
// HOURS3 10
// HOURS6 11
// HOURS12 12
// MINUTES15 13
// MINUTES30 14
// SECOND 254
match input {
0 => String::from("Minute"),
1 => String::from("Hour"),
2 => String::from("Day"),
3 => String::from("Month"),
4 => String::from("Year"),
5 => String::from("Decade"),
6 => String::from("Normal"),
7 => String::from("Century"),
10 => String::from("3 Hours"),
11 => String::from("6 Hours"),
12 => String::from("12 Hours"),
13 => String::from("15 Minutes"),
14 => String::from("30 Minutes"),
254 => String::from("Second"),
_ => String::from("Undefined"),
}
}
fn subcenter_parser(input: &[u8]) -> IResult<&[u8], SubCenter> {
let (next, value) = u8(input)?; let (next, value) = u8(input)?;
let para_table = match center { Ok((
Center::WMC(value) => { next,
// figure out if NCEP opn or reanalysis match value {
if value == 7 && p_table <= 3 { 1 => SubCenter::NCEPReAnalysis,
if sub_center == SubCenter::NCEPReAnalysis { 2 => SubCenter::NCEPEnsemble,
parm_tables::nceptable_reanal::NCEP_REANAL_PARM_TABLE 3 => SubCenter::NCEPCentral,
} else if sub_center == SubCenter::NWSMeteorologialDevLab { 4 => SubCenter::EnvModelingCenter,
parm_tables::nceptable_mdl::NCEP_TABLE_MDL_PARM_TABLE 5 => SubCenter::WeatherPredictionCenter,
} else if (process != 80 && process != 180) || (p_table != 1 && p_table != 2) { 6 => SubCenter::OceanPredictionCenter,
parm_tables::nceptable_opn::NCEP_OPN_PARM_TABLE 7 => SubCenter::ClimatePredictionCenter,
} 8 => SubCenter::AviationWeatherCenter,
} 9 => SubCenter::StormPredictionCenter,
10 => SubCenter::NationalHurricaneCenter,
11 => SubCenter::NWSTechDevLab,
12 => SubCenter::NESDIS,
13 => SubCenter::FederalAviationAdministration,
14 => SubCenter::NWSMeteorologialDevLab,
15 => SubCenter::NorthAmericaRegionalRA,
16 => SubCenter::SpaceWeather,
17 => SubCenter::ESRLGlobalSystem,
_ => SubCenter::Other,
}, },
))
}
fn init_time(input: &[u8], century: u8) -> DateTime<Utc> {
let year = 100 * (century as i32 - 1) + input[0] as i32;
let month = input[1] as u32;
let day = input[2] as u32;
let hour = input[3] as u32;
let minute = input[4] as u32;
Utc.ymd(year, month, day).and_hms(hour, minute, 0)
}
fn time_range_parser(input: &[u8]) -> IResult<&[u8], String> {
let (next, value) = count(u8, 4)(input)?;
let unit = time_unit(value[0]);
let time_range = value[1];
let p1 = value[2];
let p2 = value[3];
Ok((
next,
match time_range {
0 | 1 | 10 => format!(""),
2 => format!("valid {}-{}{}:", p1, p2, unit),
3 => format!("{}-{}{} ave:", p1, p2, unit),
4 => format!("{}-{}{} acc:", p1, p2, unit),
5 => format!("{}-{}{} diff:", p1, p2, unit),
6 => format!("-{} to -{} {} ave:", p1, p2, unit),
7 => format!("-{} to {} {} ave:", p1, p2, unit),
11 => {
if p1 > 0 {
format!("init fcst {}{}:", p1, unit)
} else {
format!("time?:")
} }
} }
fn pds_parser(input: &[u8]) -> IResult<&[u8], IS> { 13 => format!("nudge ana {}{}:", p1, unit),
14 => format!("rel. fcst {}{}:", p1, unit),
51 => {
if p1 == 0 {
/* format!("clim {}{}:",p2,unit), */
format!("0-{}{} product:ave@1yr:", p2, unit)
} else if p1 == 1 {
/* format!("clim (diurnal) {}{}:",p2,unit), */
format!("0-{}{} product:same-hour,ave@1yr:", p2, unit)
} else {
format!("clim? p1={}? {}{}?:", p1, p2, unit)
}
}
113 | 123 => format!("ave@{}{}:", p2, unit),
114 | 124 => format!("acc@{}{}:", p2, unit),
115 => format!("ave of fcst:{} to {}{}:", p1, p2, unit),
116 => format!("acc of fcst:{} to {}{}:", p1, p2, unit),
118 => format!("var@{}{}:", p2, unit),
128 => format!("{}-{}{} fcst acc:ave@24hr:", p1, p2, unit),
129 => format!("{}-{}{} fcst acc:ave@{}{}:", p1, p2, unit, p2 - p1, unit),
130 => format!("{}-{}{} fcst ave:ave@24hr:", p1, p2, unit),
131 => format!("{}-{}{} fcst ave:ave@{}{}:", p1, p2, unit, p2 - p1, unit),
/* for CFS */
132 => format!("{}-{}{} anl:ave@1yr:", p1, p2, unit),
133 => format!("{}-{}{} fcst:ave@1yr:", p1, p2, unit),
134 => format!("{}-{}{} fcst-anl:rms@1yr:", p1, p2, unit),
135 => format!("{}-{}{} fcst-fcst_mean:rms@1yr:", p1, p2, unit),
136 => format!("{}-{}{} anl-anl_mean:rms@1yr:", p1, p2, unit),
137 => format!("{}-{}{} fcst acc:ave@6hr:", p1, p2, unit),
138 => format!("{}-{}{} fcst ave:ave@6hr:", p1, p2, unit),
139 => format!("{}-{}{} fcst acc:ave@12hr:", p1, p2, unit),
140 => format!("{}-{}{} fcst ave:ave@12hr:", p1, p2, unit),
_ => format!("time?:"),
},
))
}
fn decimal_scale_parser(input: &[u8]) -> IResult<&[u8], i32> {
let (next, value) = take(2usize)(input)?;
let result = 1 - ((value[0] & 0x80) >> 6) as i32 * (((value[0] & 0x7f) << 8) + value[1]) as i32;
Ok((next, result))
}
fn pds_parser(input: &[u8]) -> IResult<&[u8], PDS> {
let date_time_parser = take(5usize);
let length = u24(Little); let length = u24(Little);
let (
next,
(
ll,
p_table,
center_and_preconf,
process,
grid_id,
flag,
raw_unit,
level_type,
raw_level,
date_time,
time_range,
average_or_acc,
missing,
init_century,
subcenter,
decimal_factor,
),
) = tuple((
length,
u8,
center_parser,
u8,
u8,
gds_or_bms_parser,
u8,
u8,
u16(Little),
date_time_parser,
time_range_parser,
i16(Little),
u8,
u8,
subcenter_parser,
decimal_scale_parser,
))(input)?;
let unit = unit_parser(
raw_unit,
p_table,
&center_and_preconf.1,
&subcenter,
process,
);
let levels = levels(level_type, &center_and_preconf.1, raw_level);
let init_time = init_time(date_time, init_century);
Ok((
next,
PDS {
center_identification: center_and_preconf.0,
generating_process_id: process,
grid_identification: grid_id,
gds_or_bms: flag,
unit,
time_range,
level_type_and_value: levels,
datetime: init_time,
average_or_missing_number: average_or_acc,
decimal_scale: decimal_factor,
sub_center: subcenter,
missing,
},
))
} }
struct GDS { struct GDS {