143 lines
5.5 KiB
Rust
143 lines
5.5 KiB
Rust
//! Tests for free space propagation.
|
||
//!
|
||
//! 自由空间传播的测试。
|
||
|
||
use apsis_core::constant::SPEED_OF_LIGHT;
|
||
use apsis_rf::{Gain, free_space_path_loss, free_space_received_ratio, wavelength};
|
||
|
||
const TOLERANCE: f64 = 1e-9;
|
||
|
||
#[test]
|
||
fn wavelength_is_the_speed_of_light_over_frequency() {
|
||
assert!((wavelength(SPEED_OF_LIGHT).expect("valid") - 1.0).abs() < TOLERANCE);
|
||
// A frequency of 300 MHz has a wavelength of about one meter.
|
||
// 300 MHz 的频率波长约为米。
|
||
assert!((wavelength(300.0e6).expect("valid") - 0.999_308).abs() < 1e-5);
|
||
// The same relation holds for a microwave frequency.
|
||
// 同一关系对微波频率也成立。
|
||
assert!((wavelength(12.0e9).expect("valid") - 0.024_982_7).abs() < 1e-6);
|
||
}
|
||
|
||
#[test]
|
||
fn wavelength_rejects_invalid_frequencies() {
|
||
assert_eq!(wavelength(0.0), None);
|
||
assert_eq!(wavelength(-1.0), None);
|
||
assert_eq!(wavelength(f64::NAN), None);
|
||
assert_eq!(wavelength(f64::INFINITY), None);
|
||
}
|
||
|
||
#[test]
|
||
fn path_loss_matches_the_closed_form() {
|
||
// Attenuation over one meter at one meter wavelength: the ratio is
|
||
// (4 pi)^2, about 21.98 dB.
|
||
// 一米波长下一米距离的衰减:比值为 (4 pi)^2,约 21.98 dB。
|
||
let loss = free_space_path_loss(1.0, SPEED_OF_LIGHT).expect("valid arguments");
|
||
let expected = 10.0 * (4.0 * core::f64::consts::PI).powi(2).log10();
|
||
assert!((loss.db() - expected).abs() < 1e-9, "loss {}", loss.db());
|
||
assert!((loss.db() - 21.984).abs() < 0.001);
|
||
}
|
||
|
||
#[test]
|
||
fn path_loss_matches_the_engineering_form() {
|
||
// The form used in link budgets, with distance in kilometers and
|
||
// frequency in megahertz,
|
||
// 链路预算中所用的形式,距离以千米、频率以兆赫兹计,
|
||
//
|
||
// L = 32.44 + 20 log10(d_km) + 20 log10(f_MHz)
|
||
// 即以千米计的距离与以兆赫兹计的频率表示的工程形式。
|
||
//
|
||
// has a constant that follows from the speed of light rounded to
|
||
// 299 792 458 m/s, and agrees with the expression used here to the
|
||
// precision of that constant.
|
||
// 其常数由舍入到 299 792 458 m/s 的光速得出,并在该常量的精度内与此处所用的
|
||
// 表达式一致。
|
||
let distance_km = 1_000.0;
|
||
let frequency_mhz = 12_000.0;
|
||
let loss = free_space_path_loss(distance_km * 1_000.0, frequency_mhz * 1.0e6)
|
||
.expect("valid arguments");
|
||
let engineering = 32.44 + 20.0 * distance_km.log10() + 20.0 * frequency_mhz.log10();
|
||
assert!(
|
||
(loss.db() - engineering).abs() < 0.01,
|
||
"loss {} engineering {}",
|
||
loss.db(),
|
||
engineering
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn a_larger_distance_increases_the_loss_by_six_decibels_per_doubling() {
|
||
let near = free_space_path_loss(1_000.0, 1.0e9).expect("valid");
|
||
let far = free_space_path_loss(2_000.0, 1.0e9).expect("valid");
|
||
assert!((far.db() - near.db() - 6.020_6).abs() < 1e-3);
|
||
}
|
||
|
||
#[test]
|
||
fn a_higher_frequency_increases_the_loss() {
|
||
let low = free_space_path_loss(1_000.0, 1.0e9).expect("valid");
|
||
let high = free_space_path_loss(1_000.0, 2.0e9).expect("valid");
|
||
assert!(high.db() > low.db());
|
||
assert!((high.db() - low.db() - 6.020_6).abs() < 1e-3);
|
||
}
|
||
|
||
#[test]
|
||
fn a_zero_or_negative_distance_is_rejected() {
|
||
// A zero distance is outside the far field range where the expression
|
||
// applies, and would give an infinite loss in decibels.
|
||
// 零距离在该表达式所适用的远场范围之外,会给出以分贝计的无穷损耗。
|
||
assert_eq!(free_space_path_loss(0.0, 1.0e9), None);
|
||
assert_eq!(free_space_path_loss(-1.0, 1.0e9), None);
|
||
}
|
||
|
||
#[test]
|
||
fn invalid_arguments_are_rejected() {
|
||
assert_eq!(free_space_path_loss(-1.0, 1.0e9), None);
|
||
assert_eq!(free_space_path_loss(f64::NAN, 1.0e9), None);
|
||
assert_eq!(free_space_path_loss(1_000.0, 0.0), None);
|
||
assert_eq!(free_space_path_loss(1_000.0, -1.0), None);
|
||
}
|
||
|
||
#[test]
|
||
fn received_ratio_combines_gains_and_loss() {
|
||
let distance = 1_000_000.0;
|
||
let frequency = 12.0e9;
|
||
let transmitter = Gain::from_dbi(30.0).expect("valid");
|
||
let receiver = Gain::from_dbi(40.0).expect("valid");
|
||
let ratio =
|
||
free_space_received_ratio(distance, frequency, transmitter, receiver).expect("valid");
|
||
let loss = free_space_path_loss(distance, frequency).expect("valid");
|
||
assert!((ratio.db() - (30.0 + 40.0 - loss.db())).abs() < 1e-9);
|
||
}
|
||
|
||
#[test]
|
||
fn a_large_receive_gain_can_exceed_the_spreading_loss() {
|
||
// Two dishes of 40 dBi at ten meters and 12 GHz. The spreading loss
|
||
// there is about 74 dB, so the 80 dB of combined gain leaves a positive
|
||
// ratio. This is the near field of a large antenna pair and is the
|
||
// reason a link budget cannot be read without also checking the
|
||
// distance regime.
|
||
// 两个 40 dBi 的天线,在十米与 12 GHz 下。那里的扩散损耗约为 74 dB,故 80 dB 的
|
||
// 合成增益留下正的比值。这是一对大天线的近场,也正是链路预算不能在不检查距离
|
||
// 区间的情况下阅读的原因。
|
||
let ratio = free_space_received_ratio(
|
||
10.0,
|
||
12.0e9,
|
||
Gain::from_dbi(40.0).expect("valid"),
|
||
Gain::from_dbi(40.0).expect("valid"),
|
||
)
|
||
.expect("valid");
|
||
assert!(ratio.db() > 0.0, "ratio {}", ratio.db());
|
||
assert!((ratio.db() - 5.969).abs() < 0.01, "ratio {}", ratio.db());
|
||
}
|
||
|
||
#[test]
|
||
fn invalid_received_ratio_arguments_are_rejected() {
|
||
assert_eq!(
|
||
free_space_received_ratio(-1.0, 1.0e9, Gain::ISOTROPIC, Gain::ISOTROPIC),
|
||
None
|
||
);
|
||
assert_eq!(
|
||
free_space_received_ratio(1_000.0, 0.0, Gain::ISOTROPIC, Gain::ISOTROPIC),
|
||
None
|
||
);
|
||
}
|