apsis-rs/crates/rf/apsis-atmosphere/tests/gaseous.rs

108 lines
3.8 KiB
Rust

//! Tests for gaseous attenuation.
use apsis_atmosphere::{
GaseousAttenuation, gaseous_attenuation, oxygen_zenith_attenuation,
water_vapour_zenith_attenuation,
};
use core::f64::consts::{FRAC_PI_2, FRAC_PI_6};
const TOLERANCE: f64 = 1e-12;
#[test]
fn oxygen_attenuation_is_small_at_low_frequencies() {
// Below about 10 GHz the oxygen contribution at the zenith is a small
// fraction of a decibel.
for gigahertz in [1.0, 2.0, 4.0, 6.0, 10.0] {
let value = oxygen_zenith_attenuation(gigahertz * 1.0e9).expect("valid frequency");
assert!(value < 0.2, "{gigahertz} GHz gave {value} dB");
assert!(value >= 0.0);
}
}
#[test]
fn oxygen_attenuation_peaks_near_sixty_gigahertz() {
let peak = oxygen_zenith_attenuation(60.0e9).expect("valid");
// The 60 GHz complex reaches about 15 dB through the whole atmosphere.
assert!((peak - 15.0).abs() < 0.5, "peak {peak}");
// A frequency on either side is lower.
let below = oxygen_zenith_attenuation(50.0e9).expect("valid");
let above = oxygen_zenith_attenuation(70.0e9).expect("valid");
assert!(below < peak);
assert!(above < peak);
}
#[test]
fn oxygen_attenuation_is_monotonic_through_the_complex() {
// The value rises steadily from below the complex into its centre.
let mut previous = 0.0;
for gigahertz in [40.0, 45.0, 50.0, 55.0, 58.0, 59.0] {
let value = oxygen_zenith_attenuation(gigahertz * 1.0e9).expect("valid");
assert!(value > previous, "{gigahertz} GHz gave {value}");
previous = value;
}
}
#[test]
fn water_vapour_attenuation_peaks_near_twenty_two_gigahertz() {
let peak = water_vapour_zenith_attenuation(22.235e9).expect("valid");
assert!((peak - 0.20).abs() < 1e-9, "peak {peak}");
let below = water_vapour_zenith_attenuation(15.0e9).expect("valid");
let above = water_vapour_zenith_attenuation(30.0e9).expect("valid");
assert!(below < peak);
assert!(above < peak);
}
#[test]
fn water_vapour_attenuation_is_small_below_ten_gigahertz() {
for gigahertz in [1.0, 4.0, 6.0, 10.0] {
let value = water_vapour_zenith_attenuation(gigahertz * 1.0e9).expect("valid");
assert!(value < 0.05, "{gigahertz} GHz gave {value} dB");
}
}
#[test]
fn the_total_is_the_sum_of_its_parts() {
let total = gaseous_attenuation(20.0e9).expect("valid");
assert!((total.zenith_db() - (total.oxygen_db + total.water_vapour_db)).abs() < TOLERANCE);
}
#[test]
fn attenuation_grows_as_the_elevation_falls() {
let gas = GaseousAttenuation::new(0.05, 0.15);
let zenith = gas.at_elevation(FRAC_PI_2);
let thirty = gas.at_elevation(FRAC_PI_6);
// The zenith total is 0.2 dB and thirty degrees doubles it.
assert!((zenith.db() - 0.2).abs() < TOLERANCE);
assert!((thirty.db() - 0.4).abs() < 1e-9);
}
#[test]
fn attenuation_at_the_horizon_is_infinite() {
let gas = GaseousAttenuation::new(0.05, 0.15);
assert!(gas.at_elevation(0.0).db().is_infinite());
}
#[test]
fn a_zero_zenith_attenuation_gives_zero_at_every_elevation() {
let none = GaseousAttenuation::new(0.0, 0.0);
assert_eq!(none.at_elevation(FRAC_PI_6).db(), 0.0);
}
#[test]
fn validity_is_reported() {
assert!(GaseousAttenuation::default().is_valid());
assert!(GaseousAttenuation::new(0.05, 0.15).is_valid());
assert!(!GaseousAttenuation::new(-0.1, 0.15).is_valid());
assert!(!GaseousAttenuation::new(0.05, f64::NAN).is_valid());
}
#[test]
fn invalid_frequencies_are_rejected() {
assert_eq!(oxygen_zenith_attenuation(0.0), None);
assert_eq!(oxygen_zenith_attenuation(-1.0), None);
assert_eq!(oxygen_zenith_attenuation(f64::NAN), None);
assert_eq!(water_vapour_zenith_attenuation(0.0), None);
assert_eq!(water_vapour_zenith_attenuation(f64::INFINITY), None);
assert_eq!(gaseous_attenuation(0.0), None);
}