docs: bilingual comments for the atmosphere attenuation models

The P.838 rain curves, the P.676 gaseous lines, the P.839 isotherm table
and the P.835 layered refraction.
This commit is contained in:
ApsisRS contributors 2026-10-02 01:28:58 +08:00
parent f3cf18df41
commit cfe8d72bb2
7 changed files with 379 additions and 0 deletions

View File

@ -28,42 +28,89 @@ use core::f64::consts::E;
/// The coefficients are the ones tabulated in table 1 of ITU-R P.676-12. The /// The coefficients are the ones tabulated in table 1 of ITU-R P.676-12. The
/// first field is the line frequency in gigahertz and the rest are the /// first field is the line frequency in gigahertz and the rest are the
/// dimensionless coefficients of the line. /// dimensionless coefficients of the line.
///
/// 一条氧吸收谱线。
///
/// 各系数即 ITU-R P.676-12 表 1 所列。第一个字段是线频率,以吉赫兹计;
/// 其余为谱线的无量纲系数。
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct OxygenLine { struct OxygenLine {
/// The line frequency, in gigahertz. /// The line frequency, in gigahertz.
///
/// 线频率,以吉赫兹计。
///
/// 线频率,以吉赫兹计。
f0: f64, f0: f64,
/// Line strength coefficient. /// Line strength coefficient.
///
/// 谱线强度系数。
///
/// 谱线强度系数。
a1: f64, a1: f64,
/// Temperature dependence of the line strength. /// Temperature dependence of the line strength.
///
/// 谱线强度的温度依赖。
///
/// 谱线强度的温度依赖。
a2: f64, a2: f64,
/// Line width coefficient. /// Line width coefficient.
///
/// 谱线宽度系数。
///
/// 谱线宽度系数。
a3: f64, a3: f64,
/// Pressure dependence of the line width. /// Pressure dependence of the line width.
///
/// 谱线宽度的气压依赖。
///
/// 谱线宽度的气压依赖。
a4: f64, a4: f64,
/// Pressure shift coefficient. /// Pressure shift coefficient.
///
/// 气压频移系数。
a5: f64, a5: f64,
/// Mixing ratio dependence of the pressure shift. /// Mixing ratio dependence of the pressure shift.
///
/// 气压频移的混合比依赖。
a6: f64, a6: f64,
} }
/// One water vapour absorption line. /// One water vapour absorption line.
/// ///
/// The coefficients are the ones tabulated in table 2 of ITU-R P.676-12. /// The coefficients are the ones tabulated in table 2 of ITU-R P.676-12.
///
/// 一条水汽吸收谱线。
///
/// 各系数即 ITU-R P.676-12 表 2 所列。
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct WaterLine { struct WaterLine {
/// The line frequency, in gigahertz. /// The line frequency, in gigahertz.
///
/// 线频率,以吉赫兹计。
f0: f64, f0: f64,
/// Line strength coefficient. /// Line strength coefficient.
///
/// 谱线强度系数。
b1: f64, b1: f64,
/// Temperature dependence of the line strength. /// Temperature dependence of the line strength.
///
/// 谱线强度的温度依赖。
b2: f64, b2: f64,
/// Line width coefficient. /// Line width coefficient.
///
/// 谱线宽度系数。
b3: f64, b3: f64,
/// Pressure dependence of the line width. /// Pressure dependence of the line width.
///
/// 谱线宽度的气压依赖。
b4: f64, b4: f64,
/// Water vapour dependence of the line width. /// Water vapour dependence of the line width.
///
/// 谱线宽度的水汽依赖。
b5: f64, b5: f64,
/// Temperature dependence of the water vapour term. /// Temperature dependence of the water vapour term.
///
/// 水汽项的温度依赖。
b6: f64, b6: f64,
} }
@ -72,6 +119,11 @@ struct WaterLine {
/// The first thirty seven entries are the fine structure of the 60 GHz /// The first thirty seven entries are the fine structure of the 60 GHz
/// complex, followed by the isolated line at 118.75 GHz and six lines in the /// complex, followed by the isolated line at 118.75 GHz and six lines in the
/// submillimetre region. /// submillimetre region.
///
/// ITU-R P.676-12 表 1 的氧吸收谱线。
///
/// 前三十七项是 60 GHz 复合体的精细结构,其后是 118.75 GHz 处的孤立线
/// 以及亚毫米波段的六条线。
const OXYGEN_LINES: &[OxygenLine] = &[ const OXYGEN_LINES: &[OxygenLine] = &[
OxygenLine { OxygenLine {
f0: 50.474_214, f0: 50.474_214,
@ -472,6 +524,8 @@ const OXYGEN_LINES: &[OxygenLine] = &[
]; ];
/// The water vapour absorption lines of table 2 of ITU-R P.676-12. /// The water vapour absorption lines of table 2 of ITU-R P.676-12.
///
/// ITU-R P.676-12 表 2 的水汽吸收谱线。
const WATER_LINES: &[WaterLine] = &[ const WATER_LINES: &[WaterLine] = &[
WaterLine { WaterLine {
f0: 22.235_080, f0: 22.235_080,
@ -795,13 +849,24 @@ const WATER_LINES: &[WaterLine] = &[
/// The pressure is the dry air pressure, as the recommendation specifies. The /// The pressure is the dry air pressure, as the recommendation specifies. The
/// water vapour density is separate because the partial pressure of water /// water vapour density is separate because the partial pressure of water
/// vapour is derived from it. /// vapour is derived from it.
///
/// 气体衰减所依赖的大气状态。
///
/// 气压是干空气气压,如建议书所规定。水汽密度单列,因为水汽分压由它
/// 导出。
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Atmosphere { pub struct Atmosphere {
/// The dry air pressure, in hectopascals. /// The dry air pressure, in hectopascals.
///
/// 干空气气压,以百帕计。
pub pressure_hpa: f64, pub pressure_hpa: f64,
/// The water vapour density, in grams per cubic meter. /// The water vapour density, in grams per cubic meter.
///
/// 水汽密度,以克每立方米计。
pub water_vapour_density: f64, pub water_vapour_density: f64,
/// The temperature, in kelvin. /// The temperature, in kelvin.
///
/// 温度,以开尔文计。
pub temperature_k: f64, pub temperature_k: f64,
} }
@ -811,6 +876,11 @@ impl Atmosphere {
/// The values are the ones the recommendation uses for its reference /// The values are the ones the recommendation uses for its reference
/// curves: a dry air pressure of 1013.25 hPa, a water vapour density of /// curves: a dry air pressure of 1013.25 hPa, a water vapour density of
/// 7.5 g/m^3, and a temperature of 288.15 K. /// 7.5 g/m^3, and a temperature of 288.15 K.
///
/// 平均海平面处的 ITU-R 参考大气。
///
/// 各值即该建议书用于其参考曲线者:干空气气压 1013.25 hPa,水汽
/// 密度 7.5 g/m^3,温度 288.15 K。
pub const REFERENCE: Self = Self { pub const REFERENCE: Self = Self {
pressure_hpa: 1_013.25, pressure_hpa: 1_013.25,
water_vapour_density: 7.5, water_vapour_density: 7.5,
@ -818,6 +888,8 @@ impl Atmosphere {
}; };
/// Creates an atmosphere from its three parameters. /// Creates an atmosphere from its three parameters.
///
/// 由其三个参数创建大气。
pub const fn new(pressure_hpa: f64, water_vapour_density: f64, temperature_k: f64) -> Self { pub const fn new(pressure_hpa: f64, water_vapour_density: f64, temperature_k: f64) -> Self {
Self { Self {
pressure_hpa, pressure_hpa,
@ -830,11 +902,18 @@ impl Atmosphere {
/// ///
/// The relation is `e = rho T / 216.7`, which follows from the ideal gas /// The relation is `e = rho T / 216.7`, which follows from the ideal gas
/// law with the molar masses of water and dry air. /// law with the molar masses of water and dry air.
///
/// 返回水汽分压,以百帕计。
///
/// 关系为 `e = rho T / 216.7`,它由理想气体定律连同水与干空气的摩尔
/// 质量得出。
pub fn water_vapour_partial_pressure(self) -> f64 { pub fn water_vapour_partial_pressure(self) -> f64 {
self.water_vapour_density * self.temperature_k / 216.7 self.water_vapour_density * self.temperature_k / 216.7
} }
/// Returns `true` when the state describes a physical atmosphere. /// Returns `true` when the state describes a physical atmosphere.
///
/// 当该状态描述一个物理大气时返回 `true`。
pub fn is_valid(self) -> bool { pub fn is_valid(self) -> bool {
self.pressure_hpa.is_finite() self.pressure_hpa.is_finite()
&& self.pressure_hpa > 0.0 && self.pressure_hpa > 0.0
@ -869,11 +948,13 @@ pub fn specific_attenuation_dry_air(frequency_ghz: f64, atmosphere: Atmosphere)
let mut sum = 0.0; let mut sum = 0.0;
for line in OXYGEN_LINES { for line in OXYGEN_LINES {
// Equations 5 and 6: the line width and the pressure shift. // Equations 5 and 6: the line width and the pressure shift.
// 式 5 与式 6:谱线宽度与气压频移。
let width = line.a3 * 1.0e-4 * (p * theta.powf(0.8 - line.a4) + 1.1 * e * theta); let width = line.a3 * 1.0e-4 * (p * theta.powf(0.8 - line.a4) + 1.1 * e * theta);
let width = (width * width + 2.25e-6).sqrt(); let width = (width * width + 2.25e-6).sqrt();
let shift = (line.a5 + line.a6 * theta) * 1.0e-4 * (p + e) * theta.powf(0.8); let shift = (line.a5 + line.a6 * theta) * 1.0e-4 * (p + e) * theta.powf(0.8);
// Equation 4: the line shape factor. // Equation 4: the line shape factor.
// 式 4:线型因子。
let shape = frequency_ghz / line.f0 let shape = frequency_ghz / line.f0
* ((width - shift * (line.f0 - frequency_ghz)) * ((width - shift * (line.f0 - frequency_ghz))
/ ((line.f0 - frequency_ghz).powi(2) + width * width) / ((line.f0 - frequency_ghz).powi(2) + width * width)
@ -881,11 +962,13 @@ pub fn specific_attenuation_dry_air(frequency_ghz: f64, atmosphere: Atmosphere)
/ ((line.f0 + frequency_ghz).powi(2) + width * width)); / ((line.f0 + frequency_ghz).powi(2) + width * width));
// Equation 3: the line strength. // Equation 3: the line strength.
// 式 3:谱线强度。
let strength = line.a1 * 1.0e-7 * p * theta.powi(3) * E.powf(line.a2 * (1.0 - theta)); let strength = line.a1 * 1.0e-7 * p * theta.powi(3) * E.powf(line.a2 * (1.0 - theta));
sum += strength * shape; sum += strength * shape;
} }
// The non-resonant Debye spectrum, equation 8. // The non-resonant Debye spectrum, equation 8.
// 非共振 Debye 谱,式 8。
let d = 5.6e-4 * (p + e) * theta.powf(0.8); let d = 5.6e-4 * (p + e) * theta.powf(0.8);
let debye = frequency_ghz let debye = frequency_ghz
* p * p
@ -924,6 +1007,7 @@ pub fn specific_attenuation_water_vapour(
for line in WATER_LINES { for line in WATER_LINES {
// Equation 7: the line width, which has a different form from the // Equation 7: the line width, which has a different form from the
// oxygen case. // oxygen case.
// 式 7:谱线宽度,其形式与氧的情形不同。
let width = let width =
line.b3 * 1.0e-4 * (p * theta.powf(line.b4) + line.b5 * e * theta.powf(line.b6)); line.b3 * 1.0e-4 * (p * theta.powf(line.b4) + line.b5 * e * theta.powf(line.b6));
let width = 0.535 * width let width = 0.535 * width
@ -934,6 +1018,7 @@ pub fn specific_attenuation_water_vapour(
+ width / ((line.f0 + frequency_ghz).powi(2) + width * width)); + width / ((line.f0 + frequency_ghz).powi(2) + width * width));
// Equation 3 for the water vapour lines. // Equation 3 for the water vapour lines.
// 对水汽谱线的式 3。
let strength = line.b1 * 1.0e-1 * e * theta.powf(3.5) * E.powf(line.b2 * (1.0 - theta)); let strength = line.b1 * 1.0e-1 * e * theta.powf(3.5) * E.powf(line.b2 * (1.0 - theta));
sum += strength * shape; sum += strength * shape;
} }

View File

@ -19,6 +19,10 @@ use crate::gaseous::{Atmosphere, specific_attenuation_dry_air, specific_attenuat
/// The coefficients of the dry air equivalent height, table 3 of P.676-12. /// The coefficients of the dry air equivalent height, table 3 of P.676-12.
/// ///
/// Each entry is the coefficient and the line frequency in gigahertz. /// Each entry is the coefficient and the line frequency in gigahertz.
///
/// 干空气等效高度的系数,P.676-12 表 3。
///
/// 每项是系数与线频率,后者以吉赫兹计。
const DRY_HEIGHT_TERMS: &[(f64, f64)] = &[ const DRY_HEIGHT_TERMS: &[(f64, f64)] = &[
(0.159_7, 118.750_334), (0.159_7, 118.750_334),
(0.106_6, 368.498_246), (0.106_6, 368.498_246),
@ -34,6 +38,10 @@ const DRY_HEIGHT_TERMS: &[(f64, f64)] = &[
/// ///
/// Each entry is the line frequency in gigahertz, a coefficient, and a /// Each entry is the line frequency in gigahertz, a coefficient, and a
/// width-like parameter. /// width-like parameter.
///
/// 水汽等效高度的系数,P.676-12 表 4。
///
/// 每项是线频率(以吉赫兹计)、一个系数,以及一个类宽度参数。
const WET_HEIGHT_TERMS: &[(f64, f64, f64)] = &[ const WET_HEIGHT_TERMS: &[(f64, f64, f64)] = &[
(22.235_08, 1.52, 2.56), (22.235_08, 1.52, 2.56),
(183.310_087, 7.62, 10.2), (183.310_087, 7.62, 10.2),
@ -52,11 +60,17 @@ const WET_HEIGHT_TERMS: &[(f64, f64, f64)] = &[
]; ];
/// The equivalent height of dry air and of water vapour, in kilometers. /// The equivalent height of dry air and of water vapour, in kilometers.
///
/// 干空气与水汽的等效高度,单位为千米。
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct EquivalentHeights { pub struct EquivalentHeights {
/// The equivalent height of dry air, in kilometers. /// The equivalent height of dry air, in kilometers.
///
/// 干空气的等效高度,单位为千米。
pub dry_km: f64, pub dry_km: f64,
/// The equivalent height of water vapour, in kilometers. /// The equivalent height of water vapour, in kilometers.
///
/// 水汽的等效高度,单位为千米。
pub wet_km: f64, pub wet_km: f64,
} }
@ -86,10 +100,12 @@ pub fn equivalent_heights(frequency_ghz: f64, atmosphere: Atmosphere) -> Option<
let rp = (atmosphere.pressure_hpa + e) / 1_013.25; let rp = (atmosphere.pressure_hpa + e) / 1_013.25;
// Equation 31, the resonance term centred just below 60 GHz. // Equation 31, the resonance term centred just below 60 GHz.
// 式 31,中心略低于 60 GHz 的共振项。
let term1 = 5.104_0 / (1.0 + 0.066 * rp.powf(-2.3)) let term1 = 5.104_0 / (1.0 + 0.066 * rp.powf(-2.3))
* (-((frequency_ghz - 59.7) / (2.87 + 12.4 * (-7.9 * rp).exp())).powi(2)).exp(); * (-((frequency_ghz - 59.7) / (2.87 + 12.4 * (-7.9 * rp).exp())).powi(2)).exp();
// Equation 32, the residual contribution of the submillimetre lines. // Equation 32, the residual contribution of the submillimetre lines.
// 式 32,亚毫米波各线的残余贡献。
let term2: f64 = DRY_HEIGHT_TERMS let term2: f64 = DRY_HEIGHT_TERMS
.iter() .iter()
.map(|&(coefficient, line_frequency)| { .map(|&(coefficient, line_frequency)| {
@ -99,6 +115,7 @@ pub fn equivalent_heights(frequency_ghz: f64, atmosphere: Atmosphere) -> Option<
.sum(); .sum();
// Equation 33, the far wing correction. // Equation 33, the far wing correction.
// 式 33,远翼修正。
let term3 = 0.011_4 * frequency_ghz / (1.0 + 0.14 * rp.powf(-2.6)) let term3 = 0.011_4 * frequency_ghz / (1.0 + 0.14 * rp.powf(-2.6))
* (15.02 * frequency_ghz.powi(2) - 1_353.0 * frequency_ghz + 5.333e4) * (15.02 * frequency_ghz.powi(2) - 1_353.0 * frequency_ghz + 5.333e4)
/ (frequency_ghz.powi(3) - 151.3 * frequency_ghz.powi(2) + 9_629.0 * frequency_ghz / (frequency_ghz.powi(3) - 151.3 * frequency_ghz.powi(2) + 9_629.0 * frequency_ghz
@ -108,6 +125,7 @@ pub fn equivalent_heights(frequency_ghz: f64, atmosphere: Atmosphere) -> Option<
// Equation 30, with the pressure dependent cap that applies below // Equation 30, with the pressure dependent cap that applies below
// 70 GHz. // 70 GHz.
// 式 30,含适用于 70 GHz 以下、依赖气压的上限。
let dry = 6.1 * a / (1.0 + 0.17 * rp.powf(-1.1)) * (1.0 + term1 + term2 + term3); let dry = 6.1 * a / (1.0 + 0.17 * rp.powf(-1.1)) * (1.0 + term1 + term2 + term3);
let dry = if frequency_ghz < 70.0 { let dry = if frequency_ghz < 70.0 {
dry.min(10.7 * rp.powf(0.3)) dry.min(10.7 * rp.powf(0.3))
@ -116,6 +134,7 @@ pub fn equivalent_heights(frequency_ghz: f64, atmosphere: Atmosphere) -> Option<
}; };
// Equations 36 through 38 and 35b. // Equations 36 through 38 and 35b.
// 式 36 至式 38 以及式 35b。
let a_wet = 1.929_8 - 0.041_66 * (temperature - 273.15) + 0.051_7 * rho; let a_wet = 1.929_8 - 0.041_66 * (temperature - 273.15) + 0.051_7 * rho;
let b_wet = 1.167_4 - 0.006_22 * (temperature - 273.15) + 0.006_3 * rho; let b_wet = 1.167_4 - 0.006_22 * (temperature - 273.15) + 0.006_3 * rho;
let sigma = 1.013 / (1.0 + (-8.6 * (rp - 0.57)).exp()); let sigma = 1.013 / (1.0 + (-8.6 * (rp - 0.57)).exp());
@ -166,6 +185,15 @@ pub fn zenith_attenuation(frequency_ghz: f64, atmosphere: Atmosphere) -> Option<
/// ///
/// Returns `None` when the frequency or atmosphere is not usable, or when /// Returns `None` when the frequency or atmosphere is not usable, or when
/// the elevation is at or below the horizon. /// the elevation is at or below the horizon.
///
/// 返回某仰角下沿斜路径的衰减,单位为分贝。
///
/// 天顶衰减除以仰角的正弦。其使用的平面平行近似即建议书用于近似方法
/// 者;在约五度仰角以上是准确的。
///
/// 仰角以弧度计。
///
/// 频率或大气不可用,或仰角在地平及其以下时返回 `None`。
pub fn slant_path_attenuation( pub fn slant_path_attenuation(
frequency_ghz: f64, frequency_ghz: f64,
elevation: f64, elevation: f64,
@ -184,6 +212,12 @@ pub fn slant_path_attenuation(
/// boundaries. The layers between them are thin near the surface and grow /// boundaries. The layers between them are thin near the surface and grow
/// with height, so the integration resolves the dense lower atmosphere where /// with height, so the integration resolves the dense lower atmosphere where
/// the absorption is concentrated. /// the absorption is concentrated.
///
/// 精确斜路径积分所用的层界数。
///
/// 建议书 P.676 以 `delta_i = 1e-4 exp(i / 100)`(`i` 从零到 921)定义
/// 层界,共得 922 个层界。其间的层在地表附近很薄并随高度增厚,故该积分
/// 能分辨吸收集中的稠密低层大气。
const EXACT_BOUNDARY_COUNT: usize = 922; const EXACT_BOUNDARY_COUNT: usize = 922;
/// The Earth radius used by the ray trace, in kilometers. /// The Earth radius used by the ray trace, in kilometers.
@ -191,6 +225,11 @@ const EXACT_BOUNDARY_COUNT: usize = 922;
/// The recommendation uses the mean radius, not the geopotential radius of /// The recommendation uses the mean radius, not the geopotential radius of
/// the standard atmosphere, because this value enters the geometry of the /// the standard atmosphere, because this value enters the geometry of the
/// ray rather than the definition of the height. /// ray rather than the definition of the height.
///
/// 射线追踪所用的地球半径,单位为千米。
///
/// 建议书用的是平均半径,而非标准大气的地势半径,因为该值进入射线的
/// 几何,而非高度的定义。
const RAY_TRACE_EARTH_RADIUS_KM: f64 = 6_371.0; const RAY_TRACE_EARTH_RADIUS_KM: f64 = 6_371.0;
/// Returns the attenuation along a slant path by the exact method of /// Returns the attenuation along a slant path by the exact method of
@ -219,6 +258,25 @@ const RAY_TRACE_EARTH_RADIUS_KM: f64 = 6_371.0;
/// ///
/// Source: ITU-R Recommendation P.676-12, equations 13 through 19, with the /// Source: ITU-R Recommendation P.676-12, equations 13 through 19, with the
/// layered atmosphere of ITU-R Recommendation P.835-6. /// layered atmosphere of ITU-R Recommendation P.835-6.
///
/// 以建议书 P.676 的精确方法返回沿斜路径的衰减。
///
/// 精确方法把大气分成厚度随高度指数增长的一叠层,计算每层的比衰减,
/// 并按射线穿过该层的路径长度对每层的贡献加权求和。由于射线发生折射,
/// 路径长度以射线追踪求得,其中对相邻层折射率之比施加 Snell 定律,而
/// 近似方法忽略这一点。
///
/// 这是低仰角下宜用的形式,此时 [`slant_path_attenuation`] 所用的平面
/// 平行近似不准确。它的代价高得多,故近似形式仍可供其足够胜任的高仰角
/// 情形使用。
///
/// 频率以吉赫兹计,仰角以弧度计,地表大气提供地面处的气压、水汽密度与
/// 温度。地表水汽密度缩放指数水汽廓线,故该大气界定了整叠层。
///
/// 频率、仰角或大气不可用时返回 `None`。
///
/// 出处:ITU-R 建议书 P.676-12,式 13 至式 19,分层大气取自 ITU-R
/// 建议书 P.835-6。
pub fn slant_path_attenuation_exact( pub fn slant_path_attenuation_exact(
frequency_ghz: f64, frequency_ghz: f64,
elevation: f64, elevation: f64,
@ -233,6 +291,7 @@ pub fn slant_path_attenuation_exact(
// Build the stack of layer boundaries. Each entry holds the geometric // Build the stack of layer boundaries. Each entry holds the geometric
// height, the thickness, the profile values, and the refractive index. // height, the thickness, the profile values, and the refractive index.
// 构建层界叠层。每项保存几何高度、厚度、廓线值与折射率。
let thickness_step = (1.0_f64 / 100.0).exp(); let thickness_step = (1.0_f64 / 100.0).exp();
let mut heights = [0.0_f64; EXACT_BOUNDARY_COUNT]; let mut heights = [0.0_f64; EXACT_BOUNDARY_COUNT];
let mut thicknesses = [0.0_f64; EXACT_BOUNDARY_COUNT]; let mut thicknesses = [0.0_f64; EXACT_BOUNDARY_COUNT];
@ -245,6 +304,7 @@ pub fn slant_path_attenuation_exact(
let exponent = index as f64 / 100.0; let exponent = index as f64 / 100.0;
thicknesses[index] = 1.0e-4 * exponent.exp(); thicknesses[index] = 1.0e-4 * exponent.exp();
// Equation 15: the geometric height of the boundary. // Equation 15: the geometric height of the boundary.
// 式 15:层界的几何高度。
heights[index] = 1.0e-4 * (exponent.exp() - 1.0) / (thickness_step - 1.0); heights[index] = 1.0e-4 * (exponent.exp() - 1.0) / (thickness_step - 1.0);
let temperature = crate::p835::standard_temperature(heights[index]); let temperature = crate::p835::standard_temperature(heights[index]);
@ -259,6 +319,7 @@ pub fn slant_path_attenuation_exact(
// The refractive index takes the total pressure, as the // The refractive index takes the total pressure, as the
// recommendation specifies, not the dry air pressure. // recommendation specifies, not the dry air pressure.
// 折射率取总气压,如建议书所规定,而非干空气气压。
let vapour_pressure = density * temperature / 216.7; let vapour_pressure = density * temperature / 216.7;
indices[index] = indices[index] =
crate::p835::radio_refractive_index(pressure, vapour_pressure, temperature); crate::p835::radio_refractive_index(pressure, vapour_pressure, temperature);
@ -266,6 +327,7 @@ pub fn slant_path_attenuation_exact(
// The ray starts below the local horizon by the complement of the // The ray starts below the local horizon by the complement of the
// elevation, and is bent upward as it climbs. // elevation, and is bent upward as it climbs.
// 射线自当地地平以下、低仰角的余角处开始,并随上升而向上弯折。
let mut angle = core::f64::consts::FRAC_PI_2 - elevation; let mut angle = core::f64::consts::FRAC_PI_2 - elevation;
let mut total = 0.0; let mut total = 0.0;
@ -275,6 +337,7 @@ pub fn slant_path_attenuation_exact(
let specific = crate::gaseous::specific_attenuation(frequency_ghz, layer_atmosphere)?; let specific = crate::gaseous::specific_attenuation(frequency_ghz, layer_atmosphere)?;
// Equation 17: the path length through the layer. // Equation 17: the path length through the layer.
// 式 17:穿过该层的路径长度。
let radius = RAY_TRACE_EARTH_RADIUS_KM + heights[index]; let radius = RAY_TRACE_EARTH_RADIUS_KM + heights[index];
let delta = thicknesses[index]; let delta = thicknesses[index];
let length = -radius * angle.cos() let length = -radius * angle.cos()
@ -286,6 +349,7 @@ pub fn slant_path_attenuation_exact(
total += length * specific; total += length * specific;
// Equation 18a: the angle at which the ray leaves the layer. // Equation 18a: the angle at which the ray leaves the layer.
// 式 18a:射线离开该层的角度。
let cosine_argument = ((-length * length - 2.0 * radius * delta - delta * delta) let cosine_argument = ((-length * length - 2.0 * radius * delta - delta * delta)
/ (2.0 * length * radius + 2.0 * length * delta)) / (2.0 * length * radius + 2.0 * length * delta))
.clamp(-1.0, 1.0); .clamp(-1.0, 1.0);
@ -294,6 +358,8 @@ pub fn slant_path_attenuation_exact(
// Equation 19a: Snell's law at the boundary, using the ratio of the // Equation 19a: Snell's law at the boundary, using the ratio of the
// refractive index here to the one above. The topmost boundary has // refractive index here to the one above. The topmost boundary has
// no layer above it, so its ratio is one. // no layer above it, so its ratio is one.
// 式 19a:层界处的 Snell 定律,使用此处折射率与上方折射率之比。
// 最顶部的层界上方没有层,故其比为一。
let next = (index + 1).min(EXACT_BOUNDARY_COUNT - 1); let next = (index + 1).min(EXACT_BOUNDARY_COUNT - 1);
let ratio = if next == index { let ratio = if next == index {
1.0 1.0

View File

@ -6,11 +6,17 @@ use core::f64::consts::PI;
/// ///
/// The elevation is measured from the horizon and the zenith angle from the /// The elevation is measured from the horizon and the zenith angle from the
/// vertical, so the two add to a right angle. /// vertical, so the two add to a right angle.
///
/// 返回某天顶角对应的仰角。
///
/// 仰角自地平量起,天顶角自垂线量起,故两者相加为直角。
pub fn elevation_from_zenith(zenith_angle: f64) -> f64 { pub fn elevation_from_zenith(zenith_angle: f64) -> f64 {
PI / 2.0 - zenith_angle PI / 2.0 - zenith_angle
} }
/// Returns the zenith angle for an elevation angle. /// Returns the zenith angle for an elevation angle.
///
/// 返回某仰角对应的天顶角。
pub fn zenith_to_elevation(elevation: f64) -> f64 { pub fn zenith_to_elevation(elevation: f64) -> f64 {
PI / 2.0 - elevation PI / 2.0 - elevation
} }
@ -35,6 +41,23 @@ pub fn zenith_to_elevation(elevation: f64) -> f64 {
/// parallel geometry it models. /// parallel geometry it models.
/// ///
/// Returns infinity at or below the horizon, where no direct path exists. /// Returns infinity at or below the horizon, where no direct path exists.
///
/// 返回给定仰角下沿斜路径的大气质量。
///
/// 大气质量是穿过平坦大气的路径长度与垂直路径长度之比。对平面平行
/// 大气它是仰角的余割,
///
/// ```text
/// m = 1 / sin(elevation)
/// ```
///
/// 故在天顶处为一,在地平处无界增长。平面平行假定在约十度仰角以下
/// 失效,那里地球曲率使路径比该式更短,真实大气质量更小。
///
/// 该值在低仰角处不被截断。需要在地平附近取有限上界的调用方应自行
/// 施加;本函数报告它所建模的平面平行几何。
///
/// 在地平及其以下不存在直接路径,返回无穷。
pub fn air_mass(elevation: f64) -> f64 { pub fn air_mass(elevation: f64) -> f64 {
let sine = elevation.sin(); let sine = elevation.sin();
if sine <= 0.0 { if sine <= 0.0 {

View File

@ -21,6 +21,11 @@
/// slightly different from the polar radius used elsewhere. It is the value /// slightly different from the polar radius used elsewhere. It is the value
/// that makes the geopotential height consistent with the tabulated /// that makes the geopotential height consistent with the tabulated
/// profiles. /// profiles.
///
/// 用于势高转换的地球半径,单位为千米。
///
/// 建议书为标准大气固定了该值,它与别处所用的极半径略有不同。正是该值
/// 使势高与所列廓线一致。
const GEOPOTENTIAL_EARTH_RADIUS_KM: f64 = 6_356.766; const GEOPOTENTIAL_EARTH_RADIUS_KM: f64 = 6_356.766;
/// Returns the geopotential height for a geometric height, in kilometers. /// Returns the geopotential height for a geometric height, in kilometers.
@ -34,6 +39,17 @@ const GEOPOTENTIAL_EARTH_RADIUS_KM: f64 = 6_356.766;
/// with `R` the fixed Earth radius of the standard atmosphere and `H` the /// with `R` the fixed Earth radius of the standard atmosphere and `H` the
/// geometric height. The geopotential height is always the smaller of the /// geometric height. The geopotential height is always the smaller of the
/// two. /// two.
///
/// 返回某几何高度对应的势高,单位为千米。
///
/// 关系为
///
/// ```text
/// h_p = R H / (R + H)
/// ```
///
/// 其中 `R` 是标准大气固定的地球半径,`H` 是几何高度。势高总是两者中较
/// 小者。
pub fn geopotential_height(geometric_height_km: f64) -> f64 { pub fn geopotential_height(geometric_height_km: f64) -> f64 {
GEOPOTENTIAL_EARTH_RADIUS_KM * geometric_height_km GEOPOTENTIAL_EARTH_RADIUS_KM * geometric_height_km
/ (GEOPOTENTIAL_EARTH_RADIUS_KM + geometric_height_km) / (GEOPOTENTIAL_EARTH_RADIUS_KM + geometric_height_km)
@ -139,6 +155,21 @@ pub fn standard_water_vapour_density(geometric_height_km: f64, surface_density:
/// ///
/// Source: ITU-R Recommendation P.453, which gives this expression for the /// Source: ITU-R Recommendation P.453, which gives this expression for the
/// refractive index of moist air. /// refractive index of moist air.
///
/// 返回湿空气的无线电折射率,无量纲。
///
/// 关系为
///
/// ```text
/// N = 77.6 P_d / T + 72 e / T + 3.75e5 e / T^2
/// n = 1 + N 1e-6
/// ```
///
/// 其中 `P_d` 是干空气气压,`e` 是水汽分压,两者均以百帕计;`T` 是
/// 温度,以开尔文计。前两项是非色散折射率,第三项是使水汽产生强烈效应
/// 的偶极项。
///
/// 出处:ITU-R 建议书 P.453,它给出湿空气折射率的这一表达式。
pub fn radio_refractive_index( pub fn radio_refractive_index(
dry_pressure_hpa: f64, dry_pressure_hpa: f64,
vapour_pressure_hpa: f64, vapour_pressure_hpa: f64,

View File

@ -16,12 +16,18 @@
use crate::p839_table::ZERO_ISOTHERM_HEIGHT_METRES; use crate::p839_table::ZERO_ISOTHERM_HEIGHT_METRES;
/// The grid resolution of the tabulated isotherm height, in degrees. /// The grid resolution of the tabulated isotherm height, in degrees.
///
/// 列表等温线高度的网格分辨率,单位为度。
const STEP_DEGREES: f64 = 1.5; const STEP_DEGREES: f64 = 1.5;
/// The number of rows in the table, running from north to south. /// The number of rows in the table, running from north to south.
///
/// 表中的行数,自北向南排列。
const ROWS: usize = 121; const ROWS: usize = 121;
/// The number of columns in the table, running from east to east. /// The number of columns in the table, running from east to east.
///
/// 表中的列数,自东向东排列。
const COLUMNS: usize = 241; const COLUMNS: usize = 241;
/// The offset added to the isotherm height to obtain the rain height, in /// The offset added to the isotherm height to obtain the rain height, in
@ -30,6 +36,11 @@ const COLUMNS: usize = 241;
/// The recommendation states that the rain height is the isotherm height /// The recommendation states that the rain height is the isotherm height
/// plus 0.36 km, which represents the depth of the melting layer below the /// plus 0.36 km, which represents the depth of the melting layer below the
/// isotherm. /// isotherm.
///
/// 加到等温线高度上以获得雨顶高度的偏移,单位为千米。
///
/// 建议书指出雨顶高度是等温线高度加 0.36 km,它代表等温线以下融化层的
/// 深度。
pub const RAIN_HEIGHT_OFFSET_KM: f64 = 0.36; pub const RAIN_HEIGHT_OFFSET_KM: f64 = 0.36;
/// Returns the height of the zero degree isotherm at a location, in /// Returns the height of the zero degree isotherm at a location, in
@ -45,6 +56,15 @@ pub const RAIN_HEIGHT_OFFSET_KM: f64 = 0.36;
/// ///
/// Source: ITU-R Recommendation P.839-4, equation 1 and the tabulated /// Source: ITU-R Recommendation P.839-4, equation 1 and the tabulated
/// isotherm height. /// isotherm height.
///
/// 返回某位置零度等温线的高度,以平均海平面以上千米计。
///
/// 纬度以度计、北正;经度以度计、东正。该值由列表网格双线性插值得到,
/// 经度先归约到 `[0, 360)`,故任何约定都被接受。
///
/// 任一坐标非有限,或纬度超出网格范围时返回 `None`。
///
/// 出处:ITU-R 建议书 P.839-4,式 1 及所列等温线高度。
pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option<f64> { pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option<f64> {
if !latitude.is_finite() || !longitude.is_finite() { if !latitude.is_finite() || !longitude.is_finite() {
return None; return None;
@ -55,12 +75,14 @@ pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option<f64> {
// The rows run from north to south, so the row index grows as the // The rows run from north to south, so the row index grows as the
// latitude falls. // latitude falls.
// 各行自北向南排列,故行下标随纬度下降而增大。
let row_position = (90.0 - latitude) / STEP_DEGREES; let row_position = (90.0 - latitude) / STEP_DEGREES;
let row_lower = (row_position.floor() as usize).min(ROWS - 1); let row_lower = (row_position.floor() as usize).min(ROWS - 1);
let row_upper = (row_lower + 1).min(ROWS - 1); let row_upper = (row_lower + 1).min(ROWS - 1);
let row_fraction = row_position - row_lower as f64; let row_fraction = row_position - row_lower as f64;
// The columns run from 0 to 360 degrees east, wrapping at the seam. // The columns run from 0 to 360 degrees east, wrapping at the seam.
// 各列自东经 0 度到 360 度排列,在接缝处环绕。
let longitude = longitude.rem_euclid(360.0); let longitude = longitude.rem_euclid(360.0);
let column_position = longitude / STEP_DEGREES; let column_position = longitude / STEP_DEGREES;
let column_lower = (column_position.floor() as usize).min(COLUMNS - 1); let column_lower = (column_position.floor() as usize).min(COLUMNS - 1);
@ -71,6 +93,7 @@ pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option<f64> {
|row: usize, column: usize| f64::from(ZERO_ISOTHERM_HEIGHT_METRES[row][column]) / 1000.0; |row: usize, column: usize| f64::from(ZERO_ISOTHERM_HEIGHT_METRES[row][column]) / 1000.0;
// Bilinear interpolation. // Bilinear interpolation.
// 双线性插值。
let top = sample(row_lower, column_lower) let top = sample(row_lower, column_lower)
+ (sample(row_lower, column_upper) - sample(row_lower, column_lower)) * column_fraction; + (sample(row_lower, column_upper) - sample(row_lower, column_lower)) * column_fraction;
let bottom = sample(row_upper, column_lower) let bottom = sample(row_upper, column_lower)
@ -84,11 +107,19 @@ pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option<f64> {
/// [`RAIN_HEIGHT_OFFSET_KM`], as the recommendation defines it. /// [`RAIN_HEIGHT_OFFSET_KM`], as the recommendation defines it.
/// ///
/// Returns `None` when the coordinates are not usable. /// Returns `None` when the coordinates are not usable.
///
/// 返回某位置的雨顶高度,以平均海平面以上千米计。
///
/// 它是零度等温线高度加 [`RAIN_HEIGHT_OFFSET_KM`],如建议书所定义。
///
/// 坐标不可用时返回 `None`。
pub fn rain_height(latitude: f64, longitude: f64) -> Option<f64> { pub fn rain_height(latitude: f64, longitude: f64) -> Option<f64> {
Some(zero_isotherm_height(latitude, longitude)? + RAIN_HEIGHT_OFFSET_KM) Some(zero_isotherm_height(latitude, longitude)? + RAIN_HEIGHT_OFFSET_KM)
} }
/// Returns the grid resolution of the tabulated isotherm height, in degrees. /// Returns the grid resolution of the tabulated isotherm height, in degrees.
///
/// 返回列表等温线高度的网格分辨率,单位为度。
pub const fn grid_step_degrees() -> f64 { pub const fn grid_step_degrees() -> f64 {
STEP_DEGREES STEP_DEGREES
} }

View File

@ -17,6 +17,15 @@
/// ///
/// Source: ITU-R Recommendation P.839-4, *Rain height model for prediction /// Source: ITU-R Recommendation P.839-4, *Rain height model for prediction
/// methods*, which tabulates the isotherm height on this grid. /// methods*, which tabulates the isotherm height on this grid.
///
/// 零度等温线的高度,单位为米。
///
/// 网格在行方向上自北纬 90 度到南纬 90 度,在列方向上自东经 0 度到
/// 360 度,步长均为 1.5 度,故有 121 行 241 列。分数纬度或经度处的值
/// 由周围四点之间的双线性插值求得。
///
/// 出处:ITU-R 建议书 P.839-4,*Rain height model for prediction methods*,
/// 它在该网格上列出等温线高度。
pub static ZERO_ISOTHERM_HEIGHT_METRES: [[u16; 241]; 121] = [ pub static ZERO_ISOTHERM_HEIGHT_METRES: [[u16; 241]; 121] = [
[ [
2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096,

View File

@ -29,6 +29,10 @@ use apsis_rf::Decibel;
/// ///
/// The value accounts for the refraction of the ray in the lower atmosphere, /// The value accounts for the refraction of the ray in the lower atmosphere,
/// which makes the Earth appear larger than it is. /// which makes the Earth appear larger than it is.
///
/// 路径几何所用的地球有效半径,单位为千米。
///
/// 该值考虑了射线在低层大气中的折射,折射使地球显得比实际更大。
const EFFECTIVE_EARTH_RADIUS_KM: f64 = 8_500.0; const EFFECTIVE_EARTH_RADIUS_KM: f64 = 8_500.0;
/// A mid latitude rain height, in kilometers above sea level. /// A mid latitude rain height, in kilometers above sea level.
@ -37,20 +41,36 @@ const EFFECTIVE_EARTH_RADIUS_KM: f64 = 8_500.0;
/// height of Recommendation P.839, which is tabulated as a function of /// height of Recommendation P.839, which is tabulated as a function of
/// latitude and longitude. Prefer [`rain_attenuation_at`], which looks the /// latitude and longitude. Prefer [`rain_attenuation_at`], which looks the
/// value up, over [`rain_attenuation`] with this constant. /// value up, over [`rain_attenuation`] with this constant.
///
/// 中纬度雨顶高度,以海拔千米计。
///
/// 建议书 P.618 从建议书 P.839 的零度等温线高度取雨顶高度,后者按纬度
/// 与经度列表给出。相较用本常量调用 [`rain_attenuation`],更宜用会查表
/// 的 [`rain_attenuation_at`]。
pub const DEFAULT_RAIN_HEIGHT_KM: f64 = 3.0; pub const DEFAULT_RAIN_HEIGHT_KM: f64 = 3.0;
/// One term of a P.838-3 curve fit. /// One term of a P.838-3 curve fit.
///
/// P.838-3 曲线拟合的一项。
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct CurveTerm { struct CurveTerm {
/// The amplitude coefficient. /// The amplitude coefficient.
///
/// 幅度系数。
amplitude: f64, amplitude: f64,
/// The centre of the term, on a base ten logarithmic frequency axis. /// The centre of the term, on a base ten logarithmic frequency axis.
///
/// 该项的中心,在十进对数频率轴上。
centre: f64, centre: f64,
/// The width of the term, on a base ten logarithmic frequency axis. /// The width of the term, on a base ten logarithmic frequency axis.
///
/// 该项的宽度,在十进对数频率轴上。
width: f64, width: f64,
} }
/// The coefficients of the `k` curve fit for horizontal polarisation. /// The coefficients of the `k` curve fit for horizontal polarisation.
///
/// 水平极化 `k` 曲线拟合的系数。
const K_HORIZONTAL_TERMS: &[CurveTerm] = &[ const K_HORIZONTAL_TERMS: &[CurveTerm] = &[
CurveTerm { CurveTerm {
amplitude: -5.339_80, amplitude: -5.339_80,
@ -75,6 +95,8 @@ const K_HORIZONTAL_TERMS: &[CurveTerm] = &[
]; ];
/// The coefficients of the `k` curve fit for vertical polarisation. /// The coefficients of the `k` curve fit for vertical polarisation.
///
/// 垂直极化 `k` 曲线拟合的系数。
const K_VERTICAL_TERMS: &[CurveTerm] = &[ const K_VERTICAL_TERMS: &[CurveTerm] = &[
CurveTerm { CurveTerm {
amplitude: -3.805_95, amplitude: -3.805_95,
@ -99,6 +121,8 @@ const K_VERTICAL_TERMS: &[CurveTerm] = &[
]; ];
/// The coefficients of the `alpha` curve fit for horizontal polarisation. /// The coefficients of the `alpha` curve fit for horizontal polarisation.
///
/// 水平极化 `alpha` 曲线拟合的系数。
const ALPHA_HORIZONTAL_TERMS: &[CurveTerm] = &[ const ALPHA_HORIZONTAL_TERMS: &[CurveTerm] = &[
CurveTerm { CurveTerm {
amplitude: -0.143_18, amplitude: -0.143_18,
@ -128,6 +152,8 @@ const ALPHA_HORIZONTAL_TERMS: &[CurveTerm] = &[
]; ];
/// The coefficients of the `alpha` curve fit for vertical polarisation. /// The coefficients of the `alpha` curve fit for vertical polarisation.
///
/// 垂直极化 `alpha` 曲线拟合的系数。
const ALPHA_VERTICAL_TERMS: &[CurveTerm] = &[ const ALPHA_VERTICAL_TERMS: &[CurveTerm] = &[
CurveTerm { CurveTerm {
amplitude: -0.077_71, amplitude: -0.077_71,
@ -157,32 +183,58 @@ const ALPHA_VERTICAL_TERMS: &[CurveTerm] = &[
]; ];
/// The linear coefficients of the `k` curve fits. /// The linear coefficients of the `k` curve fits.
///
/// `k` 曲线拟合的线性系数。
const K_SLOPE_HORIZONTAL: f64 = -0.189_61; const K_SLOPE_HORIZONTAL: f64 = -0.189_61;
/// The constant of the horizontal `k` curve fit. /// The constant of the horizontal `k` curve fit.
///
/// 水平 `k` 曲线拟合的常数。
const K_OFFSET_HORIZONTAL: f64 = 0.711_47; const K_OFFSET_HORIZONTAL: f64 = 0.711_47;
/// The linear coefficient of the vertical `k` curve fit. /// The linear coefficient of the vertical `k` curve fit.
///
/// 垂直 `k` 曲线拟合的线性系数。
const K_SLOPE_VERTICAL: f64 = -0.163_98; const K_SLOPE_VERTICAL: f64 = -0.163_98;
/// The constant of the vertical `k` curve fit. /// The constant of the vertical `k` curve fit.
///
/// 垂直 `k` 曲线拟合的常数。
const K_OFFSET_VERTICAL: f64 = 0.632_97; const K_OFFSET_VERTICAL: f64 = 0.632_97;
/// The linear coefficient of the horizontal `alpha` curve fit. /// The linear coefficient of the horizontal `alpha` curve fit.
///
/// 水平 `alpha` 曲线拟合的线性系数。
const ALPHA_SLOPE_HORIZONTAL: f64 = 0.678_49; const ALPHA_SLOPE_HORIZONTAL: f64 = 0.678_49;
/// The constant of the horizontal `alpha` curve fit. /// The constant of the horizontal `alpha` curve fit.
///
/// 水平 `alpha` 曲线拟合的常数。
const ALPHA_OFFSET_HORIZONTAL: f64 = -1.955_37; const ALPHA_OFFSET_HORIZONTAL: f64 = -1.955_37;
/// The linear coefficient of the vertical `alpha` curve fit. /// The linear coefficient of the vertical `alpha` curve fit.
///
/// 垂直 `alpha` 曲线拟合的线性系数。
const ALPHA_SLOPE_VERTICAL: f64 = -0.053_739; const ALPHA_SLOPE_VERTICAL: f64 = -0.053_739;
/// The constant of the vertical `alpha` curve fit. /// The constant of the vertical `alpha` curve fit.
///
/// 垂直 `alpha` 曲线拟合的常数。
const ALPHA_OFFSET_VERTICAL: f64 = 0.834_33; const ALPHA_OFFSET_VERTICAL: f64 = 0.834_33;
/// A rain attenuation result at a given elevation. /// A rain attenuation result at a given elevation.
///
/// 给定仰角下的降雨衰减结果。
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct RainAttenuation { pub struct RainAttenuation {
/// The specific attenuation, in decibels per kilometer. /// The specific attenuation, in decibels per kilometer.
///
/// 比衰减,单位为分贝每千米。
pub specific_db_per_km: f64, pub specific_db_per_km: f64,
/// The effective path length through the rain, in kilometers. /// The effective path length through the rain, in kilometers.
///
/// 穿过降雨的有效路径长度,单位为千米。
pub path_length_km: f64, pub path_length_km: f64,
/// The total attenuation along the path, in decibels. /// The total attenuation along the path, in decibels.
///
/// 沿路径的总衰减,单位为分贝。
pub attenuation_db: f64, pub attenuation_db: f64,
/// The horizontal reduction factor, dimensionless and at most one. /// The horizontal reduction factor, dimensionless and at most one.
///
/// 水平缩减因子,无量纲且至多为一。
pub horizontal_reduction: f64, pub horizontal_reduction: f64,
/// The vertical adjustment factor, dimensionless. /// The vertical adjustment factor, dimensionless.
/// ///
@ -190,16 +242,26 @@ pub struct RainAttenuation {
/// of the two geometric path estimates. It is at most one when the path /// of the two geometric path estimates. It is at most one when the path
/// meets the rain layer at a steep angle and can exceed one at a shallow /// meets the rain layer at a steep angle and can exceed one at a shallow
/// angle, which is the behaviour Recommendation P.618 gives. /// angle, which is the behaviour Recommendation P.618 gives.
///
/// 垂直调整因子,无量纲。
///
/// 该因子是有效路径长度与两个几何路径估计中较长者之比。当路径以陡
/// 角与雨层相交时至多为一,以浅角相交时可超过一,这正是建议书
/// P.618 所给出的行为。
pub vertical_adjustment: f64, pub vertical_adjustment: f64,
} }
impl RainAttenuation { impl RainAttenuation {
/// Returns the total attenuation. /// Returns the total attenuation.
///
/// 返回总衰减。
pub fn attenuation(self) -> Decibel { pub fn attenuation(self) -> Decibel {
Decibel::from_db(self.attenuation_db) Decibel::from_db(self.attenuation_db)
} }
/// Returns `true` when every value is finite and not negative. /// Returns `true` when every value is finite and not negative.
///
/// 当每个值有限且非负时返回 `true`。
pub fn is_valid(self) -> bool { pub fn is_valid(self) -> bool {
self.specific_db_per_km.is_finite() self.specific_db_per_km.is_finite()
&& self.specific_db_per_km >= 0.0 && self.specific_db_per_km >= 0.0
@ -214,12 +276,18 @@ impl RainAttenuation {
/// ///
/// The term is a Gaussian on a base ten logarithmic frequency axis, so the /// The term is a Gaussian on a base ten logarithmic frequency axis, so the
/// frequency must be in gigahertz and positive. /// frequency must be in gigahertz and positive.
///
/// 求一个 P.838-3 曲线项。
///
/// 该项是十进对数频率轴上的高斯,故频率必须以吉赫兹计且为正。
fn curve_term(term: CurveTerm, frequency_ghz: f64) -> f64 { fn curve_term(term: CurveTerm, frequency_ghz: f64) -> f64 {
let logarithm = frequency_ghz.log10(); let logarithm = frequency_ghz.log10();
term.amplitude * (-((logarithm - term.centre) / term.width).powi(2)).exp() term.amplitude * (-((logarithm - term.centre) / term.width).powi(2)).exp()
} }
/// Returns the `k` for a polarisation, before the elevation correction. /// Returns the `k` for a polarisation, before the elevation correction.
///
/// 返回某极化的 `k`,在仰角修正之前。
fn base_k(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64) -> f64 { fn base_k(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64) -> f64 {
let gaussian: f64 = terms let gaussian: f64 = terms
.iter() .iter()
@ -229,6 +297,8 @@ fn base_k(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64) -> f
} }
/// Returns the `alpha` for a polarisation, before the elevation correction. /// Returns the `alpha` for a polarisation, before the elevation correction.
///
/// 返回某极化的 `alpha`,在仰角修正之前。
fn base_alpha(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64) -> f64 { fn base_alpha(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64) -> f64 {
let gaussian: f64 = terms let gaussian: f64 = terms
.iter() .iter()
@ -252,6 +322,19 @@ fn base_alpha(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64)
/// 1000 GHz. /// 1000 GHz.
/// ///
/// Source: ITU-R Recommendation P.838-3, equations 2 and 3. /// Source: ITU-R Recommendation P.838-3, equations 2 and 3.
///
/// 返回比衰减幂律的 P.838-3 系数。
///
/// 极化是电场与水平方向之间以弧度计的角度,故零为水平、直角为垂直。
/// 仰角以弧度计。
///
/// 结果是关系 `gamma = k R^alpha` 中系数 `k` 与指数 `alpha` 的一对值,
/// 其中降雨率以毫米每小时计,结果以分贝每千米计。
///
/// 频率、极化或仰角不可用时返回 `None`。该建议书针对 1 GHz 至 1000 GHz
/// 的频率陈述。
///
/// 出处:ITU-R 建议书 P.838-3,式 2 与式 3。
pub fn rain_coefficients(frequency: f64, polarisation: f64, elevation: f64) -> Option<(f64, f64)> { pub fn rain_coefficients(frequency: f64, polarisation: f64, elevation: f64) -> Option<(f64, f64)> {
let frequency_ghz = frequency / 1.0e9; let frequency_ghz = frequency / 1.0e9;
if !frequency_ghz.is_finite() || !(1.0..=1_000.0).contains(&frequency_ghz) { if !frequency_ghz.is_finite() || !(1.0..=1_000.0).contains(&frequency_ghz) {
@ -290,6 +373,8 @@ pub fn rain_coefficients(frequency: f64, polarisation: f64, elevation: f64) -> O
// of the elevation angle and the cosine of twice the polarisation tilt. // of the elevation angle and the cosine of twice the polarisation tilt.
// The elevation term vanishes at the zenith, where the distinction // The elevation term vanishes at the zenith, where the distinction
// between horizontal and vertical polarisation disappears. // between horizontal and vertical polarisation disappears.
// 仰角与极化通过仰角余弦的平方以及二倍极化倾角的余弦进入。仰角项在
// 天顶处消失,那里水平与垂直极化之别不复存在。
let geometry_factor = elevation.cos().powi(2) * (2.0 * polarisation).cos(); let geometry_factor = elevation.cos().powi(2) * (2.0 * polarisation).cos();
let k = 0.5 * (k_horizontal + k_vertical + (k_horizontal - k_vertical) * geometry_factor); let k = 0.5 * (k_horizontal + k_vertical + (k_horizontal - k_vertical) * geometry_factor);
let alpha = (k_horizontal * alpha_horizontal let alpha = (k_horizontal * alpha_horizontal
@ -305,6 +390,12 @@ pub fn rain_coefficients(frequency: f64, polarisation: f64, elevation: f64) -> O
/// ///
/// Returns `None` when the rain rate is negative, or when the coefficients /// Returns `None` when the rain rate is negative, or when the coefficients
/// are not available. /// are not available.
///
/// 返回雨中的比衰减,单位为分贝每千米。
///
/// 降雨率以毫米每小时计。
///
/// 降雨率为负,或系数不可用时返回 `None`。
pub fn specific_rain_attenuation( pub fn specific_rain_attenuation(
rain_rate: f64, rain_rate: f64,
frequency: f64, frequency: f64,
@ -338,6 +429,22 @@ pub fn specific_rain_attenuation(
/// below the horizon. /// below the horizon.
/// ///
/// Source: ITU-R Recommendation P.618-13, equations 1 through 10. /// Source: ITU-R Recommendation P.618-13, equations 1 through 10.
///
/// 返回沿斜路径的总降雨衰减。
///
/// 降雨率是在所关注时间百分比上被超过的值,以毫米每小时计。站的海拔
/// 高度与雨层高度以千米计;没有本地值时,后者传
/// [`DEFAULT_RAIN_HEIGHT_KM`]。
///
/// 方法即建议书 P.618 所述。斜路径长度由雨顶高度、站高与仰角得出,
/// 并作地球曲率修正。随后两个缩减因子计入降雨的不均匀性:一个水平
/// 因子,取决于比衰减、频率以及路径的水平投影;一个垂直因子,通过
/// 路径穿越雨层的角度取决于纬度。
///
/// 降雨率、频率或仰角不可用,站高于雨层,或仰角在地平及其以下时返回
/// `None`。
///
/// 出处:ITU-R 建议书 P.618-13,式 1 至式 10。
pub fn rain_attenuation( pub fn rain_attenuation(
rain_rate: f64, rain_rate: f64,
frequency: f64, frequency: f64,
@ -362,9 +469,11 @@ pub fn rain_attenuation(
let specific = specific_rain_attenuation(rain_rate, frequency, polarisation, elevation)?; let specific = specific_rain_attenuation(rain_rate, frequency, polarisation, elevation)?;
// Equation 2: the slant path length, with the Earth curvature term. // Equation 2: the slant path length, with the Earth curvature term.
// 式 2:斜路径长度,含地球曲率项。
let height_difference = rain_height_km - station_height_km; let height_difference = rain_height_km - station_height_km;
if height_difference <= 0.0 { if height_difference <= 0.0 {
// The station is at or above the rain layer, so the path is empty. // The station is at or above the rain layer, so the path is empty.
// 站在雨层处或其上,故路径为空。
return Some(RainAttenuation { return Some(RainAttenuation {
specific_db_per_km: specific, specific_db_per_km: specific,
path_length_km: 0.0, path_length_km: 0.0,
@ -378,18 +487,22 @@ pub fn rain_attenuation(
/ ((sine * sine + 2.0 * height_difference / EFFECTIVE_EARTH_RADIUS_KM).sqrt() + sine); / ((sine * sine + 2.0 * height_difference / EFFECTIVE_EARTH_RADIUS_KM).sqrt() + sine);
// Equation 3: the horizontal projection of the slant path. // Equation 3: the horizontal projection of the slant path.
// 式 3:斜路径的水平投影。
let horizontal_length = (slant_length * elevation.cos()).abs(); let horizontal_length = (slant_length * elevation.cos()).abs();
// Equation 6: the horizontal reduction factor. // Equation 6: the horizontal reduction factor.
// 式 6:水平缩减因子。
let frequency_ghz = frequency / 1.0e9; let frequency_ghz = frequency / 1.0e9;
let horizontal_reduction = 1.0 let horizontal_reduction = 1.0
/ (1.0 + 0.78 * (horizontal_length * specific / frequency_ghz).sqrt() / (1.0 + 0.78 * (horizontal_length * specific / frequency_ghz).sqrt()
- 0.38 * (1.0 - (-2.0 * horizontal_length).exp())); - 0.38 * (1.0 - (-2.0 * horizontal_length).exp()));
// Equation 7: the angle at which the path crosses the rain layer. // Equation 7: the angle at which the path crosses the rain layer.
// 式 7:路径穿越雨层的角度。
let eta = (height_difference / (horizontal_length * horizontal_reduction)).atan(); let eta = (height_difference / (horizontal_length * horizontal_reduction)).atan();
// Equation 8: the longer of the two path estimates. // Equation 8: the longer of the two path estimates.
// 式 8:两个路径估计中较长者。
let path_length = if eta > elevation { let path_length = if eta > elevation {
horizontal_length * horizontal_reduction / elevation.cos() horizontal_length * horizontal_reduction / elevation.cos()
} else { } else {
@ -397,10 +510,13 @@ pub fn rain_attenuation(
}; };
// Equation 9: the vertical adjustment factor. // Equation 9: the vertical adjustment factor.
// 式 9:垂直调整因子。
// //
// The exponential takes the elevation in degrees, not radians, because // The exponential takes the elevation in degrees, not radians, because
// the coefficients of 31 and 0.45 were fitted with degrees. The sine // the coefficients of 31 and 0.45 were fitted with degrees. The sine
// takes the elevation in radians, as usual. // takes the elevation in radians, as usual.
// 指数以度而非弧度接收仰角,因为 31 与 0.45 两个系数是以度拟合的。
// 正弦照常以弧度接收仰角。
let latitude_magnitude = latitude.abs(); let latitude_magnitude = latitude.abs();
let xi = if latitude_magnitude < 36.0 { let xi = if latitude_magnitude < 36.0 {
36.0 - latitude_magnitude 36.0 - latitude_magnitude
@ -418,6 +534,7 @@ pub fn rain_attenuation(
- 0.45)); - 0.45));
// Equation 10: the effective path length and the attenuation. // Equation 10: the effective path length and the attenuation.
// 式 10:有效路径长度与衰减。
let effective_length = path_length * vertical_adjustment; let effective_length = path_length * vertical_adjustment;
Some(RainAttenuation { Some(RainAttenuation {
specific_db_per_km: specific, specific_db_per_km: specific,
@ -432,6 +549,10 @@ pub fn rain_attenuation(
/// horizontal. /// horizontal.
/// ///
/// This is a convenience for the two cases that are quoted by name. /// This is a convenience for the two cases that are quoted by name.
///
/// 返回以相对水平倾角描述的极化所对应的弧度角。
///
/// 这是为两种常被具名引用的情形提供的便利。
pub const fn polarisation_tilt(degrees_from_horizontal: f64) -> f64 { pub const fn polarisation_tilt(degrees_from_horizontal: f64) -> f64 {
degrees_from_horizontal * core::f64::consts::PI / 180.0 degrees_from_horizontal * core::f64::consts::PI / 180.0
} }
@ -451,6 +572,19 @@ pub const fn polarisation_tilt(degrees_from_horizontal: f64) -> f64 {
/// ///
/// Source: the rain height is from ITU-R Recommendation P.839-4 and the path /// Source: the rain height is from ITU-R Recommendation P.839-4 and the path
/// method from ITU-R Recommendation P.618-13. /// method from ITU-R Recommendation P.618-13.
///
/// 返回沿斜路径的总降雨衰减,雨顶高度取自建议书 P.839 模型。
///
/// 这与 [`rain_attenuation`] 是同一计算,只是雨层高度由给定纬经度处列表
/// 给出的零度等温线高度查得,而非由调用方提供。除非有本地实测的等温线
/// 高度,否则宜用此形式。
///
/// 站高必须给出;海拔为零的站传零。
///
/// 坐标不可用,或其他任何参数被 [`rain_attenuation`] 拒绝时返回 `None`。
///
/// 出处:雨顶高度来自 ITU-R 建议书 P.839-4,路径方法来自 ITU-R 建议书
/// P.618-13。
pub fn rain_attenuation_at( pub fn rain_attenuation_at(
rain_rate: f64, rain_rate: f64,
frequency: f64, frequency: f64,