use crate::qtty::{Airmasses, Radians};
use core::marker::PhantomData;
pub trait AirmassFormula {
const NAME: &'static str;
fn airmass(zenith: Radians) -> Airmasses;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct PlaneParallel;
impl AirmassFormula for PlaneParallel {
const NAME: &'static str = "PlaneParallel";
#[inline]
fn airmass(zenith: Radians) -> Airmasses {
Airmasses::new(1.0 / zenith.cos())
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Young1994;
impl AirmassFormula for Young1994 {
const NAME: &'static str = "Young1994";
#[inline]
fn airmass(zenith: Radians) -> Airmasses {
let c = zenith.cos();
let num = 1.002432 * c * c + 0.148386 * c + 0.0096467;
let den = c * c * c + 0.149864 * c * c + 0.0102963 * c + 0.000303978;
Airmasses::new(num / den)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Rozenberg1966;
impl AirmassFormula for Rozenberg1966 {
const NAME: &'static str = "Rozenberg1966";
#[inline]
fn airmass(zenith: Radians) -> Airmasses {
let c = zenith.cos();
Airmasses::new(1.0 / (c + 0.025 * (-11.0 * c).exp()))
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct KrisciunasSchaefer1991;
impl AirmassFormula for KrisciunasSchaefer1991 {
const NAME: &'static str = "KrisciunasSchaefer1991";
#[inline]
fn airmass(zenith: Radians) -> Airmasses {
let s = zenith.sin();
Airmasses::new((1.0 - 0.96 * s * s).powf(-0.5))
}
}
pub type DefaultAirmassFormula = KrisciunasSchaefer1991;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Formula<F: AirmassFormula>(PhantomData<F>);
impl<F: AirmassFormula> Formula<F> {
#[inline]
pub const fn new() -> Self {
Self(PhantomData)
}
}
#[inline]
pub fn airmass<F: AirmassFormula>(zenith: Radians) -> Airmasses {
F::airmass(zenith)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::qtty::Radians;
fn assert_zenith_is_one<F: AirmassFormula>() {
let x = airmass::<F>(Radians::new(0.0));
assert!((x.value() - 1.0).abs() < 1e-3, "{} -> {:?}", F::NAME, x);
}
#[test]
fn zenith_is_one_for_all_formulas() {
assert_zenith_is_one::<PlaneParallel>();
assert_zenith_is_one::<Young1994>();
assert_zenith_is_one::<Rozenberg1966>();
assert_zenith_is_one::<KrisciunasSchaefer1991>();
}
#[test]
fn plane_parallel_diverges_near_horizon() {
let z = Radians::new(89.0_f64.to_radians());
assert!(airmass::<PlaneParallel>(z).value() > 50.0);
}
#[test]
fn ks91_finite_at_horizon() {
let z = Radians::new(90.0_f64.to_radians());
let x = airmass::<KrisciunasSchaefer1991>(z);
assert!(x.value().is_finite() && x.value() > 1.0);
}
}