142 lines
4.4 KiB
Rust
142 lines
4.4 KiB
Rust
//! Tests for the parabolic aperture model.
|
|
|
|
use apsis_antenna::{ParabolicAperture, beam_width};
|
|
use core::f64::consts::PI;
|
|
|
|
/// A three meter dish with a typical aperture efficiency.
|
|
const DISH: ParabolicAperture = ParabolicAperture {
|
|
diameter: 3.0,
|
|
efficiency: 0.65,
|
|
};
|
|
|
|
#[test]
|
|
fn aperture_area_follows_from_the_diameter() {
|
|
let expected = PI * 1.5 * 1.5;
|
|
assert!((DISH.area() - expected).abs() < 1e-12);
|
|
assert!((DISH.effective_area() - expected * 0.65).abs() < 1e-12);
|
|
}
|
|
|
|
#[test]
|
|
fn peak_gain_matches_the_closed_form() {
|
|
// At 12 GHz the wavelength is 0.02498 m, and the gain is
|
|
// eta (pi D / lambda)^2.
|
|
let wavelength = 0.024_982_710_7;
|
|
let gain = DISH.peak_gain(wavelength).expect("valid");
|
|
let expected = 0.65 * (PI * 3.0 / wavelength).powi(2);
|
|
assert!((gain.ratio() - expected).abs() < 1e-6);
|
|
// The result is about 49.66 dBi.
|
|
assert!(
|
|
(gain.dbi().expect("positive") - 49.661_764_937_398_814).abs() < 1e-9,
|
|
"dBi {}",
|
|
gain.dbi().expect("positive")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn a_larger_dish_has_more_gain() {
|
|
let small = ParabolicAperture {
|
|
diameter: 1.0,
|
|
efficiency: 0.65,
|
|
};
|
|
let large = ParabolicAperture {
|
|
diameter: 2.0,
|
|
efficiency: 0.65,
|
|
};
|
|
let wavelength = 0.025;
|
|
let small_gain = small.peak_gain(wavelength).expect("valid");
|
|
let large_gain = large.peak_gain(wavelength).expect("valid");
|
|
// Doubling the diameter quadruples the gain, which is six decibels.
|
|
assert!(
|
|
(large_gain.dbi().expect("positive") - small_gain.dbi().expect("positive") - 6.020_6).abs()
|
|
< 1e-6
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn a_higher_aperture_efficiency_adds_gain() {
|
|
let low = ParabolicAperture {
|
|
diameter: 3.0,
|
|
efficiency: 0.5,
|
|
};
|
|
let high = ParabolicAperture {
|
|
diameter: 3.0,
|
|
efficiency: 0.7,
|
|
};
|
|
let wavelength = 0.025;
|
|
assert!(
|
|
high.peak_gain(wavelength).expect("valid").ratio()
|
|
> low.peak_gain(wavelength).expect("valid").ratio()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn the_beam_width_follows_the_wavelength_over_diameter() {
|
|
let wavelength = 0.025;
|
|
let uniform = DISH.uniform_beam_width(wavelength).expect("valid");
|
|
let tapered = DISH.half_power_beam_width(wavelength).expect("valid");
|
|
assert!((uniform - 1.02 * wavelength / 3.0).abs() < 1e-12);
|
|
// A tapered illumination widens the beam.
|
|
assert!(tapered > uniform);
|
|
assert!((tapered - 1.15 * wavelength / 3.0).abs() < 1e-12);
|
|
}
|
|
|
|
#[test]
|
|
fn the_uniform_beam_width_matches_a_known_value() {
|
|
// A 3 m dish at 12 GHz has a half power beam width near 0.0085 radians,
|
|
// which is about 0.49 degrees.
|
|
let width = DISH.uniform_beam_width(0.024_982_710_7).expect("valid");
|
|
assert!((width - 0.008_494).abs() < 1e-5, "width {width}");
|
|
assert!((width.to_degrees() - 0.486_7).abs() < 0.001);
|
|
}
|
|
|
|
#[test]
|
|
fn beam_width_helper_validates_its_arguments() {
|
|
assert!(beam_width(3.0, 0.025, 1.02).is_some());
|
|
assert_eq!(beam_width(0.0, 0.025, 1.02), None);
|
|
assert_eq!(beam_width(-1.0, 0.025, 1.02), None);
|
|
assert_eq!(beam_width(3.0, 0.0, 1.02), None);
|
|
assert_eq!(beam_width(3.0, -0.025, 1.02), None);
|
|
assert_eq!(beam_width(3.0, 0.025, 0.0), None);
|
|
assert_eq!(beam_width(f64::NAN, 0.025, 1.02), None);
|
|
assert_eq!(beam_width(3.0, f64::INFINITY, 1.02), None);
|
|
}
|
|
|
|
#[test]
|
|
fn an_invalid_aperture_produces_no_result() {
|
|
let zero_diameter = ParabolicAperture {
|
|
diameter: 0.0,
|
|
efficiency: 0.65,
|
|
};
|
|
assert!(!zero_diameter.is_valid());
|
|
assert_eq!(zero_diameter.peak_gain(0.025), None);
|
|
assert_eq!(zero_diameter.half_power_beam_width(0.025), None);
|
|
|
|
let bad_efficiency = ParabolicAperture {
|
|
diameter: 3.0,
|
|
efficiency: 1.5,
|
|
};
|
|
assert!(!bad_efficiency.is_valid());
|
|
assert_eq!(bad_efficiency.peak_gain(0.025), None);
|
|
|
|
let zero_efficiency = ParabolicAperture {
|
|
diameter: 3.0,
|
|
efficiency: 0.0,
|
|
};
|
|
assert!(!zero_efficiency.is_valid());
|
|
|
|
let non_finite = ParabolicAperture {
|
|
diameter: f64::NAN,
|
|
efficiency: 0.65,
|
|
};
|
|
assert!(!non_finite.is_valid());
|
|
}
|
|
|
|
#[test]
|
|
fn the_aperture_and_gain_conversions_are_consistent() {
|
|
// The peak gain and the effective area must describe the same antenna.
|
|
let wavelength = 0.025;
|
|
let gain = DISH.peak_gain(wavelength).expect("valid");
|
|
let aperture = gain.effective_aperture(wavelength).expect("valid");
|
|
assert!((aperture - DISH.effective_area()).abs() < 1e-9);
|
|
}
|