use crate::{
error::ParsingError,
grid::GridSpecs,
prelude::{Comments, Epoch, Header, Key, Quantized, QuantizedCoordinates, Record, TEC},
};
use std::{
io::{BufRead, BufReader, Read},
str::FromStr,
};
fn is_new_tec_map(line: &str) -> bool {
line.contains("START OF TEC MAP")
}
fn is_new_rms_map(line: &str) -> bool {
line.contains("START OF RMS MAP")
}
fn is_new_height_map(line: &str) -> bool {
line.contains("START OF HEIGHT MAP")
}
fn parse_tec_map(
content: &str,
lat_exponent: i8,
long_exponent: i8,
alt_exponent: i8,
tec_exponent: i8,
epoch: Epoch,
record: &mut Record,
) -> Result<(), ParsingError> {
const NON_AVAILABLE_TEC_KEYWORD: &str = "9999";
let lines = content.lines();
let mut long_ptr = 0.0_f64;
let mut grid_specs = GridSpecs::default();
for line in lines {
if line.len() > 60 {
let (content, marker) = line.split_at(60);
if marker.contains("LAT/LON1/LON2/DLON/H") {
grid_specs = GridSpecs::from_str(content)?;
long_ptr = grid_specs.longitude_space.start; continue; } else if marker.contains("END OF TEC MAP") {
return Ok(());
}
}
for item in line.split_ascii_whitespace() {
let item = item.trim();
if item != NON_AVAILABLE_TEC_KEYWORD {
if let Ok(tecu) = item.parse::<i64>() {
let tec = TEC::from_quantized(tecu, tec_exponent);
let quantized_lat = Quantized::new(grid_specs.latitude_ddeg, lat_exponent);
let quantized_long = Quantized::new(long_ptr, long_exponent);
let quantized_alt = Quantized::new(grid_specs.altitude_km, alt_exponent);
let coordinates = QuantizedCoordinates::from_quantized(
quantized_lat,
quantized_long,
quantized_alt,
);
let key = Key { epoch, coordinates };
record.insert(key, tec);
}
}
long_ptr += grid_specs.longitude_space.spacing;
}
}
Ok(())
}
fn parse_rms_map(
content: &str,
lat_exponent: i8,
long_exponent: i8,
alt_exponent: i8,
tec_exponent: i8,
epoch: Epoch,
record: &mut Record,
) -> Result<(), ParsingError> {
const NON_AVAILABLE_TEC_KEYWORD: &str = "9999";
let lines = content.lines();
let mut long_ptr = 0.0_f64;
let mut grid_specs = GridSpecs::default();
for line in lines {
if line.len() > 60 {
let marker = line.split_at(60).1;
if marker.contains("END OF RMS MAP") {
return Ok(());
} else if marker.contains("EXPONENT") {
continue; } else if marker.contains("START OF") {
continue; } else if marker.contains("LAT/LON1/LON2/DLON/H") {
grid_specs = GridSpecs::from_str(content)?;
long_ptr = grid_specs.longitude_space.start; continue; } else if marker.contains("END OF RMS MAP") {
return Ok(());
}
}
for item in line.split_ascii_whitespace() {
let item = item.trim();
if item != NON_AVAILABLE_TEC_KEYWORD {
if let Ok(rms_tecu) = item.parse::<i64>() {
let quantized_lat = Quantized::new(grid_specs.latitude_ddeg, lat_exponent);
let quantized_long = Quantized::new(long_ptr, long_exponent);
let quantized_alt = Quantized::new(grid_specs.altitude_km, alt_exponent);
let coordinates = QuantizedCoordinates::from_quantized(
quantized_lat,
quantized_long,
quantized_alt,
);
let key = Key { epoch, coordinates };
if let Some(v) = record.get_mut(&key) {
v.set_quantized_root_mean_square(rms_tecu, tec_exponent);
}
}
}
long_ptr += grid_specs.longitude_space.spacing;
}
}
Ok(())
}
pub(crate) fn parse_record<R: Read>(
header: &mut Header,
reader: &mut BufReader<R>,
) -> Result<(Record, Comments), ParsingError> {
Ok((Default::default(), Default::default()))
}
#[cfg(test)]
mod test {
use super::{is_new_height_map, is_new_rms_map, is_new_tec_map, parse_tec_map, Quantized};
use crate::prelude::{Epoch, Key, QuantizedCoordinates, Record};
#[test]
fn ionex_keywords() {
assert!(is_new_tec_map(
"1 START OF TEC MAP"
));
assert!(is_new_rms_map(
"1 START OF RMS MAP"
));
assert!(is_new_height_map(
"1 START OF HEIGHT MAP"
));
}
#[test]
fn tec_map_parsing() {
let mut record = Record::default();
let tec_exponent = -1;
let lat_exponent = Quantized::find_exponent(2.5);
let long_exponent = Quantized::find_exponent(5.0);
let alt_exponent = Quantized::find_exponent(0.0);
let epoch = Epoch::from_gregorian_utc_at_midnight(2017, 1, 1);
let content =
" 1 START OF TEC MAP
2017 1 1 0 0 0 EPOCH OF CURRENT MAP
87.5-180.0 180.0 5.0 450.0 LAT/LON1/LON2/DLON/H
33 33 32 32 32 31 31 30 30 30 29 29 28 28 28 27
27 27 26 26 26 26 26 26 26 26 26 26 26 26 26 26
27 27 27 28 28 29 29 30 30 31 31 32 32 33 33 33
34 34 35 35 35 35 36 36 36 36 36 36 36 36 36 35
35 35 35 35 34 34 34 33 33
85.0-180.0 180.0 5.0 450.0 LAT/LON1/LON2/DLON/H
36 36 35 35 34 34 33 33 32 31 31 30 29 28 28 27
26 25 25 24 24 23 23 22 22 22 22 22 22 23 23 24
24 25 25 26 27 28 29 29 30 31 32 33 34 35 36 37
38 39 39 40 41 41 41 41 42 42 42 41 41 41 41 40
40 40 39 39 38 38 37 37 36
27.5-180.0 180.0 5.0 450.0 LAT/LON1/LON2/DLON/H
235 230 222 212 200 187 173 157 141 126 110 95 92 92 92 92
92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92
92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92
92 92 92 92 92 92 92 92 92 104 120 136 151 166 180 193
205 215 224 231 236 239 240 239 235
2.5-180.0 180.0 5.0 450.0 LAT/LON1/LON2/DLON/H
364 370 374 378 380 380 378 375 370 364 356 346 336 324 311 298
283 269 253 238 222 207 191 175 159 143 127 111 96 92 92 92
92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92
92 92 92 92 92 106 124 141 158 175 191 207 223 238 252 266
280 293 305 317 328 339 348 356 364
-2.5-180.0 180.0 5.0 450.0 LAT/LON1/LON2/DLON/H
363 370 375 380 383 385 385 384 381 376 370 363 354 343 332 319
305 291 276 260 244 227 210 194 176 159 143 126 109 93 92 92
92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92
92 92 92 92 103 120 136 152 168 183 198 212 226 240 253 266
279 291 303 315 326 336 346 355 363
1 END OF TEC MAP ";
parse_tec_map(
content,
lat_exponent,
long_exponent,
alt_exponent,
tec_exponent,
epoch,
&mut record,
)
.unwrap();
for (coordinates, quantized_tecu) in [
(
QuantizedCoordinates::new(
87.5,
lat_exponent,
-180.0,
long_exponent,
450.0,
alt_exponent,
),
33,
),
(
QuantizedCoordinates::new(
87.5,
lat_exponent,
-175.0,
long_exponent,
450.0,
alt_exponent,
),
33,
),
(
QuantizedCoordinates::new(
87.5,
lat_exponent,
-170.0,
long_exponent,
450.0,
alt_exponent,
),
32,
),
(
QuantizedCoordinates::new(
87.5,
lat_exponent,
170.0,
long_exponent,
450.0,
alt_exponent,
),
34,
),
(
QuantizedCoordinates::new(
87.5,
lat_exponent,
175.0,
long_exponent,
450.0,
alt_exponent,
),
33,
),
(
QuantizedCoordinates::new(
87.5,
lat_exponent,
180.0,
long_exponent,
450.0,
alt_exponent,
),
33,
),
(
QuantizedCoordinates::new(
85.0,
lat_exponent,
-180.0,
long_exponent,
450.0,
alt_exponent,
),
36,
),
(
QuantizedCoordinates::new(
85.0,
lat_exponent,
-175.0,
long_exponent,
450.0,
alt_exponent,
),
36,
),
(
QuantizedCoordinates::new(
85.0,
lat_exponent,
-170.0,
long_exponent,
450.0,
alt_exponent,
),
35,
),
(
QuantizedCoordinates::new(
85.0,
lat_exponent,
170.0,
long_exponent,
450.0,
alt_exponent,
),
37,
),
(
QuantizedCoordinates::new(
85.0,
lat_exponent,
175.0,
long_exponent,
450.0,
alt_exponent,
),
37,
),
(
QuantizedCoordinates::new(
85.0,
lat_exponent,
180.0,
long_exponent,
450.0,
alt_exponent,
),
36,
),
(
QuantizedCoordinates::new(
27.5,
lat_exponent,
170.0,
long_exponent,
450.0,
alt_exponent,
),
240,
),
(
QuantizedCoordinates::new(
27.5,
lat_exponent,
175.0,
long_exponent,
450.0,
alt_exponent,
),
239,
),
(
QuantizedCoordinates::new(
27.5,
lat_exponent,
180.0,
long_exponent,
450.0,
alt_exponent,
),
235,
),
(
QuantizedCoordinates::new(
2.5,
lat_exponent,
-170.0,
long_exponent,
450.0,
alt_exponent,
),
374,
),
(
QuantizedCoordinates::new(
2.5,
lat_exponent,
170.0,
long_exponent,
450.0,
alt_exponent,
),
348,
),
(
QuantizedCoordinates::new(
2.5,
lat_exponent,
175.0,
long_exponent,
450.0,
alt_exponent,
),
356,
),
(
QuantizedCoordinates::new(
2.5,
lat_exponent,
180.0,
long_exponent,
450.0,
alt_exponent,
),
364,
),
(
QuantizedCoordinates::new(
-2.5,
lat_exponent,
-170.0,
long_exponent,
450.0,
alt_exponent,
),
375,
),
(
QuantizedCoordinates::new(
-2.5,
lat_exponent,
170.0,
long_exponent,
450.0,
alt_exponent,
),
346,
),
(
QuantizedCoordinates::new(
-2.5,
lat_exponent,
175.0,
long_exponent,
450.0,
alt_exponent,
),
355,
),
(
QuantizedCoordinates::new(
-2.5,
lat_exponent,
180.0,
long_exponent,
450.0,
alt_exponent,
),
363,
),
] {
let key = Key { epoch, coordinates };
let tec = record
.get(&key)
.expect(&format!("missing value at {:#?}", key));
let tecu = tec.tecu();
let expected = quantized_tecu as f64 * 10.0_f64.powi(tec_exponent as i32);
let err = (tecu - expected).abs();
assert!(err < 1.0E-6);
}
}
}