149 lines
5.7 KiB
Rust
149 lines
5.7 KiB
Rust
//! The reference standard atmosphere of ITU-R Recommendation P.835.
|
|
//!
|
|
//! The exact slant path method of Recommendation P.676 integrates the
|
|
//! specific attenuation through a stack of layers, so it needs the
|
|
//! temperature, pressure, and water vapour density at each layer height.
|
|
//! This module supplies those from the mean annual global reference
|
|
//! atmosphere of Recommendation P.835.
|
|
//!
|
|
//! Height is measured as geopotential height up to the base of the
|
|
//! thermosphere. The geopotential height is the geometric height adjusted
|
|
//! for the variation of gravity with altitude, and it is the independent
|
|
//! variable the temperature and pressure profiles are expressed in.
|
|
//!
|
|
//! Source: ITU-R Recommendation P.835-6, *Reference Standard Atmospheres*,
|
|
//! section 1, the mean annual global reference atmosphere.
|
|
|
|
/// The radius of the Earth used in the geopotential conversion, in
|
|
/// kilometers.
|
|
///
|
|
/// The recommendation fixes this value for the standard atmosphere, which is
|
|
/// slightly different from the polar radius used elsewhere. It is the value
|
|
/// that makes the geopotential height consistent with the tabulated
|
|
/// profiles.
|
|
const GEOPOTENTIAL_EARTH_RADIUS_KM: f64 = 6_356.766;
|
|
|
|
/// Returns the geopotential height for a geometric height, in kilometers.
|
|
///
|
|
/// The relation is
|
|
///
|
|
/// ```text
|
|
/// h_p = R H / (R + H)
|
|
/// ```
|
|
///
|
|
/// with `R` the fixed Earth radius of the standard atmosphere and `H` the
|
|
/// geometric height. The geopotential height is always the smaller of the
|
|
/// two.
|
|
pub fn geopotential_height(geometric_height_km: f64) -> f64 {
|
|
GEOPOTENTIAL_EARTH_RADIUS_KM * geometric_height_km
|
|
/ (GEOPOTENTIAL_EARTH_RADIUS_KM + geometric_height_km)
|
|
}
|
|
|
|
/// Returns the temperature of the reference atmosphere, in kelvin.
|
|
///
|
|
/// The profile is piecewise linear in geopotential height up to 84.852 km
|
|
/// and is then defined by two further segments that extend to 100 km. Above
|
|
/// 100 km the recommendation does not define a global profile and this
|
|
/// function returns the value at 100 km.
|
|
///
|
|
/// The geometric height is in kilometers.
|
|
pub fn standard_temperature(geometric_height_km: f64) -> f64 {
|
|
let height = geopotential_height(geometric_height_km);
|
|
if height <= 11.0 {
|
|
288.15 - 6.5 * height
|
|
} else if height <= 20.0 {
|
|
216.65
|
|
} else if height <= 32.0 {
|
|
216.65 + (height - 20.0)
|
|
} else if height <= 47.0 {
|
|
228.65 + 2.8 * (height - 32.0)
|
|
} else if height <= 51.0 {
|
|
270.65
|
|
} else if height <= 71.0 {
|
|
270.65 - 2.8 * (height - 51.0)
|
|
} else if height <= 84.852 {
|
|
214.65 - 2.0 * (height - 71.0)
|
|
} else if (86.0..=91.0).contains(&geometric_height_km) {
|
|
186.867_3
|
|
} else if geometric_height_km > 91.0 && geometric_height_km <= 100.0 {
|
|
let ratio = (geometric_height_km - 91.0) / 19.942_9;
|
|
263.190_5 - 76.323_2 * (1.0 - ratio * ratio).max(0.0).sqrt()
|
|
} else {
|
|
195.081_34
|
|
}
|
|
}
|
|
|
|
/// Returns the pressure of the reference atmosphere, in hectopascals.
|
|
///
|
|
/// The profile is piecewise, continuous at the segment boundaries, and
|
|
/// follows from the hydrostatic relation applied to each temperature
|
|
/// segment.
|
|
///
|
|
/// The geometric height is in kilometers.
|
|
pub fn standard_pressure(geometric_height_km: f64) -> f64 {
|
|
let height = geopotential_height(geometric_height_km);
|
|
if height <= 11.0 {
|
|
1_013.25 * (288.15 / (288.15 - 6.5 * height)).powf(-34.163_2 / 6.5)
|
|
} else if height <= 20.0 {
|
|
226.322_6 * (-34.163_2 * (height - 11.0) / 216.65).exp()
|
|
} else if height <= 32.0 {
|
|
54.749_80 * (216.65 / (216.65 + (height - 20.0))).powf(34.163_2)
|
|
} else if height <= 47.0 {
|
|
8.680_422 * (228.65 / (228.65 + 2.8 * (height - 32.0))).powf(34.163_2 / 2.8)
|
|
} else if height <= 51.0 {
|
|
1.109_106 * (-34.163_2 * (height - 47.0) / 270.65).exp()
|
|
} else if height <= 71.0 {
|
|
0.669_416_7 * (270.65 / (270.65 - 2.8 * (height - 51.0))).powf(-34.163_2 / 2.8)
|
|
} else if height <= 84.852 {
|
|
0.039_566_49 * (214.65 / (214.65 - 2.0 * (height - 71.0))).powf(-34.163_2 / 2.0)
|
|
} else if (86.0..=100.0).contains(&geometric_height_km) {
|
|
let h = geometric_height_km;
|
|
(95.571_899 - 4.011_801 * h + 6.424_731e-2 * h * h - 4.789_660e-4 * h * h * h
|
|
+ 1.340_543e-6 * h * h * h * h)
|
|
.exp()
|
|
} else {
|
|
1.0e-62
|
|
}
|
|
}
|
|
|
|
/// Returns the water vapour density of the reference atmosphere, in grams
|
|
/// per cubic meter.
|
|
///
|
|
/// The profile is exponential with a scale height of two kilometers, scaled
|
|
/// so that the density at the surface is the value given.
|
|
///
|
|
/// The geometric height is in kilometers.
|
|
pub fn standard_water_vapour_density(geometric_height_km: f64, surface_density: f64) -> f64 {
|
|
surface_density * (-geometric_height_km / 2.0).exp()
|
|
}
|
|
|
|
/// Returns the radio refractive index of moist air, dimensionless.
|
|
///
|
|
/// The relation is
|
|
///
|
|
/// ```text
|
|
/// N = 77.6 P_d / T + 72 e / T + 3.75e5 e / T^2
|
|
/// n = 1 + N 1e-6
|
|
/// ```
|
|
///
|
|
/// with `P_d` the dry air pressure and `e` the water vapour partial
|
|
/// pressure, both in hectopascals, and `T` the temperature in kelvin. The
|
|
/// first two terms are the non-dispersive refractivity and the third is the
|
|
/// dipole term that gives water vapour its strong effect.
|
|
///
|
|
/// Source: ITU-R Recommendation P.453, which gives this expression for the
|
|
/// refractive index of moist air.
|
|
pub fn radio_refractive_index(
|
|
dry_pressure_hpa: f64,
|
|
vapour_pressure_hpa: f64,
|
|
temperature_k: f64,
|
|
) -> f64 {
|
|
if temperature_k == 0.0 {
|
|
return 1.0;
|
|
}
|
|
let refractivity = 77.6 * dry_pressure_hpa / temperature_k
|
|
+ 72.0 * vapour_pressure_hpa / temperature_k
|
|
+ 3.75e5 * vapour_pressure_hpa / (temperature_k * temperature_k);
|
|
1.0 + refractivity * 1.0e-6
|
|
}
|