use core::f64::consts::PI;
const R2D: f64 = 180.0 / PI;
const AHT: [f64; 3] = [2.53e-5, 5.49e-3, 1.14e-3];
const COEF: [[f64; 5]; 9] = [
[
1.2769934e-3,
1.2683230e-3,
1.2465397e-3,
1.2196049e-3,
1.2045996e-3,
], [
2.9153695e-3,
2.9152299e-3,
2.9288445e-3,
2.9022565e-3,
2.9024912e-3,
], [
62.610505e-3,
62.837393e-3,
63.721774e-3,
63.824265e-3,
64.258455e-3,
], [
0.0000000e-0,
1.2709626e-5,
2.6523662e-5,
3.4000452e-5,
4.1202191e-5,
], [
0.0000000e-0,
2.1414979e-5,
3.0160779e-5,
7.2562722e-5,
11.723375e-5,
], [
0.0000000e-0,
9.0128400e-5,
4.3497037e-5,
84.795348e-5,
170.37206e-5,
], [
5.8021897e-4,
5.6794847e-4,
5.8118019e-4,
5.9727542e-4,
6.1641693e-4,
], [
1.4275268e-3,
1.5138625e-3,
1.4572752e-3,
1.5007428e-3,
1.7599082e-3,
], [
4.3472961e-2,
4.6729510e-2,
4.3908931e-2,
4.4626982e-2,
5.4736038e-2,
], ];
const MET_GATE_LOW_M: f64 = -100.0;
const MET_GATE_HI_M: f64 = 1.0e4;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct ZenithComponents {
pub e: f64,
pub zhd_m: f64,
pub zwd_m: f64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct MappingComponents {
pub mh: f64,
pub mw: f64,
pub dm: f64,
pub cosy: f64,
pub y: f64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct SlantComponents {
pub e: f64,
pub zhd_m: f64,
pub zwd_m: f64,
pub mh: f64,
pub mw: f64,
pub dm: f64,
pub cosy: f64,
pub y: f64,
pub slant_m: f64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct StandardAtmosphere {
pub pressure_hpa: f64,
pub temperature_k: f64,
pub relative_humidity: f64,
}
fn interpc(coef: &[f64; 5], lat_deg: f64) -> f64 {
let i = (lat_deg / 15.0) as i32;
if i < 1 {
return coef[0];
}
if i > 4 {
return coef[4];
}
let iu = i as usize;
let fi = i as f64;
coef[iu - 1] * (1.0 - lat_deg / 15.0 + fi) + coef[iu] * (lat_deg / 15.0 - fi)
}
fn mapf(el_rad: f64, a: f64, b: f64, c: f64) -> f64 {
let sinel = el_rad.sin();
(1.0 + a / (1.0 + b / (1.0 + c))) / (sinel + (a / (sinel + b / (sinel + c))))
}
pub(crate) fn zenith_delays(
pressure_hpa: f64,
temperature_k: f64,
relative_humidity: f64,
lat_rad: f64,
height_m: f64,
) -> ZenithComponents {
let t = temperature_k;
let e = 6.108 * relative_humidity * ((17.15 * t - 4684.0) / (t - 38.45)).exp();
let h_km = height_m / 1000.0;
let denom = 1.0 - 0.00266 * (2.0 * lat_rad).cos() - 0.00028 * h_km;
let zhd_m = 0.0022768 * pressure_hpa / denom;
let zwd_m = 0.002277 * (1255.0 / t + 0.05) * e;
ZenithComponents { e, zhd_m, zwd_m }
}
pub(crate) fn standard_atmosphere(height_m: f64, relative_humidity: f64) -> StandardAtmosphere {
let hgt = if height_m > 0.0 { height_m } else { 0.0 };
let pressure_hpa = 1013.25 * (1.0 - 2.2557e-5 * hgt).powf(5.2568);
let temperature_k = 15.0 - 6.5e-3 * hgt + 273.16;
StandardAtmosphere {
pressure_hpa,
temperature_k,
relative_humidity,
}
}
pub(crate) fn niell_mapping(
el_rad: f64,
lat_rad: f64,
height_m: f64,
doy: f64,
) -> MappingComponents {
let lat_deg = lat_rad * R2D;
let y = (doy - 28.0) / 365.25 + if lat_deg < 0.0 { 0.5 } else { 0.0 };
let cosy = (2.0 * PI * y).cos();
let alat = lat_deg.abs();
let mut ah = [0.0f64; 3];
let mut aw = [0.0f64; 3];
for k in 0..3 {
ah[k] = interpc(&COEF[k], alat) - interpc(&COEF[k + 3], alat) * cosy;
aw[k] = interpc(&COEF[k + 6], alat);
}
let dm = (1.0 / el_rad.sin() - mapf(el_rad, AHT[0], AHT[1], AHT[2])) * (height_m / 1000.0);
let mh = mapf(el_rad, ah[0], ah[1], ah[2]) + dm;
let mw = mapf(el_rad, aw[0], aw[1], aw[2]);
MappingComponents {
mh,
mw,
dm,
cosy,
y,
}
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::manual_range_contains)]
pub(crate) fn slant_components(
el_rad: f64,
lat_rad: f64,
_lon_rad: f64,
height_m: f64,
pressure_hpa: f64,
temperature_k: f64,
relative_humidity: f64,
doy: f64,
) -> SlantComponents {
if el_rad <= 0.0 || height_m < MET_GATE_LOW_M || height_m > MET_GATE_HI_M {
return SlantComponents {
e: 0.0,
zhd_m: 0.0,
zwd_m: 0.0,
mh: 0.0,
mw: 0.0,
dm: 0.0,
cosy: 0.0,
y: 0.0,
slant_m: 0.0,
};
}
let z = zenith_delays(
pressure_hpa,
temperature_k,
relative_humidity,
lat_rad,
height_m,
);
let m = niell_mapping(el_rad, lat_rad, height_m, doy);
let slant_m = z.zhd_m * m.mh + z.zwd_m * m.mw;
SlantComponents {
e: z.e,
zhd_m: z.zhd_m,
zwd_m: z.zwd_m,
mh: m.mh,
mw: m.mw,
dm: m.dm,
cosy: m.cosy,
y: m.y,
slant_m,
}
}