use crate::qtty::dimensionless::{Albedos, IlluminationFractions};
use crate::qtty::radiometry::{
WattPerSquareMeterSteradianNanometer, WattsPerSquareMeterSteradianNanometer,
};
use crate::qtty::{Kilometers, Nanometers, Radians};
pub const MEAN_MOON_RADIUS: Kilometers = Kilometers::new(1_737.4);
pub const MEAN_MOON_DISTANCE: Kilometers = Kilometers::new(384_400.0);
pub fn lunar_full_moon_albedo_jones2013(wavelength: Nanometers) -> Albedos {
let lambda = wavelength
.clamp(Nanometers::new(300.0), Nanometers::new(1_100.0))
.value();
let t = (lambda - 300.0) / 800.0;
Albedos::new(0.075 + 0.065 * t)
}
pub fn lunar_phase_attenuation_jones2013(phase_angle: Radians) -> IlluminationFractions {
let a = phase_angle.abs().value().to_degrees();
IlluminationFractions::new(10f64.powf(-0.4 * (0.026 * a + 4.0e-9 * a.powi(4))))
}
pub fn lunar_albedo_jones2013(phase_angle: Radians, wavelength: Nanometers) -> Albedos {
let a = lunar_full_moon_albedo_jones2013(wavelength).value();
let p = lunar_phase_attenuation_jones2013(phase_angle).value();
Albedos::new(a * p)
}
pub fn reflected_lunar_spectral_radiance_jones2013(
solar_irradiance_w_m2_nm: f64,
wavelength: Nanometers,
phase_angle: Radians,
moon_distance: Kilometers,
) -> WattsPerSquareMeterSteradianNanometer {
let distance = moon_distance.value();
if !solar_irradiance_w_m2_nm.is_finite() || !distance.is_finite() || distance <= 0.0 {
return WattsPerSquareMeterSteradianNanometer::new(f64::NAN);
}
let albedo = lunar_albedo_jones2013(phase_angle, wavelength).value();
let radius_ratio: f64 = MEAN_MOON_RADIUS / moon_distance;
let omega_over_pi = radius_ratio.powi(2);
let distance_ratio: f64 = MEAN_MOON_DISTANCE / moon_distance;
let distance_scale = distance_ratio.powi(2);
WattsPerSquareMeterSteradianNanometer::new(
solar_irradiance_w_m2_nm * omega_over_pi * albedo * distance_scale,
)
}
pub type LunarSpectralRadianceUnit = WattPerSquareMeterSteradianNanometer;
#[cfg(test)]
mod tests {
use super::*;
use crate::qtty::{unit::Radian, Degrees};
#[test]
fn albedo_increases_from_blue_to_red() {
let blue = lunar_full_moon_albedo_jones2013(Nanometers::new(400.0));
let red = lunar_full_moon_albedo_jones2013(Nanometers::new(800.0));
assert!(red.value() > blue.value());
assert!(blue.value() > 0.0);
}
#[test]
fn phase_attenuation_is_one_at_full_and_small_near_new() {
let full = lunar_phase_attenuation_jones2013(Degrees::new(0.0).to::<Radian>());
let new = lunar_phase_attenuation_jones2013(Degrees::new(180.0).to::<Radian>());
assert!((full.value() - 1.0).abs() < 1.0e-12);
assert!(new.value() < 1.0e-3);
}
#[test]
fn reflected_radiance_is_positive_for_full_moon() {
let l = reflected_lunar_spectral_radiance_jones2013(
1.8,
Nanometers::new(550.0),
Degrees::new(0.0).to::<Radian>(),
MEAN_MOON_DISTANCE,
);
assert!(l.value() > 0.0);
}
}