use crate::{
prelude::{coord, Duration, MappingFunction, Rect, Version, IONEX},
tests::{
init_logger,
toolkit::{generic_comparison, generic_test, TestPoint},
},
};
use std::fs::File;
use std::io::BufWriter;
#[test]
fn parse_ckmg0020() {
init_logger();
let ionex = IONEX::from_gzip_file("data/IONEX/V1/CKMG0020.22I.gz").unwrap_or_else(|e| {
panic!("Failed to parse CKMG0020: {}", e);
});
assert!(ionex.is_2d());
assert!(!ionex.is_3d());
let testpoints = vec![
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: -180.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: -175.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: -170.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: 0.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: -5.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: 5.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 85.0,
long_ddeg: -180.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 85.0,
long_ddeg: 180.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 42.5,
long_ddeg: 125.0,
alt_km: 350.0,
tecu: 10.6,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 42.5,
long_ddeg: 130.0,
alt_km: 350.0,
tecu: 10.8,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 42.5,
long_ddeg: 135.0,
alt_km: 350.0,
tecu: 10.8,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 42.5,
long_ddeg: 140.0,
alt_km: 350.0,
tecu: 10.7,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T00:00:00 UTC",
lat_ddeg: 42.5,
long_ddeg: 145.0,
alt_km: 350.0,
tecu: 10.4,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T01:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: -180.0,
alt_km: 350.0,
tecu: 9.2,
rms: None,
},
TestPoint {
epoch_str: "2022-01-02T01:00:00 UTC",
lat_ddeg: 30.0,
long_ddeg: -180.0,
alt_km: 350.0,
tecu: 21.8,
rms: None,
},
TestPoint {
epoch_str: "2022-01-03T00:00:00 UTC",
lat_ddeg: -67.5,
long_ddeg: -180.0,
alt_km: 350.0,
tecu: 12.8,
rms: None,
},
TestPoint {
epoch_str: "2022-01-03T00:00:00 UTC",
lat_ddeg: -67.5,
long_ddeg: -170.0,
alt_km: 350.0,
tecu: 12.9,
rms: None,
},
TestPoint {
epoch_str: "2022-01-03T00:00:00 UTC",
lat_ddeg: -67.5,
long_ddeg: -165.0,
alt_km: 350.0,
tecu: 13.0,
rms: None,
},
];
generic_test(&ionex, &testpoints);
for (k, tec) in ionex.record.iter() {
assert!(tec.root_mean_square().is_none());
assert_eq!(k.altitude_km(), 350.0);
}
assert_eq!(ionex.header.version, Version::new(1, 0));
assert_eq!(ionex.header.program, Some("BIMINX V5.3".to_string()));
assert_eq!(ionex.header.run_by, Some("AIUB".to_string()));
assert_eq!(ionex.header.date, Some("07-JAN-22 07:51".to_string()));
assert!(ionex.header.doi.is_none());
assert!(ionex.header.license.is_none());
assert_eq!(ionex.header.number_of_maps, 25);
assert_eq!(
ionex.header.epoch_of_first_map.to_string().as_str(),
"2022-01-02T00:00:00 UTC"
);
assert_eq!(
ionex.header.epoch_of_last_map.to_string().as_str(),
"2022-01-03T00:00:00 UTC"
);
assert_eq!(ionex.header.base_radius_km, 6371.0);
assert_eq!(ionex.header.mapf, MappingFunction::None);
assert_eq!(ionex.header.sampling_period, Duration::from_hours(1.0));
assert_eq!(ionex.header.grid.latitude.start, 87.5);
assert_eq!(ionex.header.grid.latitude.end, -87.5);
assert_eq!(ionex.header.grid.latitude.spacing, -2.5);
assert_eq!(ionex.header.grid.longitude.start, -180.0);
assert_eq!(ionex.header.grid.longitude.end, 180.0);
assert_eq!(ionex.header.grid.longitude.spacing, 5.0);
assert_eq!(ionex.header.grid.altitude.start, 350.0);
assert_eq!(ionex.header.grid.altitude.end, 350.0);
assert_eq!(ionex.header.grid.altitude.spacing, 0.0);
assert_eq!(ionex.header.elevation_cutoff, 0.0);
assert_eq!(ionex.header.exponent, -1);
assert_eq!(
ionex.map_borders_degrees(),
Rect::new(coord!(x: -180.0, y: -87.5), coord!(x: 180.0, y: 87.5))
);
assert_eq!(ionex.header.comments.len(), 2);
assert_eq!(
ionex.header.comments[0],
"CODE'S KLOBUCHAR-STYLE IONOSPHERE MODEL FOR DAY 002, 2022"
);
assert_eq!(
ionex.header.comments[1],
"TEC/RMS values in 0.1 TECU; 9999, if no value available"
);
let fd = File::create("ckmg-v1.txt").unwrap();
let mut writer = BufWriter::new(fd);
ionex.format(&mut writer).unwrap_or_else(|e| {
panic!("failed to format CKMG V1: {}", e);
});
let parsed = IONEX::from_file("ckmg-v1.txt").unwrap_or_else(|e| {
panic!("failed to parse back CKMG V1: {}", e);
});
generic_test(&parsed, &testpoints);
generic_comparison(&parsed, &ionex);
}
#[test]
fn parse_jplg() {
init_logger();
let ionex = IONEX::from_gzip_file("data/IONEX/V1/jplg0010.17i.gz").unwrap_or_else(|e| {
panic!("Failed to parse V1/jplg0010.17i.gz: {}", e);
});
assert!(ionex.is_2d());
assert!(!ionex.is_3d());
let testpoints = vec![
TestPoint {
epoch_str: "2017-01-01T00:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: -180.0,
alt_km: 450.0,
tecu: 3.3,
rms: None,
},
TestPoint {
epoch_str: "2017-01-01T00:00:00 UTC",
lat_ddeg: 87.5,
long_ddeg: -175.0,
alt_km: 450.0,
tecu: 3.3,
rms: None,
},
];
generic_test(&ionex, &testpoints);
for (k, tec) in ionex.record.iter() {
assert!(tec.root_mean_square().is_some());
assert_eq!(k.altitude_km(), 450.0);
}
assert_eq!(ionex.header.version, Version::new(1, 0));
assert_eq!(ionex.header.program, Some("GIM V3.0".to_string()));
assert_eq!(ionex.header.run_by, Some("JPL - GNISD".to_string()));
assert_eq!(ionex.header.date, Some("04-jan-2017 02:12".to_string()));
assert!(ionex.header.doi.is_none());
assert!(ionex.header.license.is_none());
assert_eq!(ionex.header.number_of_maps, 13);
assert_eq!(
ionex.header.epoch_of_first_map.to_string().as_str(),
"2017-01-01T00:00:00 UTC"
);
assert_eq!(
ionex.header.epoch_of_last_map.to_string().as_str(),
"2017-01-02T00:00:00 UTC"
);
assert_eq!(ionex.header.num_stations, 170);
assert_eq!(ionex.header.num_satellites, 31);
assert_eq!(ionex.header.base_radius_km, 6371.0);
assert_eq!(ionex.header.mapf, MappingFunction::None);
assert_eq!(ionex.header.sampling_period, Duration::from_hours(2.0));
assert_eq!(ionex.header.grid.latitude.start, 87.5);
assert_eq!(ionex.header.grid.latitude.end, -87.5);
assert_eq!(ionex.header.grid.latitude.spacing, -2.5);
assert_eq!(ionex.header.grid.longitude.start, -180.0);
assert_eq!(ionex.header.grid.longitude.end, 180.0);
assert_eq!(ionex.header.grid.longitude.spacing, 5.0);
assert_eq!(ionex.header.grid.altitude.start, 450.0);
assert_eq!(ionex.header.grid.altitude.end, 450.0);
assert_eq!(ionex.header.grid.altitude.spacing, 0.0);
assert_eq!(ionex.header.elevation_cutoff, 10.0);
assert_eq!(ionex.header.exponent, -1);
assert_eq!(ionex.header.comments.len(), 2);
assert_eq!(
ionex.header.comments[0],
"JPL'S GLOBAL IONOSPHERE MAPS YEAR 2017 DAY 001"
);
assert_eq!(
ionex.header.description,
Some(
"Global Ionospheric Maps (GIM) are generated on an hourly
and daily basis at JPL using data from up to 100 GPS sites
of the IGS and others institutions.
The vertical TEC is modeled in a solar-geomagnetic
reference frame using bi-cubic splines on a spherical grid.
A Kalman filter is used to solve simultaneously for
instrumental biases and VTEC on the grid (as stochastic
parameters).
Contact Address: gpsiono@cobra.jpl.nasa.gov ."
.to_string()
)
);
let fd = File::create("jplg-v1.txt").unwrap();
let mut writer = BufWriter::new(fd);
ionex.format(&mut writer).unwrap_or_else(|e| {
panic!("failed to format JPLG V1: {}", e);
});
let parsed = IONEX::from_file("jplg-v1.txt").unwrap_or_else(|e| {
panic!("failed to parse back JPLG V1: {}", e);
});
generic_test(&parsed, &testpoints);
generic_comparison(&parsed, &ionex);
}