diff --git a/crates/rf/apsis-atmosphere/src/gaseous.rs b/crates/rf/apsis-atmosphere/src/gaseous.rs index d4ad38b..0cdafe6 100644 --- a/crates/rf/apsis-atmosphere/src/gaseous.rs +++ b/crates/rf/apsis-atmosphere/src/gaseous.rs @@ -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 /// first field is the line frequency in gigahertz and the rest are the /// dimensionless coefficients of the line. +/// +/// 一条氧吸收谱线。 +/// +/// 各系数即 ITU-R P.676-12 表 1 所列。第一个字段是线频率,以吉赫兹计; +/// 其余为谱线的无量纲系数。 #[derive(Debug, Clone, Copy)] struct OxygenLine { /// The line frequency, in gigahertz. + /// + /// 线频率,以吉赫兹计。 + /// + /// 线频率,以吉赫兹计。 f0: f64, /// Line strength coefficient. + /// + /// 谱线强度系数。 + /// + /// 谱线强度系数。 a1: f64, /// Temperature dependence of the line strength. + /// + /// 谱线强度的温度依赖。 + /// + /// 谱线强度的温度依赖。 a2: f64, /// Line width coefficient. + /// + /// 谱线宽度系数。 + /// + /// 谱线宽度系数。 a3: f64, /// Pressure dependence of the line width. + /// + /// 谱线宽度的气压依赖。 + /// + /// 谱线宽度的气压依赖。 a4: f64, /// Pressure shift coefficient. + /// + /// 气压频移系数。 a5: f64, /// Mixing ratio dependence of the pressure shift. + /// + /// 气压频移的混合比依赖。 a6: f64, } /// One water vapour absorption line. /// /// 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)] struct WaterLine { /// The line frequency, in gigahertz. + /// + /// 线频率,以吉赫兹计。 f0: f64, /// Line strength coefficient. + /// + /// 谱线强度系数。 b1: f64, /// Temperature dependence of the line strength. + /// + /// 谱线强度的温度依赖。 b2: f64, /// Line width coefficient. + /// + /// 谱线宽度系数。 b3: f64, /// Pressure dependence of the line width. + /// + /// 谱线宽度的气压依赖。 b4: f64, /// Water vapour dependence of the line width. + /// + /// 谱线宽度的水汽依赖。 b5: f64, /// Temperature dependence of the water vapour term. + /// + /// 水汽项的温度依赖。 b6: f64, } @@ -72,6 +119,11 @@ struct WaterLine { /// 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 /// submillimetre region. +/// +/// ITU-R P.676-12 表 1 的氧吸收谱线。 +/// +/// 前三十七项是 60 GHz 复合体的精细结构,其后是 118.75 GHz 处的孤立线 +/// 以及亚毫米波段的六条线。 const OXYGEN_LINES: &[OxygenLine] = &[ OxygenLine { 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. +/// +/// ITU-R P.676-12 表 2 的水汽吸收谱线。 const WATER_LINES: &[WaterLine] = &[ WaterLine { f0: 22.235_080, @@ -795,13 +849,24 @@ const WATER_LINES: &[WaterLine] = &[ /// The pressure is the dry air pressure, as the recommendation specifies. The /// water vapour density is separate because the partial pressure of water /// vapour is derived from it. +/// +/// 气体衰减所依赖的大气状态。 +/// +/// 气压是干空气气压,如建议书所规定。水汽密度单列,因为水汽分压由它 +/// 导出。 #[derive(Debug, Clone, Copy, PartialEq)] pub struct Atmosphere { /// The dry air pressure, in hectopascals. + /// + /// 干空气气压,以百帕计。 pub pressure_hpa: f64, /// The water vapour density, in grams per cubic meter. + /// + /// 水汽密度,以克每立方米计。 pub water_vapour_density: f64, /// The temperature, in kelvin. + /// + /// 温度,以开尔文计。 pub temperature_k: f64, } @@ -811,6 +876,11 @@ impl Atmosphere { /// 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 /// 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 { pressure_hpa: 1_013.25, water_vapour_density: 7.5, @@ -818,6 +888,8 @@ impl Atmosphere { }; /// Creates an atmosphere from its three parameters. + /// + /// 由其三个参数创建大气。 pub const fn new(pressure_hpa: f64, water_vapour_density: f64, temperature_k: f64) -> Self { Self { pressure_hpa, @@ -830,11 +902,18 @@ impl Atmosphere { /// /// The relation is `e = rho T / 216.7`, which follows from the ideal gas /// law with the molar masses of water and dry air. + /// + /// 返回水汽分压,以百帕计。 + /// + /// 关系为 `e = rho T / 216.7`,它由理想气体定律连同水与干空气的摩尔 + /// 质量得出。 pub fn water_vapour_partial_pressure(self) -> f64 { self.water_vapour_density * self.temperature_k / 216.7 } /// Returns `true` when the state describes a physical atmosphere. + /// + /// 当该状态描述一个物理大气时返回 `true`。 pub fn is_valid(self) -> bool { self.pressure_hpa.is_finite() && 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; for line in OXYGEN_LINES { // 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 = (width * width + 2.25e-6).sqrt(); let shift = (line.a5 + line.a6 * theta) * 1.0e-4 * (p + e) * theta.powf(0.8); // Equation 4: the line shape factor. + // 式 4:线型因子。 let shape = frequency_ghz / line.f0 * ((width - shift * (line.f0 - frequency_ghz)) / ((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)); // Equation 3: the line strength. + // 式 3:谱线强度。 let strength = line.a1 * 1.0e-7 * p * theta.powi(3) * E.powf(line.a2 * (1.0 - theta)); sum += strength * shape; } // The non-resonant Debye spectrum, equation 8. + // 非共振 Debye 谱,式 8。 let d = 5.6e-4 * (p + e) * theta.powf(0.8); let debye = frequency_ghz * p @@ -924,6 +1007,7 @@ pub fn specific_attenuation_water_vapour( for line in WATER_LINES { // Equation 7: the line width, which has a different form from the // oxygen case. + // 式 7:谱线宽度,其形式与氧的情形不同。 let width = line.b3 * 1.0e-4 * (p * theta.powf(line.b4) + line.b5 * e * theta.powf(line.b6)); let width = 0.535 * width @@ -934,6 +1018,7 @@ pub fn specific_attenuation_water_vapour( + width / ((line.f0 + frequency_ghz).powi(2) + width * width)); // 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)); sum += strength * shape; } diff --git a/crates/rf/apsis-atmosphere/src/gaseous_path.rs b/crates/rf/apsis-atmosphere/src/gaseous_path.rs index 22288e7..0bc676f 100644 --- a/crates/rf/apsis-atmosphere/src/gaseous_path.rs +++ b/crates/rf/apsis-atmosphere/src/gaseous_path.rs @@ -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. /// /// Each entry is the coefficient and the line frequency in gigahertz. +/// +/// 干空气等效高度的系数,P.676-12 表 3。 +/// +/// 每项是系数与线频率,后者以吉赫兹计。 const DRY_HEIGHT_TERMS: &[(f64, f64)] = &[ (0.159_7, 118.750_334), (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 /// width-like parameter. +/// +/// 水汽等效高度的系数,P.676-12 表 4。 +/// +/// 每项是线频率(以吉赫兹计)、一个系数,以及一个类宽度参数。 const WET_HEIGHT_TERMS: &[(f64, f64, f64)] = &[ (22.235_08, 1.52, 2.56), (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. +/// +/// 干空气与水汽的等效高度,单位为千米。 #[derive(Debug, Clone, Copy, PartialEq)] pub struct EquivalentHeights { /// The equivalent height of dry air, in kilometers. + /// + /// 干空气的等效高度,单位为千米。 pub dry_km: f64, /// The equivalent height of water vapour, in kilometers. + /// + /// 水汽的等效高度,单位为千米。 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; // 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)) * (-((frequency_ghz - 59.7) / (2.87 + 12.4 * (-7.9 * rp).exp())).powi(2)).exp(); // Equation 32, the residual contribution of the submillimetre lines. + // 式 32,亚毫米波各线的残余贡献。 let term2: f64 = DRY_HEIGHT_TERMS .iter() .map(|&(coefficient, line_frequency)| { @@ -99,6 +115,7 @@ pub fn equivalent_heights(frequency_ghz: f64, atmosphere: Atmosphere) -> Option< .sum(); // Equation 33, the far wing correction. + // 式 33,远翼修正。 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) / (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 // 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 = if frequency_ghz < 70.0 { 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. + // 式 36 至式 38 以及式 35b。 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 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 /// the elevation is at or below the horizon. +/// +/// 返回某仰角下沿斜路径的衰减,单位为分贝。 +/// +/// 天顶衰减除以仰角的正弦。其使用的平面平行近似即建议书用于近似方法 +/// 者;在约五度仰角以上是准确的。 +/// +/// 仰角以弧度计。 +/// +/// 频率或大气不可用,或仰角在地平及其以下时返回 `None`。 pub fn slant_path_attenuation( frequency_ghz: f64, elevation: f64, @@ -184,6 +212,12 @@ pub fn slant_path_attenuation( /// boundaries. The layers between them are thin near the surface and grow /// with height, so the integration resolves the dense lower atmosphere where /// the absorption is concentrated. +/// +/// 精确斜路径积分所用的层界数。 +/// +/// 建议书 P.676 以 `delta_i = 1e-4 exp(i / 100)`(`i` 从零到 921)定义 +/// 层界,共得 922 个层界。其间的层在地表附近很薄并随高度增厚,故该积分 +/// 能分辨吸收集中的稠密低层大气。 const EXACT_BOUNDARY_COUNT: usize = 922; /// 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 standard atmosphere, because this value enters the geometry of the /// ray rather than the definition of the height. +/// +/// 射线追踪所用的地球半径,单位为千米。 +/// +/// 建议书用的是平均半径,而非标准大气的地势半径,因为该值进入射线的 +/// 几何,而非高度的定义。 const RAY_TRACE_EARTH_RADIUS_KM: f64 = 6_371.0; /// 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 /// 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( frequency_ghz: f64, elevation: f64, @@ -233,6 +291,7 @@ pub fn slant_path_attenuation_exact( // Build the stack of layer boundaries. Each entry holds the geometric // height, the thickness, the profile values, and the refractive index. + // 构建层界叠层。每项保存几何高度、厚度、廓线值与折射率。 let thickness_step = (1.0_f64 / 100.0).exp(); let mut heights = [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; thicknesses[index] = 1.0e-4 * exponent.exp(); // Equation 15: the geometric height of the boundary. + // 式 15:层界的几何高度。 heights[index] = 1.0e-4 * (exponent.exp() - 1.0) / (thickness_step - 1.0); 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 // recommendation specifies, not the dry air pressure. + // 折射率取总气压,如建议书所规定,而非干空气气压。 let vapour_pressure = density * temperature / 216.7; indices[index] = 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 // elevation, and is bent upward as it climbs. + // 射线自当地地平以下、低仰角的余角处开始,并随上升而向上弯折。 let mut angle = core::f64::consts::FRAC_PI_2 - elevation; 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)?; // Equation 17: the path length through the layer. + // 式 17:穿过该层的路径长度。 let radius = RAY_TRACE_EARTH_RADIUS_KM + heights[index]; let delta = thicknesses[index]; let length = -radius * angle.cos() @@ -286,6 +349,7 @@ pub fn slant_path_attenuation_exact( total += length * specific; // Equation 18a: the angle at which the ray leaves the layer. + // 式 18a:射线离开该层的角度。 let cosine_argument = ((-length * length - 2.0 * radius * delta - delta * delta) / (2.0 * length * radius + 2.0 * length * delta)) .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 // refractive index here to the one above. The topmost boundary has // no layer above it, so its ratio is one. + // 式 19a:层界处的 Snell 定律,使用此处折射率与上方折射率之比。 + // 最顶部的层界上方没有层,故其比为一。 let next = (index + 1).min(EXACT_BOUNDARY_COUNT - 1); let ratio = if next == index { 1.0 diff --git a/crates/rf/apsis-atmosphere/src/geometry.rs b/crates/rf/apsis-atmosphere/src/geometry.rs index b04ff56..4aa3149 100644 --- a/crates/rf/apsis-atmosphere/src/geometry.rs +++ b/crates/rf/apsis-atmosphere/src/geometry.rs @@ -6,11 +6,17 @@ use core::f64::consts::PI; /// /// The elevation is measured from the horizon and the zenith angle from the /// vertical, so the two add to a right angle. +/// +/// 返回某天顶角对应的仰角。 +/// +/// 仰角自地平量起,天顶角自垂线量起,故两者相加为直角。 pub fn elevation_from_zenith(zenith_angle: f64) -> f64 { PI / 2.0 - zenith_angle } /// Returns the zenith angle for an elevation angle. +/// +/// 返回某仰角对应的天顶角。 pub fn zenith_to_elevation(elevation: f64) -> f64 { PI / 2.0 - elevation } @@ -35,6 +41,23 @@ pub fn zenith_to_elevation(elevation: f64) -> f64 { /// parallel geometry it models. /// /// Returns infinity at or below the horizon, where no direct path exists. +/// +/// 返回给定仰角下沿斜路径的大气质量。 +/// +/// 大气质量是穿过平坦大气的路径长度与垂直路径长度之比。对平面平行 +/// 大气它是仰角的余割, +/// +/// ```text +/// m = 1 / sin(elevation) +/// ``` +/// +/// 故在天顶处为一,在地平处无界增长。平面平行假定在约十度仰角以下 +/// 失效,那里地球曲率使路径比该式更短,真实大气质量更小。 +/// +/// 该值在低仰角处不被截断。需要在地平附近取有限上界的调用方应自行 +/// 施加;本函数报告它所建模的平面平行几何。 +/// +/// 在地平及其以下不存在直接路径,返回无穷。 pub fn air_mass(elevation: f64) -> f64 { let sine = elevation.sin(); if sine <= 0.0 { diff --git a/crates/rf/apsis-atmosphere/src/p835.rs b/crates/rf/apsis-atmosphere/src/p835.rs index e455250..fa9a2d5 100644 --- a/crates/rf/apsis-atmosphere/src/p835.rs +++ b/crates/rf/apsis-atmosphere/src/p835.rs @@ -21,6 +21,11 @@ /// 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. @@ -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 /// geometric height. The geopotential height is always the smaller of the /// two. +/// +/// 返回某几何高度对应的势高,单位为千米。 +/// +/// 关系为 +/// +/// ```text +/// h_p = R H / (R + H) +/// ``` +/// +/// 其中 `R` 是标准大气固定的地球半径,`H` 是几何高度。势高总是两者中较 +/// 小者。 pub fn geopotential_height(geometric_height_km: f64) -> f64 { 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 /// 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( dry_pressure_hpa: f64, vapour_pressure_hpa: f64, diff --git a/crates/rf/apsis-atmosphere/src/p839.rs b/crates/rf/apsis-atmosphere/src/p839.rs index f585015..1ce2b20 100644 --- a/crates/rf/apsis-atmosphere/src/p839.rs +++ b/crates/rf/apsis-atmosphere/src/p839.rs @@ -16,12 +16,18 @@ use crate::p839_table::ZERO_ISOTHERM_HEIGHT_METRES; /// The grid resolution of the tabulated isotherm height, in degrees. +/// +/// 列表等温线高度的网格分辨率,单位为度。 const STEP_DEGREES: f64 = 1.5; /// The number of rows in the table, running from north to south. +/// +/// 表中的行数,自北向南排列。 const ROWS: usize = 121; /// The number of columns in the table, running from east to east. +/// +/// 表中的列数,自东向东排列。 const COLUMNS: usize = 241; /// 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 /// plus 0.36 km, which represents the depth of the melting layer below the /// isotherm. +/// +/// 加到等温线高度上以获得雨顶高度的偏移,单位为千米。 +/// +/// 建议书指出雨顶高度是等温线高度加 0.36 km,它代表等温线以下融化层的 +/// 深度。 pub const RAIN_HEIGHT_OFFSET_KM: f64 = 0.36; /// 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 /// isotherm height. +/// +/// 返回某位置零度等温线的高度,以平均海平面以上千米计。 +/// +/// 纬度以度计、北正;经度以度计、东正。该值由列表网格双线性插值得到, +/// 经度先归约到 `[0, 360)`,故任何约定都被接受。 +/// +/// 任一坐标非有限,或纬度超出网格范围时返回 `None`。 +/// +/// 出处:ITU-R 建议书 P.839-4,式 1 及所列等温线高度。 pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option { if !latitude.is_finite() || !longitude.is_finite() { return None; @@ -55,12 +75,14 @@ pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option { // The rows run from north to south, so the row index grows as the // latitude falls. + // 各行自北向南排列,故行下标随纬度下降而增大。 let row_position = (90.0 - latitude) / STEP_DEGREES; let row_lower = (row_position.floor() as usize).min(ROWS - 1); let row_upper = (row_lower + 1).min(ROWS - 1); let row_fraction = row_position - row_lower as f64; // The columns run from 0 to 360 degrees east, wrapping at the seam. + // 各列自东经 0 度到 360 度排列,在接缝处环绕。 let longitude = longitude.rem_euclid(360.0); let column_position = longitude / STEP_DEGREES; 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 { |row: usize, column: usize| f64::from(ZERO_ISOTHERM_HEIGHT_METRES[row][column]) / 1000.0; // Bilinear interpolation. + // 双线性插值。 let top = sample(row_lower, column_lower) + (sample(row_lower, column_upper) - sample(row_lower, column_lower)) * column_fraction; let bottom = sample(row_upper, column_lower) @@ -84,11 +107,19 @@ pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option { /// [`RAIN_HEIGHT_OFFSET_KM`], as the recommendation defines it. /// /// Returns `None` when the coordinates are not usable. +/// +/// 返回某位置的雨顶高度,以平均海平面以上千米计。 +/// +/// 它是零度等温线高度加 [`RAIN_HEIGHT_OFFSET_KM`],如建议书所定义。 +/// +/// 坐标不可用时返回 `None`。 pub fn rain_height(latitude: f64, longitude: f64) -> Option { Some(zero_isotherm_height(latitude, longitude)? + RAIN_HEIGHT_OFFSET_KM) } /// Returns the grid resolution of the tabulated isotherm height, in degrees. +/// +/// 返回列表等温线高度的网格分辨率,单位为度。 pub const fn grid_step_degrees() -> f64 { STEP_DEGREES } diff --git a/crates/rf/apsis-atmosphere/src/p839_table.rs b/crates/rf/apsis-atmosphere/src/p839_table.rs index e5531c7..0370267 100644 --- a/crates/rf/apsis-atmosphere/src/p839_table.rs +++ b/crates/rf/apsis-atmosphere/src/p839_table.rs @@ -17,6 +17,15 @@ /// /// Source: ITU-R Recommendation P.839-4, *Rain height model for prediction /// 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] = [ [ 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, diff --git a/crates/rf/apsis-atmosphere/src/rain.rs b/crates/rf/apsis-atmosphere/src/rain.rs index 8c1e340..920ada0 100644 --- a/crates/rf/apsis-atmosphere/src/rain.rs +++ b/crates/rf/apsis-atmosphere/src/rain.rs @@ -29,6 +29,10 @@ use apsis_rf::Decibel; /// /// The value accounts for the refraction of the ray in the lower atmosphere, /// which makes the Earth appear larger than it is. +/// +/// 路径几何所用的地球有效半径,单位为千米。 +/// +/// 该值考虑了射线在低层大气中的折射,折射使地球显得比实际更大。 const EFFECTIVE_EARTH_RADIUS_KM: f64 = 8_500.0; /// 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 /// latitude and longitude. Prefer [`rain_attenuation_at`], which looks the /// 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; /// One term of a P.838-3 curve fit. +/// +/// P.838-3 曲线拟合的一项。 #[derive(Debug, Clone, Copy)] struct CurveTerm { /// The amplitude coefficient. + /// + /// 幅度系数。 amplitude: f64, /// The centre of the term, on a base ten logarithmic frequency axis. + /// + /// 该项的中心,在十进对数频率轴上。 centre: f64, /// The width of the term, on a base ten logarithmic frequency axis. + /// + /// 该项的宽度,在十进对数频率轴上。 width: f64, } /// The coefficients of the `k` curve fit for horizontal polarisation. +/// +/// 水平极化 `k` 曲线拟合的系数。 const K_HORIZONTAL_TERMS: &[CurveTerm] = &[ CurveTerm { amplitude: -5.339_80, @@ -75,6 +95,8 @@ const K_HORIZONTAL_TERMS: &[CurveTerm] = &[ ]; /// The coefficients of the `k` curve fit for vertical polarisation. +/// +/// 垂直极化 `k` 曲线拟合的系数。 const K_VERTICAL_TERMS: &[CurveTerm] = &[ CurveTerm { amplitude: -3.805_95, @@ -99,6 +121,8 @@ const K_VERTICAL_TERMS: &[CurveTerm] = &[ ]; /// The coefficients of the `alpha` curve fit for horizontal polarisation. +/// +/// 水平极化 `alpha` 曲线拟合的系数。 const ALPHA_HORIZONTAL_TERMS: &[CurveTerm] = &[ CurveTerm { amplitude: -0.143_18, @@ -128,6 +152,8 @@ const ALPHA_HORIZONTAL_TERMS: &[CurveTerm] = &[ ]; /// The coefficients of the `alpha` curve fit for vertical polarisation. +/// +/// 垂直极化 `alpha` 曲线拟合的系数。 const ALPHA_VERTICAL_TERMS: &[CurveTerm] = &[ CurveTerm { amplitude: -0.077_71, @@ -157,32 +183,58 @@ const ALPHA_VERTICAL_TERMS: &[CurveTerm] = &[ ]; /// The linear coefficients of the `k` curve fits. +/// +/// `k` 曲线拟合的线性系数。 const K_SLOPE_HORIZONTAL: f64 = -0.189_61; /// The constant of the horizontal `k` curve fit. +/// +/// 水平 `k` 曲线拟合的常数。 const K_OFFSET_HORIZONTAL: f64 = 0.711_47; /// The linear coefficient of the vertical `k` curve fit. +/// +/// 垂直 `k` 曲线拟合的线性系数。 const K_SLOPE_VERTICAL: f64 = -0.163_98; /// The constant of the vertical `k` curve fit. +/// +/// 垂直 `k` 曲线拟合的常数。 const K_OFFSET_VERTICAL: f64 = 0.632_97; /// The linear coefficient of the horizontal `alpha` curve fit. +/// +/// 水平 `alpha` 曲线拟合的线性系数。 const ALPHA_SLOPE_HORIZONTAL: f64 = 0.678_49; /// The constant of the horizontal `alpha` curve fit. +/// +/// 水平 `alpha` 曲线拟合的常数。 const ALPHA_OFFSET_HORIZONTAL: f64 = -1.955_37; /// The linear coefficient of the vertical `alpha` curve fit. +/// +/// 垂直 `alpha` 曲线拟合的线性系数。 const ALPHA_SLOPE_VERTICAL: f64 = -0.053_739; /// The constant of the vertical `alpha` curve fit. +/// +/// 垂直 `alpha` 曲线拟合的常数。 const ALPHA_OFFSET_VERTICAL: f64 = 0.834_33; /// A rain attenuation result at a given elevation. +/// +/// 给定仰角下的降雨衰减结果。 #[derive(Debug, Clone, Copy, PartialEq)] pub struct RainAttenuation { /// The specific attenuation, in decibels per kilometer. + /// + /// 比衰减,单位为分贝每千米。 pub specific_db_per_km: f64, /// The effective path length through the rain, in kilometers. + /// + /// 穿过降雨的有效路径长度,单位为千米。 pub path_length_km: f64, /// The total attenuation along the path, in decibels. + /// + /// 沿路径的总衰减,单位为分贝。 pub attenuation_db: f64, /// The horizontal reduction factor, dimensionless and at most one. + /// + /// 水平缩减因子,无量纲且至多为一。 pub horizontal_reduction: f64, /// 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 /// meets the rain layer at a steep angle and can exceed one at a shallow /// angle, which is the behaviour Recommendation P.618 gives. + /// + /// 垂直调整因子,无量纲。 + /// + /// 该因子是有效路径长度与两个几何路径估计中较长者之比。当路径以陡 + /// 角与雨层相交时至多为一,以浅角相交时可超过一,这正是建议书 + /// P.618 所给出的行为。 pub vertical_adjustment: f64, } impl RainAttenuation { /// Returns the total attenuation. + /// + /// 返回总衰减。 pub fn attenuation(self) -> Decibel { Decibel::from_db(self.attenuation_db) } /// Returns `true` when every value is finite and not negative. + /// + /// 当每个值有限且非负时返回 `true`。 pub fn is_valid(self) -> bool { self.specific_db_per_km.is_finite() && 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 /// frequency must be in gigahertz and positive. +/// +/// 求一个 P.838-3 曲线项。 +/// +/// 该项是十进对数频率轴上的高斯,故频率必须以吉赫兹计且为正。 fn curve_term(term: CurveTerm, frequency_ghz: f64) -> f64 { let logarithm = frequency_ghz.log10(); term.amplitude * (-((logarithm - term.centre) / term.width).powi(2)).exp() } /// Returns the `k` for a polarisation, before the elevation correction. +/// +/// 返回某极化的 `k`,在仰角修正之前。 fn base_k(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64) -> f64 { let gaussian: f64 = terms .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. +/// +/// 返回某极化的 `alpha`,在仰角修正之前。 fn base_alpha(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64) -> f64 { let gaussian: f64 = terms .iter() @@ -252,6 +322,19 @@ fn base_alpha(terms: &[CurveTerm], slope: f64, offset: f64, frequency_ghz: f64) /// 1000 GHz. /// /// 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)> { let frequency_ghz = frequency / 1.0e9; 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. // The elevation term vanishes at the zenith, where the distinction // between horizontal and vertical polarisation disappears. + // 仰角与极化通过仰角余弦的平方以及二倍极化倾角的余弦进入。仰角项在 + // 天顶处消失,那里水平与垂直极化之别不复存在。 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 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 /// are not available. +/// +/// 返回雨中的比衰减,单位为分贝每千米。 +/// +/// 降雨率以毫米每小时计。 +/// +/// 降雨率为负,或系数不可用时返回 `None`。 pub fn specific_rain_attenuation( rain_rate: f64, frequency: f64, @@ -338,6 +429,22 @@ pub fn specific_rain_attenuation( /// below the horizon. /// /// 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( rain_rate: f64, frequency: f64, @@ -362,9 +469,11 @@ pub fn rain_attenuation( let specific = specific_rain_attenuation(rain_rate, frequency, polarisation, elevation)?; // Equation 2: the slant path length, with the Earth curvature term. + // 式 2:斜路径长度,含地球曲率项。 let height_difference = rain_height_km - station_height_km; if height_difference <= 0.0 { // The station is at or above the rain layer, so the path is empty. + // 站在雨层处或其上,故路径为空。 return Some(RainAttenuation { specific_db_per_km: specific, 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); // Equation 3: the horizontal projection of the slant path. + // 式 3:斜路径的水平投影。 let horizontal_length = (slant_length * elevation.cos()).abs(); // Equation 6: the horizontal reduction factor. + // 式 6:水平缩减因子。 let frequency_ghz = frequency / 1.0e9; let horizontal_reduction = 1.0 / (1.0 + 0.78 * (horizontal_length * specific / frequency_ghz).sqrt() - 0.38 * (1.0 - (-2.0 * horizontal_length).exp())); // Equation 7: the angle at which the path crosses the rain layer. + // 式 7:路径穿越雨层的角度。 let eta = (height_difference / (horizontal_length * horizontal_reduction)).atan(); // Equation 8: the longer of the two path estimates. + // 式 8:两个路径估计中较长者。 let path_length = if eta > elevation { horizontal_length * horizontal_reduction / elevation.cos() } else { @@ -397,10 +510,13 @@ pub fn rain_attenuation( }; // Equation 9: the vertical adjustment factor. + // 式 9:垂直调整因子。 // // The exponential takes the elevation in degrees, not radians, because // the coefficients of 31 and 0.45 were fitted with degrees. The sine // takes the elevation in radians, as usual. + // 指数以度而非弧度接收仰角,因为 31 与 0.45 两个系数是以度拟合的。 + // 正弦照常以弧度接收仰角。 let latitude_magnitude = latitude.abs(); let xi = if latitude_magnitude < 36.0 { 36.0 - latitude_magnitude @@ -418,6 +534,7 @@ pub fn rain_attenuation( - 0.45)); // Equation 10: the effective path length and the attenuation. + // 式 10:有效路径长度与衰减。 let effective_length = path_length * vertical_adjustment; Some(RainAttenuation { specific_db_per_km: specific, @@ -432,6 +549,10 @@ pub fn rain_attenuation( /// horizontal. /// /// This is a convenience for the two cases that are quoted by name. +/// +/// 返回以相对水平倾角描述的极化所对应的弧度角。 +/// +/// 这是为两种常被具名引用的情形提供的便利。 pub const fn polarisation_tilt(degrees_from_horizontal: f64) -> f64 { 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 /// 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( rain_rate: f64, frequency: f64,