use core::ops::Sub;
use crate::Quantity;
use qtty_derive::Unit;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Magnitude(pub f64);
impl Magnitude {
#[inline]
pub const fn new(v: f64) -> Self {
Self(v)
}
#[inline]
pub fn value(self) -> f64 {
self.0
}
}
impl Sub for Magnitude {
type Output = Magnitude;
#[inline]
fn sub(self, rhs: Magnitude) -> Magnitude {
Magnitude(self.0 - rhs.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct SurfaceBrightness(pub f64);
impl SurfaceBrightness {
#[inline]
pub const fn new(v: f64) -> Self {
Self(v)
}
#[inline]
pub fn value(self) -> f64 {
self.0
}
}
impl Sub for SurfaceBrightness {
type Output = SurfaceBrightness;
#[inline]
fn sub(self, rhs: SurfaceBrightness) -> SurfaceBrightness {
SurfaceBrightness(self.0 - rhs.0)
}
}
#[inline]
pub fn flux_to_magnitude(flux: f64, zero_point: f64) -> Magnitude {
Magnitude(zero_point - 2.5 * flux.log10())
}
#[inline]
pub fn magnitude_to_flux(mag: Magnitude, zero_point: f64) -> f64 {
10_f64.powf((zero_point - mag.0) / 2.5)
}
#[inline]
pub fn band_flux_to_surface_brightness(flux: f64, zero_point: f64) -> SurfaceBrightness {
SurfaceBrightness(zero_point - 2.5 * flux.log10())
}
#[cfg(feature = "radiometry")]
#[inline]
pub fn s10_to_surface_brightness(
flux: crate::units::radiometry::S10s,
zero_point: f64,
) -> SurfaceBrightness {
band_flux_to_surface_brightness(flux.value(), zero_point)
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
#[test]
fn flux_to_mag_unit_flux() {
let m = flux_to_magnitude(1.0, 0.0);
assert!(
(m.value() - 0.0).abs() < 1e-12,
"expected 0, got {}",
m.value()
);
}
#[test]
fn flux_to_mag_hundred_to_one() {
let m = flux_to_magnitude(100.0, 0.0);
assert!(
(m.value() - (-5.0)).abs() < 1e-12,
"expected -5, got {}",
m.value()
);
}
#[test]
fn round_trip_bit_equivalence() {
for &(f, zp) in &[(1.0_f64, 0.0), (42.0, 20.0), (1e-4, 30.0), (1e6, -5.0)] {
let recovered = magnitude_to_flux(flux_to_magnitude(f, zp), zp);
assert!(
(recovered - f).abs() / f < 1e-12,
"round-trip failed: f={f}, zp={zp}, recovered={recovered}"
);
}
}
#[test]
fn nsb_zero_point() {
let m = flux_to_magnitude(1.0, 27.78);
assert!(
(m.value() - 27.78).abs() < 1e-12,
"expected 27.78, got {}",
m.value()
);
}
#[test]
fn magnitude_subtraction() {
let a = Magnitude::new(15.0);
let b = Magnitude::new(10.0);
let diff = a - b;
assert!((diff.value() - 5.0).abs() < 1e-12);
}
#[test]
fn surface_brightness_nsb_zero_point() {
let sb = band_flux_to_surface_brightness(1.0, 27.78);
assert!((sb.value() - 27.78).abs() < 1e-12);
}
#[test]
fn surface_brightness_subtraction() {
let a = SurfaceBrightness::new(22.0);
let b = SurfaceBrightness::new(20.0);
let diff = a - b;
assert!((diff.value() - 2.0).abs() < 1e-12);
}
}
pub use crate::dimension::{Illuminance, LuminousFlux, LuminousIntensity};
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "cd", dimension = LuminousIntensity, ratio = 1.0)]
pub struct Candela;
pub type Cd = Candela;
pub type Candelas = Quantity<Cd>;
pub const CANDELA: Candelas = Candelas::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "lm", dimension = LuminousFlux, ratio = 1.0)]
pub struct Lumen;
pub type Lm = Lumen;
pub type Lumens = Quantity<Lm>;
pub const LUMEN: Lumens = Lumens::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "mlm", dimension = LuminousFlux, ratio = 1e-3)]
pub struct Millilumen;
pub type Millilumens = Quantity<Millilumen>;
pub const MILLILUMEN: Millilumens = Millilumens::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "klm", dimension = LuminousFlux, ratio = 1e3)]
pub struct Kilolumen;
pub type Kilolumens = Quantity<Kilolumen>;
pub const KILOLUMEN: Kilolumens = Kilolumens::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "lx", dimension = Illuminance, ratio = 1.0)]
pub struct Lux;
pub type Lx = Lux;
pub type Luxs = Quantity<Lx>;
pub const LUX: Luxs = Luxs::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "mlx", dimension = Illuminance, ratio = 1e-3)]
pub struct Millilux;
pub type Milliluxs = Quantity<Millilux>;
pub const MILLILUX: Milliluxs = Milliluxs::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "klx", dimension = Illuminance, ratio = 1e3)]
pub struct Kilolux;
pub type Kiloluxs = Quantity<Kilolux>;
pub const KILOLUX: Kiloluxs = Kiloluxs::new(1.0);
#[macro_export]
#[doc(hidden)]
macro_rules! candela_units {
($cb:path) => {
$cb!(Candela);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! lumen_units {
($cb:path) => {
$cb!(Lumen, Millilumen, Kilolumen);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! lux_units {
($cb:path) => {
$cb!(Lux, Millilux, Kilolux);
};
}
candela_units!(crate::impl_unit_from_conversions);
lumen_units!(crate::impl_unit_from_conversions);
lux_units!(crate::impl_unit_from_conversions);
#[cfg(feature = "cross-unit-ops")]
candela_units!(crate::impl_unit_cross_unit_ops);
#[cfg(feature = "cross-unit-ops")]
lumen_units!(crate::impl_unit_cross_unit_ops);
#[cfg(feature = "cross-unit-ops")]
lux_units!(crate::impl_unit_cross_unit_ops);
#[cfg(test)]
candela_units!(crate::assert_units_are_builtin);
#[cfg(test)]
lumen_units!(crate::assert_units_are_builtin);
#[cfg(test)]
lux_units!(crate::assert_units_are_builtin);
#[cfg(all(test, feature = "std"))]
mod photometric_unit_tests {
use super::*;
use approx::assert_abs_diff_eq;
#[test]
fn kilolumen_to_lumen() {
let klm = Kilolumens::new(1.0);
let lm: Lumens = klm.to();
assert_abs_diff_eq!(lm.value(), 1_000.0, epsilon = 1e-9);
}
#[test]
fn millilux_to_lux() {
let mlx = Milliluxs::new(1_000.0);
let lx: Luxs = mlx.to();
assert_abs_diff_eq!(lx.value(), 1.0, epsilon = 1e-12);
}
}