//! Rain attenuation. //! //! 降雨衰减。 //! //! The specific attenuation of rain is modelled as a power law in the rain //! rate, `gamma = k R^alpha`, where `k` and `alpha` depend on the frequency, //! on the polarisation, and on the elevation. The coefficients are given by //! ITU-R Recommendation P.838 as closed form curve fits rather than as a //! table, which is what the current revision of that recommendation uses. //! //! The attenuation along a slant path follows ITU-R Recommendation P.618. //! The path length through the rain is limited by the height of the rain //! layer above the station, and two reduction factors account for the fact //! that the rain rate is not uniform along the path: a horizontal reduction //! and a vertical adjustment. Both depend on the specific attenuation, so //! the calculation is not a simple product of a rate and a length. //! //! Sources: //! //! - Specific attenuation coefficients: ITU-R Recommendation P.838-3, //! *Specific attenuation model for rain for use in prediction methods*, //! equations 2 and 3 and tables 1 and 2. //! - Slant path method: ITU-R Recommendation P.618-13, *Propagation data and //! prediction methods required for the design of Earth-space //! telecommunication systems*, equations 1 through 10. use apsis_rf::Decibel; /// The effective radius of the Earth used by the path geometry, in /// kilometers. /// /// 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. /// /// Recommendation P.618 takes the rain height from the zero degree isotherm /// 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, centre: -0.100_08, width: 1.130_98, }, CurveTerm { amplitude: -0.353_51, centre: 1.269_70, width: 0.454_00, }, CurveTerm { amplitude: -0.237_89, centre: 0.860_36, width: 0.153_54, }, CurveTerm { amplitude: -0.941_58, centre: 0.645_52, width: 0.168_17, }, ]; /// The coefficients of the `k` curve fit for vertical polarisation. /// /// 垂直极化 `k` 曲线拟合的系数。 const K_VERTICAL_TERMS: &[CurveTerm] = &[ CurveTerm { amplitude: -3.805_95, centre: 0.569_34, width: 0.810_61, }, CurveTerm { amplitude: -3.449_65, centre: -0.229_11, width: 0.510_59, }, CurveTerm { amplitude: -0.399_02, centre: 0.730_42, width: 0.118_99, }, CurveTerm { amplitude: 0.501_67, centre: 1.073_19, width: 0.271_95, }, ]; /// The coefficients of the `alpha` curve fit for horizontal polarisation. /// /// 水平极化 `alpha` 曲线拟合的系数。 const ALPHA_HORIZONTAL_TERMS: &[CurveTerm] = &[ CurveTerm { amplitude: -0.143_18, centre: 1.824_42, width: -0.551_87, }, CurveTerm { amplitude: 0.295_91, centre: 0.775_64, width: 0.198_22, }, CurveTerm { amplitude: 0.321_77, centre: 0.637_73, width: 0.131_64, }, CurveTerm { amplitude: -5.376_10, centre: -0.962_30, width: 1.478_28, }, CurveTerm { amplitude: 16.172_1, centre: -3.299_80, width: 3.439_90, }, ]; /// The coefficients of the `alpha` curve fit for vertical polarisation. /// /// 垂直极化 `alpha` 曲线拟合的系数。 const ALPHA_VERTICAL_TERMS: &[CurveTerm] = &[ CurveTerm { amplitude: -0.077_71, centre: 2.338_40, width: -0.762_84, }, CurveTerm { amplitude: 0.567_27, centre: 0.955_45, width: 0.540_39, }, CurveTerm { amplitude: -0.202_38, centre: 1.145_20, width: 0.268_09, }, CurveTerm { amplitude: -48.299_1, centre: 0.791_669, width: 0.116_226, }, CurveTerm { amplitude: 48.583_3, centre: 0.791_459, width: 0.116_479, }, ]; /// 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. /// /// The factor is the ratio of the effective path length to the longer /// 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 && self.path_length_km.is_finite() && self.path_length_km >= 0.0 && self.attenuation_db.is_finite() && self.attenuation_db >= 0.0 } } /// Evaluates one P.838-3 curve term. /// /// 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() .map(|term| curve_term(*term, frequency_ghz)) .sum(); 10f64.powf(gaussian + slope * frequency_ghz.log10() + offset) } /// 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() .map(|term| curve_term(*term, frequency_ghz)) .sum(); gaussian + slope * frequency_ghz.log10() + offset } /// Returns the P.838-3 coefficients of the specific attenuation power law. /// /// The polarisation is an angle in radians between the electric field and /// the horizontal, so zero is horizontal and a right angle is vertical. The /// elevation is in radians. /// /// The result is a pair of the coefficient `k` and the exponent `alpha` of /// the relation `gamma = k R^alpha`, with the rain rate in millimeters per /// hour and the result in decibels per kilometer. /// /// Returns `None` when the frequency, polarisation, or elevation is not /// usable. The recommendation is stated for frequencies from 1 GHz to /// 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) { return None; } if !polarisation.is_finite() || !elevation.is_finite() { return None; } let k_horizontal = base_k( K_HORIZONTAL_TERMS, K_SLOPE_HORIZONTAL, K_OFFSET_HORIZONTAL, frequency_ghz, ); let k_vertical = base_k( K_VERTICAL_TERMS, K_SLOPE_VERTICAL, K_OFFSET_VERTICAL, frequency_ghz, ); let alpha_horizontal = base_alpha( ALPHA_HORIZONTAL_TERMS, ALPHA_SLOPE_HORIZONTAL, ALPHA_OFFSET_HORIZONTAL, frequency_ghz, ); let alpha_vertical = base_alpha( ALPHA_VERTICAL_TERMS, ALPHA_SLOPE_VERTICAL, ALPHA_OFFSET_VERTICAL, frequency_ghz, ); // The elevation and polarisation enter through the square of the cosine // 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 + k_vertical * alpha_vertical + (k_horizontal * alpha_horizontal - k_vertical * alpha_vertical) * geometry_factor) / (2.0 * k); Some((k, alpha)) } /// Returns the specific attenuation in rain, in decibels per kilometer. /// /// The rain rate is in millimeters per hour. /// /// 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, polarisation: f64, elevation: f64, ) -> Option { if !rain_rate.is_finite() || rain_rate < 0.0 { return None; } let (k, alpha) = rain_coefficients(frequency, polarisation, elevation)?; Some(k * rain_rate.powf(alpha)) } /// Returns the total rain attenuation along a slant path. /// /// The rain rate is the value exceeded for the percentage of time of /// interest, in millimeters per hour. The station height above sea level and /// the rain layer height are in kilometers; pass [`DEFAULT_RAIN_HEIGHT_KM`] /// for the latter when no local value is available. /// /// The method is the one of Recommendation P.618. The slant path length /// follows from the rain height, the station height, and the elevation, with /// a correction for the curvature of the Earth. Two reduction factors then /// account for the non-uniformity of the rain: a horizontal factor that /// depends on the specific attenuation, the frequency, and the horizontal /// projection of the path, and a vertical factor that depends on the /// latitude through the angle at which the path crosses the rain layer. /// /// Returns `None` when the rain rate, frequency, or elevation is not usable, /// when the station is above the rain layer, or when the elevation is at or /// 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, polarisation: f64, elevation: f64, station_height_km: f64, rain_height_km: f64, latitude: f64, ) -> Option { if !rain_rate.is_finite() || rain_rate < 0.0 { return None; } if !elevation.is_finite() || elevation.sin() <= 0.0 { return None; } if !station_height_km.is_finite() || !rain_height_km.is_finite() { return None; } if !latitude.is_finite() { return None; } 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, attenuation_db: 0.0, horizontal_reduction: 1.0, vertical_adjustment: 1.0, }); } let sine = elevation.sin(); let slant_length = 2.0 * height_difference / ((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 { height_difference / sine }; // 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 } else { 0.0 }; let elevation_degrees = elevation.to_degrees(); let vertical_adjustment = 1.0 / (1.0 + sine.sqrt() * (31.0 * (1.0 - (-(elevation_degrees / (1.0 + xi))).exp()) * (path_length * specific).sqrt() / frequency_ghz.powi(2) - 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, path_length_km: effective_length, attenuation_db: specific * effective_length, horizontal_reduction, vertical_adjustment, }) } /// Returns the angle in radians for a polarisation described by a tilt from /// 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 } /// Returns the total rain attenuation along a slant path, taking the rain /// height from the Recommendation P.839 model. /// /// This is the same calculation as [`rain_attenuation`], with the rain layer /// height looked up from the tabulated zero degree isotherm height at the /// given latitude and longitude rather than supplied by the caller. It is /// the form to use unless a locally measured isotherm height is available. /// /// The station height must be given; pass zero for a station at sea level. /// /// Returns `None` when the coordinates are not usable, or when any of the /// other arguments is rejected by [`rain_attenuation`]. /// /// 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, polarisation: f64, elevation: f64, station_height_km: f64, latitude: f64, longitude: f64, ) -> Option { let rain_height_km = crate::p839::rain_height(latitude, longitude)?; rain_attenuation( rain_rate, frequency, polarisation, elevation, station_height_km, rain_height_km, latitude, ) }