use crate::{Quantity, Unit};
use qtty_derive::Unit;
pub use crate::dimension::{
InverseSolidAngle, PhotonRadiance, Radiance, SpectralPhotonRadiance, SpectralRadiance,
};
pub trait RadianceUnit: Unit<Dim = Radiance> {}
impl<T: Unit<Dim = Radiance>> RadianceUnit for T {}
pub trait SpectralRadianceUnit: Unit<Dim = SpectralRadiance> {}
impl<T: Unit<Dim = SpectralRadiance>> SpectralRadianceUnit for T {}
pub trait PhotonRadianceUnit: Unit<Dim = PhotonRadiance> {}
impl<T: Unit<Dim = PhotonRadiance>> PhotonRadianceUnit for T {}
pub trait SpectralPhotonRadianceUnit: Unit<Dim = SpectralPhotonRadiance> {}
impl<T: Unit<Dim = SpectralPhotonRadiance>> SpectralPhotonRadianceUnit for T {}
pub trait InverseSolidAngleUnit: Unit<Dim = InverseSolidAngle> {}
impl<T: Unit<Dim = InverseSolidAngle>> InverseSolidAngleUnit for T {}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "W·m⁻²·sr⁻¹", dimension = Radiance, ratio = 1.0)]
pub struct WattPerSquareMeterSteradian;
pub type WattsPerSquareMeterSteradian = Quantity<WattPerSquareMeterSteradian>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "erg·s⁻¹·cm⁻²·sr⁻¹", dimension = Radiance, ratio = 1.0e-3)]
pub struct ErgPerSecondSquareCentimeterSteradian;
pub type ErgsPerSecondSquareCentimeterSteradian = Quantity<ErgPerSecondSquareCentimeterSteradian>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "W·m⁻²·sr⁻¹·m⁻¹", dimension = SpectralRadiance, ratio = 1.0)]
pub struct WattPerSquareMeterSteradianMeter;
pub type WattsPerSquareMeterSteradianMeter = Quantity<WattPerSquareMeterSteradianMeter>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "W·m⁻²·sr⁻¹·nm⁻¹", dimension = SpectralRadiance, ratio = 1.0e9)]
pub struct WattPerSquareMeterSteradianNanometer;
pub type WattsPerSquareMeterSteradianNanometer = Quantity<WattPerSquareMeterSteradianNanometer>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "erg·s⁻¹·cm⁻²·sr⁻¹·Å⁻¹", dimension = SpectralRadiance, ratio = 1.0e7)]
pub struct ErgPerSecondSquareCentimeterSteradianAngstrom;
pub type ErgsPerSecondSquareCentimeterSteradianAngstrom =
Quantity<ErgPerSecondSquareCentimeterSteradianAngstrom>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ph·m⁻²·s⁻¹·sr⁻¹", dimension = PhotonRadiance, ratio = 1.0)]
pub struct PhotonPerSquareMeterSecondSteradian;
pub type PhotonsPerSquareMeterSecondSteradian = Quantity<PhotonPerSquareMeterSecondSteradian>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ph·cm⁻²·s⁻¹·sr⁻¹", dimension = PhotonRadiance, ratio = 1.0e4)]
pub struct PhotonPerSquareCentimeterSecondSteradian;
pub type PhotonsPerSquareCentimeterSecondSteradian =
Quantity<PhotonPerSquareCentimeterSecondSteradian>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ph·cm⁻²·ns⁻¹·sr⁻¹", dimension = PhotonRadiance, ratio = 1.0e13)]
pub struct PhotonPerSquareCentimeterNanosecondSteradian;
pub type PhotonsPerSquareCentimeterNanosecondSteradian =
Quantity<PhotonPerSquareCentimeterNanosecondSteradian>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ph·m⁻²·s⁻¹·sr⁻¹·m⁻¹", dimension = SpectralPhotonRadiance, ratio = 1.0)]
pub struct PhotonPerSquareMeterSecondSteradianMeter;
pub type PhotonsPerSquareMeterSecondSteradianMeter =
Quantity<PhotonPerSquareMeterSecondSteradianMeter>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ph·cm⁻²·s⁻¹·sr⁻¹·Å⁻¹", dimension = SpectralPhotonRadiance, ratio = 1.0e14)]
pub struct PhotonPerSquareCentimeterSecondSteradianAngstrom;
pub type PhotonsPerSquareCentimeterSecondSteradianAngstrom =
Quantity<PhotonPerSquareCentimeterSecondSteradianAngstrom>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ph·cm⁻²·s⁻¹·sr⁻¹·nm⁻¹", dimension = SpectralPhotonRadiance, ratio = 1.0e13)]
pub struct PhotonPerSquareCentimeterSecondSteradianNanometer;
pub type PhotonsPerSquareCentimeterSecondSteradianNanometer =
Quantity<PhotonPerSquareCentimeterSecondSteradianNanometer>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ph·cm⁻²·ns⁻¹·sr⁻¹·nm⁻¹", dimension = SpectralPhotonRadiance, ratio = 1.0e22)]
pub struct PhotonPerSquareCentimeterNanosecondSteradianNanometer;
pub type PhotonsPerSquareCentimeterNanosecondSteradianNanometer =
Quantity<PhotonPerSquareCentimeterNanosecondSteradianNanometer>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "S10", dimension = InverseSolidAngle, ratio = 1.0)]
pub struct S10;
pub type S10s = Quantity<S10>;
#[macro_export]
#[doc(hidden)]
macro_rules! radiance_units {
($cb:path) => {
$cb!(
WattPerSquareMeterSteradian,
ErgPerSecondSquareCentimeterSteradian
);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! spectral_radiance_units {
($cb:path) => {
$cb!(
WattPerSquareMeterSteradianMeter,
WattPerSquareMeterSteradianNanometer,
ErgPerSecondSquareCentimeterSteradianAngstrom
);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! photon_radiance_units {
($cb:path) => {
$cb!(
PhotonPerSquareMeterSecondSteradian,
PhotonPerSquareCentimeterSecondSteradian,
PhotonPerSquareCentimeterNanosecondSteradian
);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! spectral_photon_radiance_units {
($cb:path) => {
$cb!(
PhotonPerSquareMeterSecondSteradianMeter,
PhotonPerSquareCentimeterSecondSteradianAngstrom,
PhotonPerSquareCentimeterSecondSteradianNanometer,
PhotonPerSquareCentimeterNanosecondSteradianNanometer
);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! inverse_solid_angle_units {
($cb:path) => {
$cb!(S10);
};
}
radiance_units!(crate::impl_unit_from_conversions);
spectral_radiance_units!(crate::impl_unit_from_conversions);
photon_radiance_units!(crate::impl_unit_from_conversions);
spectral_photon_radiance_units!(crate::impl_unit_from_conversions);
#[cfg(feature = "cross-unit-ops")]
radiance_units!(crate::impl_unit_cross_unit_ops);
#[cfg(feature = "cross-unit-ops")]
spectral_radiance_units!(crate::impl_unit_cross_unit_ops);
#[cfg(feature = "cross-unit-ops")]
photon_radiance_units!(crate::impl_unit_cross_unit_ops);
#[cfg(feature = "cross-unit-ops")]
spectral_photon_radiance_units!(crate::impl_unit_cross_unit_ops);
#[cfg(test)]
radiance_units!(crate::assert_units_are_builtin);
#[cfg(test)]
spectral_radiance_units!(crate::assert_units_are_builtin);
#[cfg(test)]
photon_radiance_units!(crate::assert_units_are_builtin);
#[cfg(test)]
spectral_photon_radiance_units!(crate::assert_units_are_builtin);
#[cfg(test)]
inverse_solid_angle_units!(crate::assert_units_are_builtin);
const HC_ERG_ANGSTROM: f64 = 1.986_445_857_148_968e-8;
const HC_JOULE_METER: f64 = 1.986_445_857_148_968e-25;
#[inline]
pub fn erg_to_photon(
energy_radiance: ErgsPerSecondSquareCentimeterSteradianAngstrom,
lambda: crate::length::Meters,
) -> PhotonsPerSquareCentimeterSecondSteradianAngstrom {
let lambda_angstrom = lambda.value() * 1.0e10;
PhotonsPerSquareCentimeterSecondSteradianAngstrom::new(
energy_radiance.value() * lambda_angstrom / HC_ERG_ANGSTROM,
)
}
#[inline]
pub fn spectral_radiance_to_photon_radiance_ns_nm(
energy_radiance: WattsPerSquareMeterSteradianNanometer,
lambda: crate::length::Nanometers,
) -> PhotonsPerSquareCentimeterNanosecondSteradianNanometer {
let lambda_m = lambda.value() * 1.0e-9;
let photons_per_s_m2_nm = energy_radiance.value() * lambda_m / HC_JOULE_METER;
PhotonsPerSquareCentimeterNanosecondSteradianNanometer::new(photons_per_s_m2_nm * 1.0e-13)
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::length::Meters;
use approx::assert_relative_eq;
#[test]
fn radiance_cgs_to_si_conversion() {
let cgs = ErgsPerSecondSquareCentimeterSteradian::new(1.0);
let si = cgs.to::<WattPerSquareMeterSteradian>();
assert_relative_eq!(si.value(), 1.0e-3, max_relative = 1e-12);
}
#[test]
fn spectral_radiance_cgs_to_si_conversion() {
let cgs = ErgsPerSecondSquareCentimeterSteradianAngstrom::new(1.0);
let si = cgs.to::<WattPerSquareMeterSteradianMeter>();
assert_relative_eq!(si.value(), 1.0e7, max_relative = 1e-12);
let nm = cgs.to::<WattPerSquareMeterSteradianNanometer>();
assert_relative_eq!(nm.value(), 1.0e-2, max_relative = 1e-12);
}
#[test]
fn photon_radiance_unit_conversions() {
let cgs = PhotonsPerSquareCentimeterSecondSteradian::new(1.0);
let si = cgs.to::<PhotonPerSquareMeterSecondSteradian>();
assert_relative_eq!(si.value(), 1.0e4, max_relative = 1e-12);
let ns = PhotonsPerSquareCentimeterNanosecondSteradian::new(1.0);
let s = ns.to::<PhotonPerSquareCentimeterSecondSteradian>();
assert_relative_eq!(s.value(), 1.0e9, max_relative = 1e-12);
}
#[test]
fn erg_to_photon_at_5500_angstrom() {
let lambda = Meters::new(5.5e-7);
let e_rad = ErgsPerSecondSquareCentimeterSteradianAngstrom::new(1.0);
let p_rad = erg_to_photon(e_rad, lambda);
assert_relative_eq!(p_rad.value(), 2.768_764e11, max_relative = 1e-4);
}
#[test]
fn erg_to_photon_matches_legacy_constant_within_tolerance() {
let lambda = Meters::new(1.0e-10);
let e_rad = ErgsPerSecondSquareCentimeterSteradianAngstrom::new(1.0);
let p_rad = erg_to_photon(e_rad, lambda);
let legacy = 5.03e7_f64;
let exact = p_rad.value();
let rel = (exact - legacy).abs() / exact;
assert!(
rel < 1.0e-3,
"exact = {exact}, legacy = {legacy}, rel = {rel}"
);
}
#[test]
fn si_spectral_radiance_to_photon_radiance_matches_legacy_path() {
let lambda = crate::length::Nanometers::new(550.0);
let e_rad = WattsPerSquareMeterSteradianNanometer::new(1.0);
let got = spectral_radiance_to_photon_radiance_ns_nm(e_rad, lambda);
let legacy = 100.0 * 5.03e7 * 5500.0 * 10.0 * 1.0e-9;
let rel = (got.value() - legacy).abs() / got.value();
assert!(
rel < 1.0e-3,
"typed = {}, legacy = {legacy}, rel = {rel}",
got.value()
);
}
#[test]
fn s10_value_passthrough() {
let s = S10s::new(123.0);
assert_eq!(s.value(), 123.0);
}
}