| Title: | Underwater Light Analysis and Visual Ecology |
|---|---|
| Description: | Tools for quantifying and modelling underwater light environments. Propagates spectral irradiance through the water column via Beer-Lambert attenuation, computes photosynthetically active radiation (PAR), and converts between energy and photon-flux units. Provides the 'lux_spectrum' S3 class for unit-safe spectral arithmetic and format readers for TriOS RAMSES and Ocean Optics instruments. The optional 'lightr' package parses further instrument formats into strict native records; explicit measurement, scale, calibration, and resampling policies govern conversion via read_spectrum(). Spectra convert to and from the generic spectral classes of the optional 'photobiology' package, which also serves as the reference implementation luxR's integration and photometry are validated against. Includes visual ecology utilities: Govardovskii (2000) visual-pigment templates, photoreceptor quantum catch, Vorobyev-Osorio colour discrimination (JND), Secchi depth, and explicitly unvalidated contrast-threshold distance scenarios. |
| Authors: | John D Kirwan [aut, cre] |
| Maintainer: | John D Kirwan <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.1.1 |
| Built: | 2026-09-27 10:46:20 UTC |
| Source: | https://github.com/JohnKirwan/luxR |
Tools for quantifying and modelling underwater light environments.
Propagates spectral irradiance through the water column via Beer-Lambert
attenuation, computes PAR, and converts between energy and photon-flux
units. Provides the lux_spectrum S3 class for unit-safe
spectral arithmetic and native format readers for TriOS RAMSES and Ocean
Optics instruments. Optional lightr interoperability preserves native
instrument records until their measurement, scale, calibration, and
resampling policies are explicitly declared. Includes visual ecology
utilities: Govardovskii (2000)
visual-pigment templates, photoreceptor quantum catch,
Vorobyev-Osorio colour discrimination (JND), Secchi depth, and
heuristic horizontal visual-range and object-detection scenarios.
Maintainer: John D Kirwan [email protected]
Authors:
John D Kirwan [email protected]
Useful links:
Report bugs at https://github.com/JohnKirwan/luxR/issues
The negative logarithm of transmittance. Natural-log absorbance
(base = "e") is used by vision scientists and oceanographers;
base-10 absorbance (base = "10") is "optical density" (OD), used by
chemists and filter makers. They differ by a constant: (Johnsen 2012, ch. 4).
absorbance(transmittance, base = c("e", "10"))absorbance(transmittance, base = c("e", "10"))
transmittance |
Transmittance (0-1]. Vectorised. |
base |
|
Absorbance / optical density (dimensionless, ).
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 4.
absorbance(0.01, base = "10") # OD = 2 (1% transmitted) absorbance(exp(-1.5)) # natural-log absorbance = 1.5absorbance(0.01, base = "10") # OD = 2 (1% transmitted) absorbance(exp(-1.5)) # natural-log absorbance = 1.5
. Polarization orientation is
axial (period 180 degrees); the value is returned in degrees in the half-open
range (-90, 90].
angle_of_polarization(Q, U)angle_of_polarization(Q, U)
Q, U
|
Linear polarization Stokes parameters. |
Angle of polarization in degrees, in (-90, 90]. The result is
NA where both Q and U are zero because orientation is
undefined. Scalar inputs are broadcast; incompatible lengths are rejected.
angle_of_polarization(Q = 1, U = 0) # 0 angle_of_polarization(Q = 0, U = 1) # 45angle_of_polarization(Q = 1, U = 0) # 0 angle_of_polarization(Q = 0, U = 1) # 45
Reshapes luxR data into the plain data frame colourvision expects for
an illuminant (I) or reflectance (R1, R2, Rb):
the first column is wavelength and each remaining column is one spectrum.
luxR handles the water (depth propagation, the in-water light field);
colourvision handles the colour-space / visual model
(RNLmodel(), CTTKmodel(), colour spaces). Build the receptor
matrix C with species_sensitivity_matrix.
as_colourvision(x, ...) ## S3 method for class 'lux_spectrum' as_colourvision(x, name = NULL, ...) ## S3 method for class 'list' as_colourvision(x, ...)as_colourvision(x, ...) ## S3 method for class 'lux_spectrum' as_colourvision(x, name = NULL, ...) ## S3 method for class 'list' as_colourvision(x, ...)
x |
A |
... |
Currently unused; present for method consistency. |
name |
Single-spectrum method only: the value column name. Defaults to
the spectrum's |
Unlike as_rspec, this performs no interpolation: colourvision
resamples inputs itself via its nm/interpolate arguments at
model time, so the converter is a pure reshape with no hidden resampling.
colourvision is an optional (Suggests) dependency.
A data.frame with a wl column plus one column per
spectrum, ready to pass as colourvision's I, R1, R2,
or Rb.
species_sensitivity_matrix for the C matrix;
as_rspec for the pavo equivalent; colourvision::RNLmodel
## Not run: I <- as_colourvision(from_solar("clear_noon"), name = "I") ## End(Not run)## Not run: I <- as_colourvision(from_solar("clear_noon"), name = "I") ## End(Not run)
Converts a numeric vector or data frame to a lux_spectrum
object. The data-frame method auto-detects the wavelength column
(lambda > wavelength > wv) and the spectral-value
column (E > irradiance > radiance > value).
Passing an existing lux_spectrum returns it unchanged.
as_lux_spectrum(x, ...) ## S3 method for class 'numeric' as_lux_spectrum( x, lambda, quantity = "irradiance", unit = "W/m2/nm", binwidth = NULL, meta = list(), ... ) ## S3 method for class 'data.frame' as_lux_spectrum( x, lambda_col = NULL, E_col = NULL, quantity = "irradiance", unit = "W/m2/nm", binwidth = NULL, meta = list(), ... ) ## S3 method for class 'lux_spectrum' as_lux_spectrum(x, ...) ## S3 method for class 'lux_instrument_spectrum' as_lux_spectrum( x, quantity = NULL, unit = NULL, calibration = NULL, negative_policy = "error", ... ) ## S3 method for class 'rspec' as_lux_spectrum( x, column = 2, quantity = "irradiance", unit = "W/m2/nm", meta = list(), ... ) ## S3 method for class 'source_spct' as_lux_spectrum( x, measurement = NULL, negative_policy = "error", interpolate = FALSE, ... ) ## S3 method for class 'reflector_spct' as_lux_spectrum(x, negative_policy = "error", interpolate = FALSE, ...)as_lux_spectrum(x, ...) ## S3 method for class 'numeric' as_lux_spectrum( x, lambda, quantity = "irradiance", unit = "W/m2/nm", binwidth = NULL, meta = list(), ... ) ## S3 method for class 'data.frame' as_lux_spectrum( x, lambda_col = NULL, E_col = NULL, quantity = "irradiance", unit = "W/m2/nm", binwidth = NULL, meta = list(), ... ) ## S3 method for class 'lux_spectrum' as_lux_spectrum(x, ...) ## S3 method for class 'lux_instrument_spectrum' as_lux_spectrum( x, quantity = NULL, unit = NULL, calibration = NULL, negative_policy = "error", ... ) ## S3 method for class 'rspec' as_lux_spectrum( x, column = 2, quantity = "irradiance", unit = "W/m2/nm", meta = list(), ... ) ## S3 method for class 'source_spct' as_lux_spectrum( x, measurement = NULL, negative_policy = "error", interpolate = FALSE, ... ) ## S3 method for class 'reflector_spct' as_lux_spectrum(x, negative_policy = "error", interpolate = FALSE, ...)
x |
Object to coerce. |
... |
Additional arguments passed to methods. |
lambda |
Wavelength vector in nm (required for numeric method). |
quantity |
Physical quantity string. Default |
unit |
Unit string from the controlled vocabulary. Default |
binwidth |
Bin width in nm; inferred if |
meta |
Named list of metadata. |
lambda_col |
Column name for wavelengths. Auto-detected from
|
E_col |
Column name for spectral values. Auto-detected from
|
calibration |
For an instrument spectrum, a non-empty calibration identifier or description. |
negative_policy |
For an instrument spectrum, either error on negative processed values or explicitly replace them with zero and record the affected indices. |
column |
For the |
measurement |
For a photobiology |
interpolate |
For a photobiology spectrum, whether an irregular
wavelength grid may be resampled onto integer nanometres. Defaults to
|
A lux_spectrum object.
lux_spectrum, from_naples,
from_solar
# from a numeric vector lam <- seq(400, 700, by = 10) as_lux_spectrum(rep(1, length(lam)), lambda = lam) # from a data frame df <- data.frame(lambda = lam, E = rep(1, length(lam))) as_lux_spectrum(df)# from a numeric vector lam <- seq(400, 700, by = 10) as_lux_spectrum(rep(1, length(lam)), lambda = lam) # from a data frame df <- data.frame(lambda = lam, E = rep(1, length(lam))) as_lux_spectrum(df)
Bridges a reflectance lux_spectrum into photobiology as a
reflector_spct. luxR already constrains reflectance to [0, 1],
so values map onto Rfr directly.
as_reflector_spct(x, ...) ## S3 method for class 'lux_spectrum' as_reflector_spct(x, Rfr.type = NULL, strict.range = TRUE, ...) ## S3 method for class 'list' as_reflector_spct(x, ...)as_reflector_spct(x, ...) ## S3 method for class 'lux_spectrum' as_reflector_spct(x, Rfr.type = NULL, strict.range = TRUE, ...) ## S3 method for class 'list' as_reflector_spct(x, ...)
x |
A reflectance |
... |
Passed to |
Rfr.type |
Either |
strict.range |
Passed to |
Rfr.type is required. Whether a reflectance is total or specular is a
fact about how it was measured, which luxR does not otherwise carry;
defaulting it would silently mislabel the measurement inside another
package's ecosystem. Record it once in the spectrum's
meta$Rfr_type to have it picked up automatically.
A photobiology reflector_spct.
as_source_spct, as_lux_spectrum
## Not run: r <- as_reflector_spct(my_reflectance, Rfr.type = "total") ## End(Not run)## Not run: r <- as_reflector_spct(my_reflectance, Rfr.type = "total") ## End(Not run)
rspec objectBridges luxR data into the pavo ecosystem so pavo's colour-space and
visual-modelling tools (vismodel(), coldist(),
colspace(), plotting) can be applied to luxR-derived spectra. The
division of labour: luxR handles the water (depth propagation, inherent
optical properties, the in-water light field); pavo handles the colour space.
as_rspec(x, ...) ## S3 method for class 'lux_spectrum' as_rspec(x, name = NULL, ...) ## S3 method for class 'list' as_rspec(x, ...)as_rspec(x, ...) ## S3 method for class 'lux_spectrum' as_rspec(x, name = NULL, ...) ## S3 method for class 'list' as_rspec(x, ...)
x |
A |
... |
Passed to |
name |
Single-spectrum method only: the column name to give the spectrum
in the |
Delegates to pavo::as.rspec(), which validates the input and, by
default, interpolates onto a 1 nm grid, so the result is a genuine
rspec object. pavo is an optional (Suggests) dependency.
A pavo rspec data frame: a wl column plus one
column per spectrum.
as_lux_spectrum for the reverse conversion;
pavo::as.rspec, pavo::vismodel
## Not run: # A bundled solar spectrum as an rspec, ready for the pavo toolkit: r <- as_rspec(from_solar("clear_noon")) # Several spectra at once (e.g. TriOS radiance scans): r2 <- as_rspec(from_trios(system.file("extdata/trios.dat", package = "luxR"))) ## End(Not run)## Not run: # A bundled solar spectrum as an rspec, ready for the pavo toolkit: r <- as_rspec(from_solar("clear_noon")) # Several spectra at once (e.g. TriOS radiance scans): r2 <- as_rspec(from_trios(system.file("extdata/trios.dat", package = "luxR"))) ## End(Not run)
Bridges luxR data into the photobiology ecosystem so its generic spectral classes, integration, and photometry can be applied to luxR-derived spectra. The division of labour: luxR handles the water (depth propagation, inherent optical properties, the in-water light field); photobiology handles generic spectral computation.
as_source_spct(x, ...) ## S3 method for class 'lux_spectrum' as_source_spct(x, strict.range = TRUE, ...) ## S3 method for class 'list' as_source_spct(x, ...)as_source_spct(x, ...) ## S3 method for class 'lux_spectrum' as_source_spct(x, strict.range = TRUE, ...) ## S3 method for class 'list' as_source_spct(x, ...)
x |
A |
... |
Passed to the photobiology constructor. |
strict.range |
Passed to the photobiology constructor. Defaults to
|
as_source_spct() requires spectral irradiance and delegates to
photobiology::source_spct(). Energy units become s.e.irrad and
photon units become s.q.irrad, rescaled to photobiology's
mol/m2/s/nm. The spectrum is not converted between energy and
photon bases: it arrives in the basis it already had, and
photobiology::e2q() or q2e() does any conversion, so only one
implementation of that conversion is ever in play.
Radiance is refused, because source_spct has no per-steradian term.
photobiology is an optional (Suggests) dependency.
A photobiology source_spct.
as_reflector_spct for reflectance,
as_lux_spectrum for the reverse conversion,
as_rspec for the pavo bridge
## Not run: # The downwelling light field at 15 m, as a photobiology source_spct: # Jerlov Kd data cover 350-700 nm, so trim before propagating. surface <- from_solar("clear_noon")[350, 700] at_depth <- propagate_spectrum(surface, jerlov_Kd("II", surface$lambda), from = 0, to = 15) s <- as_source_spct(at_depth[["15"]]) photobiology::e_irrad(s) ## End(Not run)## Not run: # The downwelling light field at 15 m, as a photobiology source_spct: # Jerlov Kd data cover 350-700 nm, so trim before propagating. surface <- from_solar("clear_noon")[350, 700] at_depth <- propagate_spectrum(surface, jerlov_Kd("II", surface$lambda), from = 0, to = 15) s <- as_source_spct(at_depth[["15"]]) photobiology::e_irrad(s) ## End(Not run)
Computes E(z) = E0 * exp(-Kd * z). Vectorised over depth.
attenuate_depth(E0, Kd, depth)attenuate_depth(E0, Kd, depth)
E0 |
Surface irradiance. Any unit; result is in the same unit. |
Kd |
Diffuse attenuation coefficient in 1/m. Must be >= 0. |
depth |
Depth(s) in metres. Must be >= 0. Scalar or vector. |
This is the package's principal lightweight water-column model. It
assumes that is constant over the modeled path and propagates
each wavelength independently. It does not solve the angular radiance
field, multiple scattering, or wavelength redistribution. The scalar API
has no inherent wavelength or depth range; the caller is responsible for
supplying a that is valid for the medium and path.
Numeric vector the same length as depth.
attenuate_depth(100, Kd = 0.06, depth = c(0, 10, 25, 50))attenuate_depth(100, Kd = 0.06, depth = c(0, 10, 25, 50))
Applies per-wavelength Kd across a depth grid using the Beer-Lambert law. Returns a tidy data frame, a numeric matrix, or (for lux_spectrum input) a named list of lux_spectrum objects.
attenuate_spectrum(E0_lambda, ...) ## S3 method for class 'numeric' attenuate_spectrum( E0_lambda, Kd_lambda, depths, format = c("long", "matrix"), lambda = NULL, ... ) ## S3 method for class 'lux_spectrum' attenuate_spectrum(E0_lambda, Kd_lambda, depths, ...)attenuate_spectrum(E0_lambda, ...) ## S3 method for class 'numeric' attenuate_spectrum( E0_lambda, Kd_lambda, depths, format = c("long", "matrix"), lambda = NULL, ... ) ## S3 method for class 'lux_spectrum' attenuate_spectrum(E0_lambda, Kd_lambda, depths, ...)
E0_lambda |
Surface spectral irradiance. Length-N numeric vector or
a |
... |
Ignored. |
Kd_lambda |
Finite, non-negative diffuse attenuation coefficients
(1/m) at the same wavelength bins as |
depths |
Finite, non-negative depths to evaluate in metres. Length-M numeric vector. |
format |
|
lambda |
Optional wavelength values (nm) for the |
This is a wavelength-resolved Beer-Lambert calculation, not a
radiative-transfer solution. It assumes a homogeneous
over the modeled path, treats wavelengths independently, and accepts only
irradiance when a lux_spectrum is supplied. The generic numeric API
cannot verify physical quantity or wavelength alignment; callers must
supply aligned irradiance and vectors. Bundled Jerlov
coefficients are supported only from 350–700 nm; see
jerlov_Kd.
A data frame, matrix, or named list of lux_spectrum.
Kirk JTO (1994) Light and Photosynthesis in Aquatic Ecosystems, 2nd edn. Cambridge University Press.
propagate_spectrum, band_irradiance,
jerlov_Kd
E0 <- c(10, 20, 30) Kd <- c(0.01, 0.05, 0.20) lam <- c(450, 550, 650) attenuate_spectrum(E0, Kd, depths = c(0, 10, 25), lambda = lam)E0 <- c(10, 20, 30) Kd <- c(0.01, 0.05, 0.20) lam <- c(450, 550, 650) attenuate_spectrum(E0, Kd, depths = c(0, 10, 25), lambda = lam)
Propagates a spectrum from a known depth to one or more target depths via
Beer-Lambert attenuation and integrates the result over a wavelength band.
Works in both directions: set from > depths to recover
shallower values (including the surface) from a deeper measurement.
band_irradiance(E_lambda, ...) ## S3 method for class 'numeric' band_irradiance( E_lambda, Kd_lambda, lambda, depths, from = 0, lambda_min = NULL, lambda_max = NULL, photonic = FALSE, molar_unit = "umol", binwidth = NULL, allow_above_surface = FALSE, ... ) ## S3 method for class 'lux_spectrum' band_irradiance(E_lambda, Kd_lambda, depths, lambda = NULL, ...)band_irradiance(E_lambda, ...) ## S3 method for class 'numeric' band_irradiance( E_lambda, Kd_lambda, lambda, depths, from = 0, lambda_min = NULL, lambda_max = NULL, photonic = FALSE, molar_unit = "umol", binwidth = NULL, allow_above_surface = FALSE, ... ) ## S3 method for class 'lux_spectrum' band_irradiance(E_lambda, Kd_lambda, depths, lambda = NULL, ...)
E_lambda |
Spectral irradiance ( |
... |
Ignored. |
Kd_lambda |
Diffuse attenuation coefficients (1/m) at the same wavelengths. Length-N vector. |
lambda |
Wavelengths in nm. Length-N vector (not needed when
|
depths |
Target depth(s) in metres. Scalar or vector. |
from |
Depth of the known spectrum in metres. Default |
lambda_min |
Lower wavelength bound in nm (inclusive). Defaults to
|
lambda_max |
Upper wavelength bound in nm (inclusive). Defaults to
|
photonic |
Logical; if |
molar_unit |
Molar unit for photonic output. Default |
binwidth |
Wavelength bin width in nm. Inferred from |
allow_above_surface |
Logical; permits target depths above the surface
(negative values). Default |
Two operations are applied in sequence. First, Beer-Lambert propagation at each wavelength:
where is the depth of the known spectrum (from) and
is the diffuse attenuation coefficient. Second, the
propagated spectrum is integrated over the specified wavelength window
[, ]:
approximated as over bins within
the window. See propagate_spectrum for the full Beer-Lambert
documentation.
A data.frame with columns:
Target depth in metres.
Band-integrated irradiance in (or mol m s
when photonic = TRUE).
propagate_spectrum, par_irradiance
sp <- solar_irradiance("clear_noon") keep <- sp$wavelength >= 350 & sp$wavelength <= 700 lam <- sp$wavelength[keep] E0 <- sp$irradiance[keep] Kd <- jerlov_Kd("II", lam) # surface to depth profile, 500-600 nm band band_irradiance(E0, Kd, lam, depths = c(0, 5, 10, 20, 50), lambda_min = 500, lambda_max = 600) # recover surface from a 10 m measurement E_10m <- E0 * exp(-Kd * 10) band_irradiance(E_10m, Kd, lam, depths = 0, from = 10)sp <- solar_irradiance("clear_noon") keep <- sp$wavelength >= 350 & sp$wavelength <= 700 lam <- sp$wavelength[keep] E0 <- sp$irradiance[keep] Kd <- jerlov_Kd("II", lam) # surface to depth profile, 500-600 nm band band_irradiance(E0, Kd, lam, depths = c(0, 5, 10, 20, 50), lambda_min = 500, lambda_max = 600) # recover surface from a 10 m measurement E_10m <- E0 * exp(-Kd * 10) band_irradiance(E_10m, Kd, lam, depths = 0, from = 10)
The beam (total) attenuation coefficient is the sum of the absorption and
scattering coefficients, (Johnsen 2012, ch. 5). It governs
how a collimated beam — and the contrast of a sighted object — fades with
distance, and is always larger than the diffuse attenuation .
beam_attenuation(absorption, scattering)beam_attenuation(absorption, scattering)
absorption |
Absorption coefficient |
scattering |
Scattering coefficient |
Beam attenuation coefficient (1/m).
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 5.
single_scattering_albedo, transmittance,
visual_range
beam_attenuation(absorption = 0.1, scattering = 0.05) # 0.15beam_attenuation(absorption = 0.1, scattering = 0.05) # 0.15
Given a single measured value and a reference spectral shape, returns a
rescaled spectrum whose forward operator reproduces the input. The shape
assumption must be provided by the caller (e.g. from
solar_irradiance()).
broadband2spectrum( value, unit = c("lx", "W", "kW", "umol", "radiance_W", "cd"), spectrum, water_type = NULL, depth = 0, wavelength_policy = c("error", "trim", "constant") )broadband2spectrum( value, unit = c("lx", "W", "kW", "umol", "radiance_W", "cd"), spectrum, water_type = NULL, depth = 0, wavelength_policy = c("error", "trim", "constant") )
value |
The measured scalar (e.g. 18200 lux, 480 W, 1.2 kW). |
unit |
One of |
spectrum |
Reference spectral shape as a data frame with columns
|
water_type |
Optional Jerlov water type string (e.g. |
depth |
Depth in metres at which the measurement was taken.
Only used when |
wavelength_policy |
Policy for wavelengths outside the bundled Jerlov
domain when attenuation is requested: |
Supported units and their forward operators:
"lx"photopic integral via irradiance2lux()
"W"broadband integral
"kW"same as "W" divided by 1000
"umol"PAR-band photon flux (400–700 nm) in
"radiance_W"Lambertian radiance: , then integrate
"cd"photometric radiance:
A data frame with columns wavelength and irradiance,
with a luxR.assumption attribute recording the provenance.
lam <- seq(400, 700, by = 10) spec <- data.frame(wavelength = lam, irradiance = rep(1, length(lam))) broadband2spectrum(480, unit = "W", spectrum = spec)lam <- seq(400, 700, by = 10) spec <- data.frame(wavelength = lam, irradiance = rep(1, length(lam))) broadband2spectrum(480, unit = "W", spectrum = spec)
The CIE scotopic luminous efficiency function V'(lambda) for night vision,
from CIE 018:2019 Table 2. Tabulated at 1 nm intervals from 380 nm to
780 nm, with its peak at 507 nm. Used by scotopic_lux.
CIE_scotopicCIE_scotopic
A data frame with 401 rows and 2 columns:
Wavelength in standard air, in nm (380-780, step 1).
V'(lambda), dimensionless sensitivity from 0 to 1.
Generated by data-raw/build_cie_efficiency_data.R from the
checksum-pinned CIE CSV and companion metadata. Source URLs, DOI,
checksums, retrieval date, standard, and license are stored as data-frame
attributes under "luxR.provenance".
International Commission on Illumination (CIE), CIE spectral luminous efficiency for scotopic vision, DOI: doi:10.25039/CIE.DS.gr6w4b5g, licensed CC BY-SA 4.0.
The CIE 1931 photopic luminous efficiency function V(lambda), also called the standard observer luminosity curve, from CIE 018:2019 Table 1. Tabulated at 1 nm intervals from 360 nm to 830 nm, with its peak at 555 nm.
CIE1931CIE1931
A data frame with 471 rows and 2 columns:
Wavelength in standard air, in nm (360-830, step 1).
V(lambda), dimensionless sensitivity from 0 to 1.
Generated by data-raw/build_cie_efficiency_data.R from the
checksum-pinned CIE CSV and companion metadata. Source URLs, DOI,
checksums, retrieval date, standard, and license are stored as data-frame
attributes under "luxR.provenance".
International Commission on Illumination (CIE), CIE spectral luminous efficiency for photopic vision, DOI: doi:10.25039/CIE.DS.dktna2s3, licensed CC BY-SA 4.0.
Computes the just-noticeable difference (JND) between two spectral stimuli
as seen by a named species, using the receptor-noise-limited colour
discrimination model of Vorobyev & Osorio (1998). A
indicates the pair is not reliably discriminable;
indicates discrimination is possible.
colour_jnd( stim1, stim2, lambda, species, receptor = NULL, noise = 0.05, binwidth = NULL )colour_jnd( stim1, stim2, lambda, species, receptor = NULL, noise = 0.05, binwidth = NULL )
stim1, stim2
|
Numeric vectors of spectral irradiance (W m |
lambda |
Wavelength vector in nm. |
species |
Species name matching |
receptor |
Character vector of receptor class(es) to include (e.g.
|
noise |
Weber fraction(s). Either a single value applied to all receptors, or a named numeric vector with one entry per receptor class. Default 0.05 (5% noise), typical for cones in well-lit conditions. |
binwidth |
Wavelength bin width in nm. Inferred from |
For each receptor type , the log-ratio of quantum catches
is computed, then the chromatic
distance is:
where and is the noise
(Weber fraction) of receptor . The formula is general for any
number of receptor types .
Scalar: chromatic distance in just-noticeable
differences (JNDs).
Vorobyev M, Osorio D (1998) Receptor noise as a determinant of colour thresholds. Proc. R. Soc. B 265:351–358.
Vorobyev M et al. (2001) Colour thresholds and receptor noise: behaviour and physiology compared. Vision Res. 41:639–653.
quantum_catch, species_brightness,
species_LEF
sp1 <- solar_irradiance("clear_noon") sp2 <- solar_irradiance("underwater_10m") # Human trichromat colour_jnd(sp1$irradiance, sp2$irradiance, lambda = sp1$wavelength, species = "Homo sapiens", receptor = c("L-cone", "M-cone", "S-cone")) # Zebrafish tetrachromat (all cones) colour_jnd(sp1$irradiance, sp2$irradiance, lambda = sp1$wavelength, species = "Danio rerio")sp1 <- solar_irradiance("clear_noon") sp2 <- solar_irradiance("underwater_10m") # Human trichromat colour_jnd(sp1$irradiance, sp2$irradiance, lambda = sp1$wavelength, species = "Homo sapiens", receptor = c("L-cone", "M-cone", "S-cone")) # Zebrafish tetrachromat (all cones) colour_jnd(sp1$irradiance, sp2$irradiance, lambda = sp1$wavelength, species = "Danio rerio")
Attenuates an inherent contrast over a horizontal (or vertical) viewing path
as a teaching/scenario heuristic:
(Johnsen 2012, ch. 5). The beam
attenuation is approximated as Kd * kd_to_c; looking up uses
and looking down .
contrast_at_distance( C0, distance, Kd, kd_to_c = 1.5, direction = c("horizontal", "up", "down"), beam_c = NULL )contrast_at_distance( C0, distance, Kd, kd_to_c = 1.5, direction = c("horizontal", "up", "down"), beam_c = NULL )
C0 |
Inherent (zero-distance) contrast (achromatic Weber contrast
or chromatic |
distance |
Finite, non-negative viewing distance(s) in metres. Scalar or vector. |
Kd |
Diffuse attenuation coefficient at the sighting wavelength (1/m). |
kd_to_c |
Ratio |
direction |
Viewing direction: |
beam_c |
Optional measured beam attenuation |
This scalar calculation does not propagate spectra or recompute receptor
catches. In particular, applying it to a chromatic JND is not a validated
chromatic-vision model; use detection_range(model = "spectral_path")
for wavelength-resolved propagation.
Heuristic contrast remaining at each distance.
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 5.
inherent_contrast, detection_range
contrast_at_distance(C0 = 1, distance = c(0, 5, 10), Kd = 0.06)contrast_at_distance(C0 = 1, distance = c(0, 5, 10), Kd = 0.06)
Converts the spectral values in a lux_spectrum between energy and
photon-flux units, or between mW and W for radiance spectra. Supported
conversions: "W/m2/nm"
"umol/m2/s/nm" / "mmol/m2/s/nm" / "mol/m2/s/nm",
and the corresponding "/sr" photonic units. Energy radiance units
"W/m2/sr/nm" and "mW/m2/sr/nm" are also interchangeable.
convert_unit(x, to, ...)convert_unit(x, to, ...)
x |
A |
to |
Target unit string from the controlled vocabulary. |
... |
Ignored. |
A new lux_spectrum with updated unit and E.
lam <- seq(400, 700, by = 10) x_W <- lux_spectrum(rep(1, length(lam)), lam, "irradiance", "W/m2/nm") x_mol <- convert_unit(x_W, "umol/m2/s/nm") x_mol$unitlam <- seq(400, 700, by = 10) x_W <- lux_spectrum(rep(1, length(lam)), lam, "irradiance", "W/m2/nm") x_mol <- convert_unit(x_W, "umol/m2/s/nm") x_mol$unit
. With V = 0 (the default, and
the usual case for the underwater light field, which is essentially linearly
polarized) this is the degree of linear polarization.
degree_of_polarization(I, Q, U, V = 0)degree_of_polarization(I, Q, U, V = 0)
I |
Total radiance (Stokes I). Must be > 0. |
Q, U
|
Linear polarization Stokes parameters. |
V |
Circular polarization Stokes parameter. Default 0. |
All inputs must be finite numeric vectors. A Stokes vector is
physical only when and
. Values exceeding this bound by more
than 64 machine epsilons are rejected with a
luxR_polarization_physicality_error. Values within that round-off
tolerance are returned as exactly one.
Degree of polarization in [0, 1]. Vectorised. Scalar inputs
are broadcast; incompatible non-scalar lengths are rejected.
angle_of_polarization, underwater_polarization
degree_of_polarization(I = 1, Q = 0.4, U = 0) # 0.4degree_of_polarization(I = 1, Q = 0.4, U = 0) # 0.4
One-stop wrapper that bundles inherent_contrast,
detection_range, and a contrast-vs-distance curve into a single
lux_detection object with print and plot methods.
detectability( object, background, illuminant, lambda, Kd, kd_to_c = 1.5, species = NULL, receptor = NULL, channel = c("both", "achromatic", "chromatic"), contrast_threshold = 0.02, jnd_threshold = 1, direction = c("horizontal", "up", "down"), noise = 0.05, distances = NULL, binwidth = NULL, beam_c = NULL, model = c("scalar_heuristic", "spectral_path"), veiling = NULL, max_distance = 1000, search_points = 1001L, geometry = c("lambertian", "scalar", "collimated", "custom"), solid_angle = NULL )detectability( object, background, illuminant, lambda, Kd, kd_to_c = 1.5, species = NULL, receptor = NULL, channel = c("both", "achromatic", "chromatic"), contrast_threshold = 0.02, jnd_threshold = 1, direction = c("horizontal", "up", "down"), noise = 0.05, distances = NULL, binwidth = NULL, beam_c = NULL, model = c("scalar_heuristic", "spectral_path"), veiling = NULL, max_distance = 1000, search_points = 1001L, geometry = c("lambertian", "scalar", "collimated", "custom"), solid_angle = NULL )
object, background
|
Reflectance spectra (numeric vectors on the
|
illuminant |
The in-water light field illuminating both (numeric vector
or irradiance |
lambda |
Finite, positive, strictly increasing, regularly spaced wavelength grid in nm. |
Kd |
Positive diffuse attenuation coefficient (1/m). The scalar
heuristic requires one value at the sighting wavelength, e.g.
|
kd_to_c |
Ratio |
species |
Species name in |
receptor |
Receptor class(es) to include. Default |
channel |
|
contrast_threshold |
Achromatic Weber-contrast threshold. Default 0.02. |
jnd_threshold |
Chromatic threshold in JNDs. Default 1. |
direction |
Viewing direction: |
noise |
Receptor Weber fraction(s) for the chromatic channel; passed
to |
distances |
Optional distance grid (m) for the contrast-vs-distance curve. Default spans 0 to 1.2x the largest detection range. |
binwidth |
Bin width in nm; inferred from |
beam_c |
Optional measured beam attenuation |
model |
|
veiling |
Veiling/path radiance spectrum on the |
max_distance |
Finite positive maximum search distance in metres. A criterion still detectable there is reported as censored, not infinite. |
search_points |
Integer number of distances used to bracket the furthest downward threshold crossing in the spectral-path model. Default 1001. |
geometry |
Irradiance-to-radiance geometry used by the spectral-path
model: |
solid_angle |
Solid angle in steradians when
|
An object of class lux_detection: a list with inherent
(zero-distance contrast), range (scenario estimate per channel),
curve (data frame of contrast vs distance), status metadata, and
the call settings.
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 5.
inherent_contrast, detection_range,
contrast_at_distance
sp <- solar_irradiance("clear_noon") lam <- sp$wavelength grey <- rep(0.3, length(lam)) red <- 0.2 + 0.25 * exp(-((lam - 625) / 35)^2) d <- detectability(red, grey, sp$irradiance, lam, Kd = jerlov_Kd("IA", lambda = 490), species = "Danio rerio") d ## Not run: plot(d)sp <- solar_irradiance("clear_noon") lam <- sp$wavelength grey <- rep(0.3, length(lam)) red <- 0.2 + 0.25 * exp(-((lam - 625) / 35)^2) d <- detectability(red, grey, sp$irradiance, lam, Kd = jerlov_Kd("IA", lambda = 490), species = "Danio rerio") d ## Not run: plot(d)
Generalises visual_range by using the estimated inherent
contrast of an object against its background (from
inherent_contrast) rather than assuming a contrast of 1. The
default scalar heuristic uses
. The spectral-path
model instead propagates object, background, and veiling radiances at every
wavelength and recomputes receptor catches at each evaluated distance.
detection_range( object, background, illuminant, lambda, Kd, kd_to_c = 1.5, species = NULL, receptor = NULL, channel = c("both", "achromatic", "chromatic"), contrast_threshold = 0.02, jnd_threshold = 1, direction = c("horizontal", "up", "down"), noise = 0.05, binwidth = NULL, beam_c = NULL, model = c("scalar_heuristic", "spectral_path"), veiling = NULL, max_distance = 1000, search_points = 1001L, geometry = c("lambertian", "scalar", "collimated", "custom"), solid_angle = NULL )detection_range( object, background, illuminant, lambda, Kd, kd_to_c = 1.5, species = NULL, receptor = NULL, channel = c("both", "achromatic", "chromatic"), contrast_threshold = 0.02, jnd_threshold = 1, direction = c("horizontal", "up", "down"), noise = 0.05, binwidth = NULL, beam_c = NULL, model = c("scalar_heuristic", "spectral_path"), veiling = NULL, max_distance = 1000, search_points = 1001L, geometry = c("lambertian", "scalar", "collimated", "custom"), solid_angle = NULL )
object, background
|
Reflectance spectra (numeric vectors on the
|
illuminant |
The in-water light field illuminating both (numeric vector
or irradiance |
lambda |
Finite, positive, strictly increasing, regularly spaced wavelength grid in nm. |
Kd |
Positive diffuse attenuation coefficient (1/m). The scalar
heuristic requires one value at the sighting wavelength, e.g.
|
kd_to_c |
Ratio |
species |
Species name in |
receptor |
Receptor class(es) to include. Default |
channel |
|
contrast_threshold |
Achromatic Weber-contrast threshold. Default 0.02. |
jnd_threshold |
Chromatic threshold in JNDs. Default 1. |
direction |
Viewing direction: |
noise |
Receptor Weber fraction(s) for the chromatic channel; passed
to |
binwidth |
Bin width in nm; inferred from |
beam_c |
Optional measured beam attenuation |
model |
|
veiling |
Veiling/path radiance spectrum on the |
max_distance |
Finite positive maximum search distance in metres. A criterion still detectable there is reported as censored, not infinite. |
search_points |
Integer number of distances used to bracket the furthest downward threshold crossing in the spectral-path model. Default 1001. |
geometry |
Irradiance-to-radiance geometry used by the spectral-path
model: |
solid_angle |
Solid angle in steradians when
|
Neither model is an empirically validated prediction of actual organismal detection distance. They omit target angular size, acuity, photon limitation, adaptation changes, behaviour, and observation probability.
The spectral-path scenario treats object and background as coplanar stimuli sharing one water path and one asymptotic veiling radiance. For each stimulus it evaluates
Both stimuli therefore converge toward the supplied veiling spectrum. This is
a lightweight single-path model, not a multiple-scattering radiative-transfer
solution. Because wavelength-dependent signals can be non-monotonic, the
reported spectral distance is the furthest downward threshold crossing
bracketed by search_points; callers should check resolution sensitivity.
A list with elements achromatic and chromatic giving
scenario estimates in metres. A value is 0 when no evaluated distance is
detectable, NA when the channel was not requested or remained
detectable through max_distance. Attributes record per-channel
status, model, validation state, and maximum distance.
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 5.
inherent_contrast, contrast_at_distance,
visual_range
sp <- solar_irradiance("clear_noon") lam <- sp$wavelength grey <- rep(0.3, length(lam)) red <- 0.2 + 0.25 * exp(-((lam - 625) / 35)^2) Kd <- jerlov_Kd("IA", lambda = 490) detection_range(red, grey, sp$irradiance, lam, Kd = Kd, species = "Danio rerio")sp <- solar_irradiance("clear_noon") lam <- sp$wavelength grey <- rep(0.3, length(lam)) red <- 0.2 + 0.25 * exp(-((lam - 625) / 35)^2) Kd <- jerlov_Kd("IA", lambda = 490) detection_range(red, grey, sp$irradiance, lam, Kd = Kd, species = "Danio rerio")
Closed-form Beer-Lambert inversion: Kd = log(E1/E2) / (z2 - z1).
Works for scalar or spectral (vector) inputs, or lux_spectrum objects.
fit_Kd(E1, ...) ## S3 method for class 'numeric' fit_Kd(E1, z1, E2, z2, ...) ## S3 method for class 'lux_spectrum' fit_Kd(E1, z1, E2, z2, ...)fit_Kd(E1, ...) ## S3 method for class 'numeric' fit_Kd(E1, z1, E2, z2, ...) ## S3 method for class 'lux_spectrum' fit_Kd(E1, z1, E2, z2, ...)
E1 |
Irradiance at the shallower depth |
... |
Ignored. |
z1 |
Depth of E1 in metres. Must be < z2. |
E2 |
Irradiance at the deeper depth |
z2 |
Depth of E2 in metres. Must be > z1. |
Kd in 1/m. Same length as E1/E2.
Kirk JTO (1994) Light and Photosynthesis in Aquatic Ecosystems, 2nd edn. Cambridge University Press.
propagate_spectrum, attenuate_spectrum,
jerlov_Kd
data(Naples) fit_Kd(sum(Naples$depth_0m), 0, sum(Naples$depth_10m), 10)data(Naples) fit_Kd(sum(Naples$depth_0m), 0, sum(Naples$depth_10m), 10)
Finds the peak wavelength () that minimises the sum of
squared residuals between the Govardovskii (2000) template and the supplied
sensitivity measurements. Uses optimize for an exact
single-parameter search, so the result is a continuous estimate rather than
a grid point.
fit_sensitivity( lambda, sensitivity, chromophore = c("A1", "A2"), lambda_max_range = c(300, 700) )fit_sensitivity( lambda, sensitivity, chromophore = c("A1", "A2"), lambda_max_range = c(300, 700) )
lambda |
Wavelengths at which sensitivity was measured (nm). |
sensitivity |
Measured spectral sensitivity. Normalised to
|
chromophore |
|
lambda_max_range |
Length-2 numeric vector giving the search interval
for |
A list with components:
lambda_maxBest-fit peak wavelength (nm).
chromophoreChromophore used.
SSResidual sum of squares at the optimum.
fittedData frame with columns lambda and S
giving the template evaluated on the input wavelength grid.
Govardovskii VI, Fyhrquist N, Reuter T, Kuzmin DG, Donner K (2000) In search of the visual pigment template. Visual Neuroscience 17:509–528.
lam <- seq(400, 700, by = 10) true <- govardovskii_template(lam, lambda_max = 530)$S noisy <- pmax(0, true + rnorm(length(true), 0, 0.02)) fit <- fit_sensitivity(lam, noisy) fit$lambda_max # should be close to 530lam <- seq(400, 700, by = 10) true <- govardovskii_template(lam, lambda_max = 530)$S noisy <- pmax(0, true + rnorm(length(true), 0, 0.02)) fit <- fit_sensitivity(lam, noisy) fit$lambda_max # should be close to 530
Unpolarised Fresnel reflectance for light crossing a flat air-water boundary,
as a function of incidence angle (Johnsen 2012, ch. 5). At normal incidence
about 2% of downwelling light is reflected ().
Going from water to air, total internal reflection () occurs beyond
the critical angle (the edge of snells_window).
fresnel_reflectance( angle = 0, n = 1.333, from = c("air", "water"), component = c("unpolarized", "s", "p") )fresnel_reflectance( angle = 0, n = 1.333, from = c("air", "water"), component = c("unpolarized", "s", "p") )
angle |
Angle of incidence from the surface normal, in degrees (0-90). Vectorised. |
n |
Refractive index of water relative to air. Default 1.333. |
from |
Incident medium: |
component |
Which polarization component to return: |
Reflectance (0-1); the transmitted fraction is .
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 5.
snells_window, underwater_polarization
fresnel_reflectance(0) # ~0.02 at normal incidence fresnel_reflectance(60, from = "water") # total internal reflection -> 1 fresnel_reflectance(53.1, component = "p") # ~0 near Brewster's anglefresnel_reflectance(0) # ~0.02 at normal incidence fresnel_reflectance(60, from = "water") # total internal reflection -> 1 fresnel_reflectance(53.1, component = "p") # ~0 near Brewster's angle
Load a Naples depth spectrum as a lux_spectrum
from_naples(depth = c("0m", "5m", "10m"))from_naples(depth = c("0m", "5m", "10m"))
depth |
One of |
A lux_spectrum with unit "umol/m2/s/nm".
x <- from_naples("0m") print(x)x <- from_naples("0m") print(x)
Ocean Optics text files do not contain a standardized machine-readable unit
or calibration state. All physical declarations are therefore required.
Use read_ocean_optics to inspect raw or relative values.
from_ocean_optics(path, quantity = NULL, unit = NULL, calibration = NULL)from_ocean_optics(path, quantity = NULL, unit = NULL, calibration = NULL)
path |
Path to the Ocean Optics text file. |
quantity |
Explicit physical quantity represented by the values. |
unit |
Explicit unit compatible with |
calibration |
Non-empty calibration identifier or description. |
A validated lux_spectrum with import provenance in metadata.
read_ocean_optics, from_trios,
read_spectrum
Load a reference solar spectrum as a lux_spectrum
from_solar(condition = "clear_noon")from_solar(condition = "clear_noon")
condition |
One of |
A lux_spectrum with unit "W/m2/nm".
solar_irradiance, as_lux_spectrum
x <- from_solar("clear_noon") print(x)x <- from_solar("clear_noon") print(x)
Wraps read_trios. The low-level reader preserves negative
instrument values. Construction of non-negative physical radiance spectra
therefore either fails (the default) or requires an explicit, recorded
zero-floor preprocessing policy.
from_trios(path, negative_policy = c("error", "zero"))from_trios(path, negative_policy = c("error", "zero"))
path |
Path to a TriOS RAMSES |
negative_policy |
Either |
A named list of radiance lux_spectrum objects.
Vectorised; passes through NA.
ft2m(z)ft2m(z)
z |
Depth or distance in feet. |
Numeric vector in metres.
propagate_depth(8260, Kd = 0.062, from = ft2m(72), to = 0)propagate_depth(8260, Kd = 0.062, from = ft2m(72), to = 0)
Reconstructs a photoreceptor's normalised absorbance spectrum from the peak wavelength alone, using the parametric template of Govardovskii et al. (2000) for A1 and A2 chromophores. This is the standard workhorse of comparative visual physiology.
govardovskii_template(lambda, lambda_max, chromophore = c("A1", "A2"))govardovskii_template(lambda, lambda_max, chromophore = c("A1", "A2"))
lambda |
Wavelength grid in nm (numeric vector). |
lambda_max |
Peak absorbance wavelength in nm. |
chromophore |
Chromophore type: |
The template models absorbance as the sum of an -band (main peak)
and a -band (short-wavelength shoulder). The -band
follows the four-parameter expression:
where , , , , and are chromophore-specific
constants (A1: , , , ,
wavelength-dependent; A2: , ,
, ). The -band is a Gaussian:
with and
. The full template is
, normalised to a peak of 1.
A data frame with columns lambda and S (normalised
absorbance, 0–1).
Govardovskii VI, Fyhrquist N, Reuter T, Kuzmin DG, Donner K (2000) In search of the visual pigment template. Visual Neuroscience 17:509–528.
lam <- seq(300, 750, by = 1) govardovskii_template(lam, lambda_max = 560)lam <- seq(300, 750, by = 1) govardovskii_template(lam, lambda_max = 560)
Computes the contrast a viewer perceives between an object and its background
at zero viewing distance, from their reflectance spectra and the in-water
illuminant. Two channels are available: an achromatic (brightness)
Weber contrast and a chromatic distance (, in JNDs)
from the Vorobyev-Osorio model. Object and background radiances are formed as
reflectance times illuminant.
inherent_contrast( object, background, illuminant, lambda, species = NULL, receptor = NULL, noise = 0.05, channel = c("both", "achromatic", "chromatic"), binwidth = NULL )inherent_contrast( object, background, illuminant, lambda, species = NULL, receptor = NULL, noise = 0.05, channel = c("both", "achromatic", "chromatic"), binwidth = NULL )
object, background
|
Reflectance spectra (numeric vectors on the
|
illuminant |
The in-water light field illuminating both (numeric vector
or irradiance |
lambda |
Finite, positive, strictly increasing, regularly spaced wavelength grid in nm. |
species |
Species name in |
receptor |
Receptor class(es) to include. Default |
noise |
Receptor Weber fraction(s) for the chromatic channel; passed
to |
channel |
|
binwidth |
Bin width in nm; inferred from |
A list with elements achromatic (Weber contrast) and
chromatic ( in JNDs); unrequested channels are
NA.
Johnsen S (2012) The Optics of Life: A Biologist's Guide to Light in Nature. Princeton University Press. (Ch. 5: contrast and sighting distance.)
contrast_at_distance, detection_range,
colour_jnd, weber_contrast
sp <- solar_irradiance("clear_noon") lam <- sp$wavelength grey <- rep(0.3, length(lam)) red <- 0.2 + 0.25 * exp(-((lam - 625) / 35)^2) inherent_contrast(red, grey, sp$irradiance, lam, species = "Danio rerio")sp <- solar_irradiance("clear_noon") lam <- sp$wavelength grey <- rep(0.3, length(lam)) red <- 0.2 + 0.25 * exp(-((lam - 625) / 35)^2) inherent_contrast(red, grey, sp$irradiance, lam, species = "Danio rerio")
Computes photopic illuminance (lux) by weighting spectral irradiance with
the CIE 1931 V() luminous efficiency function. The default
LEF argument can be swapped for CIE_scotopic to obtain
scotopic lux, or for any user-supplied spectral sensitivity curve.
irradiance2lux(irradiance, ...) ## Default S3 method: irradiance2lux(irradiance, ...) ## S3 method for class 'numeric' irradiance2lux( irradiance, lambda, photonic = FALSE, molar_unit = "photons", total = TRUE, LEF = CIE1931, binwidth = NULL, integration = c("rectangle", "trapezoid"), verbose = FALSE, ... ) ## S3 method for class 'lux_spectrum' irradiance2lux( irradiance, total = TRUE, LEF = CIE1931, integration = c("rectangle", "trapezoid"), verbose = FALSE, ... )irradiance2lux(irradiance, ...) ## Default S3 method: irradiance2lux(irradiance, ...) ## S3 method for class 'numeric' irradiance2lux( irradiance, lambda, photonic = FALSE, molar_unit = "photons", total = TRUE, LEF = CIE1931, binwidth = NULL, integration = c("rectangle", "trapezoid"), verbose = FALSE, ... ) ## S3 method for class 'lux_spectrum' irradiance2lux( irradiance, total = TRUE, LEF = CIE1931, integration = c("rectangle", "trapezoid"), verbose = FALSE, ... )
irradiance |
Spectral irradiance ( |
... |
Ignored. |
lambda |
Wavelength in nm (not needed for |
photonic |
Logical; if |
molar_unit |
If photonic, unit string such as |
total |
Logical; sum per-bin values into a scalar. Defaults to TRUE. |
LEF |
Luminous efficiency function data frame. Defaults to CIE1931. |
binwidth |
Wavelength bin width in nm for rectangular integration. For
numeric input, inferred from a regularly spaced wavelength grid when
|
integration |
Integration method: |
verbose |
Logical; emit diagnostic messages. Defaults to FALSE. |
Photopic illuminance is a spectrally weighted integral of irradiance using
the CIE 1931 photopic luminous efficiency function .
The maximum luminous efficacy lm W occurs at
555 nm:
With the default rectangular method, the integral is approximated as
.
For irregular numeric wavelength grids, callers can explicitly select
composite trapezoidal integration. This treats the weighted spectrum as
piecewise linear between measured wavelengths and integrates only over
[min(lambda), max(lambda)]; it does not infer outer bin edges.
values are taken from the LEF data frame by linear
interpolation at each measured wavelength. Wavelengths outside the range of
the supplied LEF receive zero weight.
A scalar lux value (total = TRUE) or numeric vector.
CIE Publication 15: Colorimetry, 3rd edn (2004). Commission Internationale de l'Eclairage, Vienna.
lux2irradiance, scotopic_lux,
par_irradiance
utils::data(Naples) irradiance2lux(Naples$depth_0m, Naples$wv, photonic = TRUE, molar_unit = "umol") irradiance2lux(c(0.2, 0.8, 0.4), c(500, 515, 540), integration = "trapezoid")utils::data(Naples) irradiance2lux(Naples$depth_0m, Naples$wv, photonic = TRUE, molar_unit = "umol") irradiance2lux(c(0.2, 0.8, 0.4), c(500, 515, 540), integration = "trapezoid")
The relationship between irradiance E and radiance L depends on the angular distribution of the light field. The caller must supply the geometry; the function refuses to silently apply the wrong identity.
irradiance2radiance( E, geometry = c("lambertian", "scalar", "collimated", "custom"), solid_angle = NULL )irradiance2radiance( E, geometry = c("lambertian", "scalar", "collimated", "custom"), solid_angle = NULL )
E |
Irradiance value(s) in |
geometry |
One of:
|
solid_angle |
Finite solid angle in (0, 4 pi] steradians. Required when
|
Radiance in (same numeric unit as E).
Johnsen S (2012) The Optics of Life: A Biologist's Guide to Light in Nature. Princeton University Press. (Ch. 2: vector vs scalar irradiance.)
irradiance2radiance(pi) # lambertian: L = 1 irradiance2radiance(2 * pi, "scalar") # scalar: L = 1 irradiance2radiance(10, "collimated") # L = E irradiance2radiance(10, "custom", solid_angle = 2) # L = 5irradiance2radiance(pi) # lambertian: L = 1 irradiance2radiance(2 * pi, "scalar") # scalar: L = 1 irradiance2radiance(10, "collimated") # L = E irradiance2radiance(10, "custom", solid_angle = 2) # L = 5
Returns Kd(lambda) for the specified Jerlov water type from the bundled
jerlov_types dataset (Jerlov 1976; Solonenko & Mobley 2015).
Optionally interpolates onto a user-supplied wavelength grid.
jerlov_Kd( type = "IA", lambda = NULL, interp = c("linear", "spline", "nearest"), extrapolation = c("error", "constant") )jerlov_Kd( type = "IA", lambda = NULL, interp = c("linear", "spline", "nearest"), extrapolation = c("error", "constant") )
type |
Jerlov water type. One of |
lambda |
Wavelengths in nm to interpolate Kd onto. If |
interp |
Interpolation method when |
extrapolation |
Out-of-range policy. The default |
The bundled Jerlov table supports 350–700 nm. Values outside that domain are not measured by this dataset and therefore fail by default. Constant endpoint extension is available only as an explicit modelling assumption; linear or spline extrapolation is not supported.
If lambda is NULL: a data frame with columns
type, lambda, Kd and dataset metadata attributes.
If lambda is supplied: a numeric vector of Kd values (1/m) with a
"luxR.jerlov" provenance attribute recording the water type,
interpolation and extrapolation policies, supported and requested ranges,
source checksum, build commit, and model version.
Jerlov NG (1976) Marine Optics. Elsevier, Amsterdam.
Solonenko MG, Mobley CD (2015) Inherent and apparent optical properties of Jerlov water types. Applied Optics 54(17):5392-5401.
jerlov_Kd("IA") Kd_IA <- jerlov_Kd("IA", lambda = c(400, 450, 500, 550, 600)) as.numeric(Kd_IA) attr(Kd_IA, "luxR.jerlov")[c("type", "interp", "extrapolation", "supported_wavelength_range_nm")] lam <- seq(400, 700, by = 10) Kd <- jerlov_Kd("II", lambda = lam) attenuate_spectrum(rep(1, length(lam)), Kd, depths = c(10, 25, 50), lambda = lam)jerlov_Kd("IA") Kd_IA <- jerlov_Kd("IA", lambda = c(400, 450, 500, 550, 600)) as.numeric(Kd_IA) attr(Kd_IA, "luxR.jerlov")[c("type", "interp", "extrapolation", "supported_wavelength_range_nm")] lam <- seq(400, 700, by = 10) Kd <- jerlov_Kd("II", lambda = lam) attenuate_spectrum(rep(1, length(lam)), Kd, depths = c(10, 25, 50), lambda = lam)
Long-format table of Kd(lambda) values for five oceanic and three coastal water types from Jerlov (1976) and Solonenko & Mobley (2015). Attributes record the supported wavelength range and spacing, a table checksum, source artifact path, build commit, model version, and package version.
jerlov_typesjerlov_types
A data frame with 120 rows and 3 columns:
Jerlov water type (character): "I", "IA",
"IB", "II", "III" (oceanic, clearest to most
turbid) and "C1", "C2", "C3" (coastal).
Wavelength in nm (350 to 700, 25 nm steps).
Diffuse attenuation coefficient in 1/m.
Jerlov NG (1976) Marine Optics. Elsevier, Amsterdam.
Solonenko MG, Mobley CD (2015) Inherent and apparent optical properties of Jerlov water types. Applied Optics 54(17):5392-5401.
data(jerlov_types) subset(jerlov_types, type == "IA")data(jerlov_types) subset(jerlov_types, type == "IA")
Convenience wrapper that takes one of the bundled reference solar spectra,
converts it to , and propagates it from its recorded reference depth
to a single absolute depth in a Jerlov water type via
jerlov_Kd and propagate_spectrum.
This is the "solar condition + water type + depth -> spectrum at depth"
operation the Shiny app's colour-discrimination and detection tabs share.
light_at_depth( condition = "clear_noon", water_type = "IA", depth = 0, surface_source = c("direct", "diffuse"), surface_angle = 30, refractive_index = 1.333, wavelength_policy = c("error", "trim", "constant") )light_at_depth( condition = "clear_noon", water_type = "IA", depth = 0, surface_source = c("direct", "diffuse"), surface_angle = 30, refractive_index = 1.333, wavelength_policy = c("error", "trim", "constant") )
condition |
Solar condition passed to |
water_type |
Jerlov water type passed to |
depth |
Absolute target depth in metres below the water surface. Must be length-1, non-negative, and not shallower than an in-water source's recorded reference depth. Default 0 (surface). |
surface_source |
Surface illumination model used when |
surface_angle |
Incidence angle in degrees from the surface normal for
|
refractive_index |
Refractive index of water relative to air used by the Fresnel surface model. Default 1.333. |
wavelength_policy |
Policy for source wavelengths outside the bundled
Jerlov domain (350–700 nm): |
An above-surface source is multiplied by
surface_transmittance before water-column attenuation. The
default is a flat interface and direct illumination incident 30 degrees
from vertical. An in-water source is unchanged when depth equals its
reference depth. For deeper targets, only the additional water-column
distance is applied. Shallower targets are rejected because reconstructing
them would require an explicit inverse-propagation model.
Water-column propagation is the lightweight spectral Beer-Lambert model:
it assumes a depth-invariant Jerlov , propagates
wavelength bins independently, and is not a multiple-scattering or
angular radiative-transfer calculation. No universal maximum depth is
implied; the selected water type must remain representative of the whole
path.
A data frame with columns lambda (nm) and irradiance
(downwelling spectral irradiance at depth, in ). The
"luxR.jerlov" attribute records the wavelength policy and data
provenance.
from_solar, jerlov_Kd,
propagate_spectrum
head(light_at_depth("clear_noon", "IA", depth = 5, wavelength_policy = "trim"))head(light_at_depth("clear_noon", "IA", depth = 5, wavelength_policy = "trim"))
Constructs a validated lux_spectrum — the core spectral container
used throughout luxR. The object carries spectral values, wavelength grid,
physical quantity, unit, bin width, and free-form metadata together, so
unit conversions and arithmetic operations stay consistent.
lux_spectrum( E, lambda, quantity = "irradiance", unit = "W/m2/nm", binwidth = NULL, meta = list() )lux_spectrum( E, lambda, quantity = "irradiance", unit = "W/m2/nm", binwidth = NULL, meta = list() )
E |
Numeric vector of spectral values, one per wavelength bin. |
lambda |
Numeric vector of bin-centre wavelengths in nm. |
quantity |
Physical quantity: |
unit |
Unit string from the controlled vocabulary. See Details. |
binwidth |
Bin width in nm. Inferred from the wavelength spacing
when |
meta |
Named list of free-form metadata (depth, instrument, etc.). |
Valid unit strings: "W/m2/nm", "W/m2/sr/nm",
"mW/m2/sr/nm", "umol/m2/s/nm", "mmol/m2/s/nm",
"mol/m2/s/nm", "umol/m2/s/sr/nm",
"mmol/m2/s/sr/nm", "mol/m2/s/sr/nm",
"dimensionless".
Both spectral vectors must be non-empty, numeric, finite, and equal in
length. Wavelengths must be strictly increasing, unique, and regularly
spaced. Irradiance and radiance values must be non-negative; reflectance
values must lie in [0, 1]. Units without "/sr" describe
irradiance, units with "/sr" describe radiance, and
"dimensionless" is valid only for reflectance.
S3 methods available on lux_spectrum objects:
print(), summary(), plot(),
as.data.frame(), [ (wavelength subsetting),
and the arithmetic operators +, -, *, /.
A list of lux_spectrum objects can be plotted with
plot_spectra.
An S3 object of class "lux_spectrum".
as_lux_spectrum, convert_unit,
plot_spectra
lam <- seq(400, 700, by = 10) x <- lux_spectrum(rep(1, length(lam)), lam, "irradiance", "W/m2/nm") print(x)lam <- seq(400, 700, by = 10) x <- lux_spectrum(rep(1, length(lam)), lam, "irradiance", "W/m2/nm") print(x)
lux is a single scalar; a spectrum has many degrees of freedom. This
function takes a reference shape and rescales it so its photopic
integral equals lx. The shape assumption must be supplied by the
caller.
lux2irradiance( lx, spectrum, lambda = NULL, LEF = CIE1931, binwidth = NULL, integration = c("rectangle", "trapezoid") )lux2irradiance( lx, spectrum, lambda = NULL, LEF = CIE1931, binwidth = NULL, integration = c("rectangle", "trapezoid") )
lx |
Target illuminance in lux. |
spectrum |
Reference spectral shape. Either a data frame with columns
|
lambda |
Wavelength grid in nm. Required when |
LEF |
Luminous efficiency function. Defaults to |
binwidth |
Wavelength bin width in nm. Inferred from a regular
multi-bin wavelength grid when |
integration |
Integration method passed to
|
Numeric vector of rescaled spectral irradiance (same length as the wavelength grid).
spec <- data.frame(lambda = CIE1931$lambda, irradiance = CIE1931$W) irr <- lux2irradiance(5000, spec)spec <- data.frame(lambda = CIE1931$lambda, irradiance = CIE1931$W) irr <- lux2irradiance(5000, spec)
Vectorised; passes through NA.
m2ft(z)m2ft(z)
z |
Depth or distance in metres. |
Numeric vector in feet.
m2ft(30)m2ft(30)
Computes the Michelson (sinusoidal) contrast: . Values range from 0 (no contrast) to 1 (maximum
contrast). Commonly used for periodic patterns such as gratings.
michelson_contrast(I_bright, I_dark)michelson_contrast(I_bright, I_dark)
I_bright |
Luminance or radiance of the brighter region (scalar or
vector). Must be |
I_dark |
Luminance or radiance of the darker region (scalar or vector). |
Contrast value(s) in .
[weber_contrast()]
michelson_contrast(I_bright = 8, I_dark = 2)michelson_contrast(I_bright = 8, I_dark = 2)
Converts per-bin photon flux (photons m s
nm, or molar equivalents) to energy-based spectral irradiance
in W m nm. The inverse operation is
W2mol_spec_irradiance.
n2W_spec_irradiance(value, ...) ## S3 method for class 'numeric' n2W_spec_irradiance( value, lambda, photonic = FALSE, molar_unit = "photons", ... ) ## S3 method for class 'lux_spectrum' n2W_spec_irradiance(value, ...)n2W_spec_irradiance(value, ...) ## S3 method for class 'numeric' n2W_spec_irradiance( value, lambda, photonic = FALSE, molar_unit = "photons", ... ) ## S3 method for class 'lux_spectrum' n2W_spec_irradiance(value, ...)
value |
Spectral irradiance in |
... |
Ignored. |
lambda |
Wavelength in nm for each bin. |
photonic |
Logical; if |
molar_unit |
Molar unit string (e.g. |
Each photon at wavelength carries energy .
Spectral irradiance in W m nm is therefore the photon
flux (photons m s nm) multiplied by
the per-photon energy:
where J s (Planck constant) and
m s (speed of light). The constant
in nm-compatible units is J nm, the
value used internally.
Numeric vector of spectral irradiance in , or a
lux_spectrum with unit = "W/m2/nm" or
"W/m2/sr/nm" according to the input dimensionality.
utils::data(Naples) n2W_spec_irradiance(Naples$depth_0m, Naples$wv, photonic = TRUE, molar_unit = "umol")utils::data(Naples) n2W_spec_irradiance(Naples$depth_0m, Naples$wv, photonic = TRUE, molar_unit = "umol")
A small set of downwelling spectral irradiance measurements made at three
depths at Mare Chiaro in the Gulf of Naples, Italy. This is example
field data — a single site on particular days — provided to illustrate
working with measured spectra; it is not a general reference spectrum (cf.
the ASTM solar_spectra). Values are in micromol photons per
square metre per second per nm (); wavelength bins are
approximately 5 nm wide.
NaplesNaples
A data frame with 103 rows and 4 columns:
Bin-centre wavelength in nm.
Irradiance at the surface (0 m depth).
Irradiance at 5 m depth.
Irradiance at 10 m depth.
Field measurements of downwelling spectral irradiance at Mare Chiaro, Gulf of Naples, Italy, attributed to M. J. Bok and J. D. Kirwan (2021, unpublished data). The bundled object is regenerated from an explicitly labelled legacy canonical snapshot; original acquisition metadata and redistribution terms are not documented.
data(Naples) irradiance2lux(Naples$depth_0m, Naples$wv, photonic = TRUE, molar_unit = "umol")data(Naples) irradiance2lux(Naples$depth_0m, Naples$wv, photonic = TRUE, molar_unit = "umol")
Returns the ratio of band-integrated irradiance over 400–700 nm to the total integrated irradiance across the full wavelength range supplied. Useful for characterising how spectrally narrowed a light field has become (e.g. at depth, where red wavelengths are preferentially removed).
par_fraction(irradiance, ...) ## S3 method for class 'numeric' par_fraction(irradiance, lambda, binwidth = NULL, ...) ## S3 method for class 'lux_spectrum' par_fraction(irradiance, ...)par_fraction(irradiance, ...) ## S3 method for class 'numeric' par_fraction(irradiance, lambda, binwidth = NULL, ...) ## S3 method for class 'lux_spectrum' par_fraction(irradiance, ...)
irradiance |
Spectral irradiance (any consistent energy unit per nm),
or a |
... |
Ignored. |
lambda |
Wavelength vector in nm. |
binwidth |
Bin width in nm. Inferred from |
Numeric scalar in (0, 1].
sp <- solar_irradiance("clear_noon") par_fraction(sp$irradiance, sp$wavelength)sp <- solar_irradiance("clear_noon") par_fraction(sp$irradiance, sp$wavelength)
Integrates spectral irradiance over the photosynthetically active range
400–700 nm and returns the result in µmol photons m
s. Accepts energy units (W m nm),
raw photon counts, or a lux_spectrum object in any supported unit.
par_irradiance(irradiance, ...) ## S3 method for class 'numeric' par_irradiance(irradiance, lambda, photonic = FALSE, binwidth = NULL, ...) ## S3 method for class 'lux_spectrum' par_irradiance(irradiance, ...)par_irradiance(irradiance, ...) ## S3 method for class 'numeric' par_irradiance(irradiance, lambda, photonic = FALSE, binwidth = NULL, ...) ## S3 method for class 'lux_spectrum' par_irradiance(irradiance, ...)
irradiance |
Spectral irradiance (W m |
... |
Ignored. |
lambda |
Wavelength vector in nm (not needed for |
photonic |
Logical. If |
binwidth |
Bin width in nm. Inferred from |
PAR is the photon flux integrated over the photosynthetically active range
400–700 nm, expressed in µmol photons m s. When
input is in energy units (W m nm), each bin is first
converted to photon flux via , then the total is
divided by Avogadro's constant scaled to µmol:
where mol and
J nm. The result is in
µmol photons m s.
Scalar PAR irradiance in µmol photons m s.
Thimijan RW, Heins RD (1983) HortScience 18:818-822.
par_fraction, irradiance2lux,
band_irradiance
sp <- solar_irradiance("clear_noon") par_irradiance(sp$irradiance, sp$wavelength)sp <- solar_irradiance("clear_noon") par_irradiance(sp$irradiance, sp$wavelength)
Solves exp(-Kd * z) = fraction for z, giving -log(fraction) / Kd. The default 1
photic_depth(Kd, fraction = 0.01)photic_depth(Kd, fraction = 0.01)
Kd |
Diffuse attenuation coefficient(s) in 1/m. |
fraction |
Fraction of surface irradiance. Must be in (0, 1). Default 0.01 (1%). |
Depth(s) in metres at which fraction of surface light remains.
photic_depth(0.06) photic_depth(0.06, fraction = 0.1)photic_depth(0.06) photic_depth(0.06, fraction = 0.1)
Uses E_photon = hc / lambda, with hc = 1.98644585714893e-16 J·nm.
Vectorised over both n and lambda.
photon2W(n, lambda)photon2W(n, lambda)
n |
Photon count(s) in |
lambda |
Wavelength(s) in nm. |
Numeric vector of spectral irradiance in .
photon2W(1e15, 555)photon2W(1e15, 555)
Draws all spectra in x on a single plot with a shared wavelength
axis. Line colours cycle through the default palette. Names of the list
(or meta$label if present) are used for the legend. Additional
arguments in ... are passed to the initial plot() call.
plot_spectra(x, ...)plot_spectra(x, ...)
x |
A named or unnamed list of |
... |
Additional arguments passed to |
invisible(x) — called for its side effect (plot).
How distinct a target's polarization is from its background, for a polarization-sensitive viewer. Each polarization state is mapped to its normalised linear-polarization (Stokes) coordinates
where is the angle of polarization; the polarization
distance is the Euclidean separation in this plane,
The factor of two in the angle correctly handles the 180-degree periodicity of polarization orientation, so orthogonal e-vectors (90 degrees apart) at equal degree of polarization give the maximal angular contrast. This is the geometric core of How & Marshall's (2014) polarization-distance model.
polarization_contrast(target_dop, target_aop, background_dop, background_aop)polarization_contrast(target_dop, target_aop, background_dop, background_aop)
target_dop, target_aop
|
Degree of polarization (0-1) and angle of polarization (degrees) of the target. Vectorised. |
background_dop, background_aop
|
Degree and angle of polarization of the background. Vectorised. Scalars are broadcast; implicit partial recycling is rejected. |
A data frame with columns delta_dop (absolute difference in
degree of polarization), delta_aop (axial angular difference in
degrees, [0, 90]), and distance (the polarization distance
).
How MJ, Marshall NJ (2014) Polarization distance: a framework for modelling object detection by polarization vision systems. Proceedings of the Royal Society B 281:20131632.
underwater_polarization,
degree_of_polarization, weber_contrast
# A horizontally polarized target against a weakly polarized background: polarization_contrast(target_dop = 0.5, target_aop = 0, background_dop = 0.1, background_aop = 45)# A horizontally polarized target against a weakly polarized background: polarization_contrast(target_dop = 0.5, target_aop = 0, background_dop = 0.1, background_aop = 45)
Computes E(to) = E_known * exp(-Kd * (to - from)). Works forward (to > from), inverse (to < from, recovering a shallower value), and between any two depths.
propagate_depth(E_known, Kd, from = 0, to, allow_above_surface = FALSE)propagate_depth(E_known, Kd, from = 0, to, allow_above_surface = FALSE)
E_known |
Irradiance at depth |
Kd |
Diffuse attenuation coefficient in 1/m. Scalar. |
from |
Depth of the known measurement in metres. Default 0 (surface). |
to |
Target depth(s) in metres. Scalar or vector. |
allow_above_surface |
Logical; permits negative absolute depths (above the surface). Default FALSE. |
Irradiance at to, in the same units as E_known.
Un-attenuating with a negative depth delta assumes the water column
is homogeneous above the measurement point. For stratified water bodies
(thermocline, chlorophyll layer) the result will be approximate. Inverse
propagation also amplifies measurement noise and can overflow for long or
strongly attenuating paths. This model does not cross the air-water
interface; use surface_transmittance explicitly or a
high-level workflow such as light_at_depth.
propagate_depth(112400, Kd = 0.062, from = 0, to = 30) propagate_depth(8260, Kd = 0.062, from = 22, to = 0)propagate_depth(112400, Kd = 0.062, from = 0, to = 30) propagate_depth(8260, Kd = 0.062, from = 22, to = 0)
Applies Beer-Lambert attenuation at each wavelength independently,
propagating a known spectral irradiance from depth from to one or
more target depths to. Works in both directions: set to <
from to recover a shallower (or surface) spectrum from a deeper
measurement, assuming a homogeneous water column.
propagate_spectrum(E_lambda, ...) ## S3 method for class 'numeric' propagate_spectrum( E_lambda, Kd_lambda, from = 0, to, format = c("long", "matrix"), lambda = NULL, allow_above_surface = FALSE, ... ) ## S3 method for class 'lux_spectrum' propagate_spectrum( E_lambda, Kd_lambda, from = 0, to, allow_above_surface = FALSE, ... )propagate_spectrum(E_lambda, ...) ## S3 method for class 'numeric' propagate_spectrum( E_lambda, Kd_lambda, from = 0, to, format = c("long", "matrix"), lambda = NULL, allow_above_surface = FALSE, ... ) ## S3 method for class 'lux_spectrum' propagate_spectrum( E_lambda, Kd_lambda, from = 0, to, allow_above_surface = FALSE, ... )
E_lambda |
Spectral irradiance at depth |
... |
Ignored. |
Kd_lambda |
Finite, non-negative diffuse attenuation coefficients (1/m). Length-N vector. |
from |
Finite scalar depth of the known spectrum in metres. Default 0. |
to |
Finite target depth(s) in metres. Scalar or vector. |
format |
|
lambda |
Optional wavelength values (nm) for long-format output. |
allow_above_surface |
Logical; permits negative absolute depths. Default FALSE. |
This is the package's principal lightweight propagation tier. It
applies , assuming is constant along the
path and wavelengths propagate independently. It does not model an
angular radiance field, multiple scattering, or spectral redistribution.
There is no universal supported depth range: validity depends on whether
the supplied represents the entire path. Bundled Jerlov data are
limited to 350–700 nm. Inverse propagation is an assumption-heavy
reconstruction that amplifies noise.
A data frame, matrix, or named list of lux_spectrum.
Kirk JTO (1994) Light and Photosynthesis in Aquatic Ecosystems, 2nd edn. Cambridge University Press.
E_10m <- c(8, 15, 20) Kd <- c(0.02, 0.06, 0.25) propagate_spectrum(E_10m, Kd, from = 10, to = c(0, 25, 50))E_10m <- c(8, 15, 20) Kd <- c(0.02, 0.06, 0.25) propagate_spectrum(E_10m, Kd, from = 10, to = c(0, 25, 50))
Computes , where
is spectral photon irradiance and is a dimensionless
sensitivity or absorptance curve. The result is a photon irradiance weighted
by the supplied curve, not an absolute photon rate for one receptor.
quantum_catch( irradiance, lambda, S, input_unit = NULL, total = TRUE, binwidth = NULL )quantum_catch( irradiance, lambda, S, input_unit = NULL, total = TRUE, binwidth = NULL )
irradiance |
Non-negative spectral irradiance vector, one value per
wavelength bin, in the exact unit declared by |
lambda |
Wavelength bin centres in nm. |
S |
Spectral sensitivity: a data frame with columns
|
input_unit |
Exact unit of |
total |
Logical; sum across wavelengths (default |
binwidth |
Wavelength bin width in nm. Inferred from |
All inputs are converted to raw photon counts before integration, so the
returned unit is photons m^-2 s^-1. With a normalised pigment template,
is a relative sensitivity-weighted photon irradiance. With a
calibrated absorptance curve, it is absorbed photon irradiance per unit
collection area at the plane where irradiance was measured.
Calculating photons s^-1 receptor^-1 additionally requires receptor
collection area, the solid angle subtended by the source, ocular
transmission, and quantum efficiency. This function does not accept or
assume those quantities and therefore does not return an absolute receptor
rate. Sensitivity is assigned zero outside the wavelength range supplied in
S; endpoint values are not extrapolated.
A scalar (when total = TRUE) or numeric vector of integrated
per-bin contributions, in photons m^-2 s^-1.
Kelber A, Vorobyev M, Osorio D (2003) Animal colour vision — behavioural tests and physiological concepts. Biological Reviews 78:81–118.
species_LEF, govardovskii_template,
species_brightness, colour_jnd
lam <- Naples$wv S <- species_LEF("Danio rerio", receptor = "L-cone", lambda = lam) quantum_catch(Naples$depth_0m, lam, S, input_unit = "umol/m2/s/nm")lam <- Naples$wv S <- species_LEF("Danio rerio", receptor = "L-cone", lambda = lam) quantum_catch(Naples$depth_0m, lam, S, input_unit = "umol/m2/s/nm")
Inverse of irradiance2radiance. The geometry argument must
match the one used during collection.
radiance2irradiance( L, geometry = c("lambertian", "scalar", "collimated", "custom"), solid_angle = NULL )radiance2irradiance( L, geometry = c("lambertian", "scalar", "collimated", "custom"), solid_angle = NULL )
L |
Radiance value(s) in |
geometry |
One of |
solid_angle |
Finite solid angle in (0, 4 pi] steradians. Required for
|
Irradiance in .
radiance2irradiance(1) # lambertian: E = pi radiance2irradiance(1, "scalar") # scalar: E = 2*pi radiance2irradiance(5, "custom", solid_angle = 2) # E = 10radiance2irradiance(1) # lambertian: E = pi radiance2irradiance(1, "scalar") # scalar: E = 2*pi radiance2irradiance(5, "custom", solid_angle = 2) # E = 10
Uses lightr for file-format dispatch while retaining a strict,
single-spectrum record that can represent irregular wavelength grids and
signed processed values. This function does not claim that processed values
are a physically valid lux_spectrum; use
as_lux_spectrum or read_spectrum for that step.
read_instrument_spectrum( path, measurement = NULL, value_scale = NULL, range = NULL, interpolate = FALSE, parser_args = list(), warning_policy = "error" )read_instrument_spectrum( path, measurement = NULL, value_scale = NULL, range = NULL, interpolate = FALSE, parser_args = list(), warning_policy = "error" )
path |
Path to one spectral file. |
measurement |
Declared measurement represented by the processed values:
one of |
value_scale |
Declared scale of the processed values: |
range |
Two finite increasing wavelength limits in nm. |
interpolate |
Whether lightr should interpolate to integer nanometres. The default preserves native sampling in the instrument record. |
parser_args |
Named list of simple scalar arguments passed to lightr's individual parser. Arguments controlling file discovery, wavelength range, or interpolation cannot be overridden. |
warning_policy |
Whether parser and metadata warnings should cause an error (the default) or be explicitly retained in the import record. |
A lux_instrument_spectrum containing wavelengths, processed
values, declarations, instrument metadata, notices, and reproducibility
context.
read_spectrum, as_lux_spectrum
Parses the plain-text format without assigning a physical quantity or unit. Every row must contain exactly two tab-separated numeric fields. Instrument values, including negatives, are preserved without clipping.
read_ocean_optics(path)read_ocean_optics(path)
path |
Path to the Ocean Optics |
A data frame containing lambda and signal. Parsed header
and import provenance are attached as "luxR.header" and
"luxR.import" attributes.
Parses one instrument file with read_instrument_spectrum, then
applies explicit scale and negative-value policies before constructing a
lux_spectrum. Typical native spectrometer grids are irregular,
so interpolate = TRUE is normally required. Use
read_instrument_spectrum(interpolate = FALSE) to inspect or retain
native sampling without claiming it satisfies the lux_spectrum grid
contract.
read_spectrum( path, measurement = NULL, value_scale = NULL, quantity = NULL, unit = NULL, calibration = NULL, range = NULL, interpolate = FALSE, negative_policy = "error", parser_args = list(), warning_policy = "error" )read_spectrum( path, measurement = NULL, value_scale = NULL, quantity = NULL, unit = NULL, calibration = NULL, range = NULL, interpolate = FALSE, negative_policy = "error", parser_args = list(), warning_policy = "error" )
path |
Path to one spectral file. |
measurement |
Declared measurement represented by lightr's processed
values. See |
value_scale |
Declared input scale: |
quantity |
Explicit luxR physical quantity. It must match
|
unit |
Explicit unit compatible with |
calibration |
Non-empty calibration identifier or description. |
range |
Required finite increasing wavelength limits in nm. |
interpolate |
Whether lightr should interpolate to integer nanometres.
Defaults to |
negative_policy |
Either |
parser_args |
Named list of simple scalar arguments passed to the lightr parser. |
warning_policy |
Whether lightr warnings should fail the import or be explicitly retained. Defaults to fail-fast behavior. |
A validated lux_spectrum with calibration, instrument
metadata, parser provenance, notices, scaling, and resampling policy.
read_instrument_spectrum,
from_ocean_optics, read_ocean_optics,
from_trios
## Not run: x <- read_spectrum( "path/to/calibrated-irradiance.IRR8", measurement = "irradiance", value_scale = "absolute", quantity = "irradiance", unit = "W/m2/nm", calibration = "certificate CAL-2026-04", range = c(300, 700), interpolate = TRUE ) ## End(Not run)## Not run: x <- read_spectrum( "path/to/calibrated-irradiance.IRR8", measurement = "irradiance", value_scale = "absolute", quantity = "irradiance", unit = "W/m2/nm", calibration = "certificate CAL-2026-04", range = c(300, 700), interpolate = TRUE ) ## End(Not run)
Parses one or more spectra from a TriOS RAMSES '.dat' text file. Each
spectrum is preceded by a 'Comment' header line and terminated by a '[END]'
marker. Data rows contain three whitespace-separated fields: index,
wavelength (nm), and spectral radiance (mW m nm
sr). Radiance values are converted to W m nm
sr. Negative instrument values are preserved; no clipping or
other preprocessing is performed by this low-level reader.
read_trios(path)read_trios(path)
path |
Character. Path to the '.dat' file. |
A data.frame with columns:
Character. Spectrum label from the Comment header.
Numeric. Wavelength in nm.
Numeric. Spectral radiance in W m nm sr.
Converts a normalised visual-pigment absorbance spectrum (e.g. from
govardovskii_template) into the receptor's absorptance —
the fraction of incident photons actually captured at each wavelength — given
the axial optical density of the photoreceptor. At low optical density the
absorptance is nearly proportional to the template; at high optical density
the peak saturates toward 1 and the curve broadens ("self-screening"), so the
realised spectral sensitivity is wider than the bare pigment absorbance. This
is the sensitivity that should be fed to quantum_catch for long
or dense photoreceptors (e.g. deep-sea rods).
receptor_absorptance( S, optical_density = NULL, alpha = NULL, path_length = NULL )receptor_absorptance( S, optical_density = NULL, alpha = NULL, path_length = NULL )
S |
A data frame with columns |
optical_density |
Peak axial optical density (base-10 absorbance),
dimensionless. Typically ~0.3–0.5 for vertebrate cones, up to ~1 or more
for long deep-sea rods. Supply this or ( |
alpha |
Peak absorption coefficient per unit length (e.g. per µm). |
path_length |
Outer-segment (path) length, in the same length unit as
|
For a peak-normalised absorbance and axial optical density
(base-10 absorbance along the outer segment at the peak), the
absorptance is
with . Equivalently, supply the peak absorption
coefficient (alpha, per unit length) and the
outer-segment path_length , giving
(Johnsen 2012, ch. 4, eq. 4.8).
If S is a data frame, a data frame with columns lambda
and S holding the absorptance (so it drops straight into
quantum_catch); if S is numeric, a numeric vector of
absorptance values in .
Johnsen S (2012) The Optics of Life: A Biologist's Guide to Light in Nature. Princeton University Press. (Ch. 4: absorptance and self-screening.)
govardovskii_template, quantum_catch
lam <- seq(300, 750, by = 1) tmpl <- govardovskii_template(lam, lambda_max = 500) # Realised sensitivity of a moderately dense cone (axial OD 0.4): A <- receptor_absorptance(tmpl, optical_density = 0.4) # A long deep-sea rod via Johnsen's absorption coefficient (0.064 / um): A75 <- receptor_absorptance(tmpl, alpha = 0.064, path_length = 75)lam <- seq(300, 750, by = 1) tmpl <- govardovskii_template(lam, lambda_max = 500) # Realised sensitivity of a moderately dense cone (axial OD 0.4): A <- receptor_absorptance(tmpl, optical_density = 0.4) # A long deep-sea rod via Johnsen's absorption coefficient (0.064 / um): A75 <- receptor_absorptance(tmpl, alpha = 0.064, path_length = 75)
Multiplies a reflectance spectrum by an illuminant spectrum bin-by-bin.
For a lux_spectrum irradiance illuminant, a Lambertian reflector is
assumed: radiance is , and the output unit gains the
steradian term. For a radiance illuminant, the values are multiplied
directly and its radiance unit is retained.
reflectance_to_radiance(reflectance, ...) ## S3 method for class 'numeric' reflectance_to_radiance(reflectance, illuminant, lambda, ...) ## S3 method for class 'lux_spectrum' reflectance_to_radiance(reflectance, illuminant, ...)reflectance_to_radiance(reflectance, ...) ## S3 method for class 'numeric' reflectance_to_radiance(reflectance, illuminant, lambda, ...) ## S3 method for class 'lux_spectrum' reflectance_to_radiance(reflectance, illuminant, ...)
reflectance |
Numeric vector of reflectance values in [0, 1], or a
|
... |
Ignored. |
illuminant |
Numeric vector of spectral irradiance/radiance, or a
|
lambda |
Numeric vector of wavelengths in nm (not needed when both
inputs are |
Named numeric vector, or a lux_spectrum with
quantity = "radiance" and a dimensionally compatible radiance unit.
The numeric method performs only element-wise multiplication because
numeric vectors do not carry irradiance/radiance dimensional metadata.
sp <- solar_irradiance("clear_noon") lam <- sp$wavelength refl <- rep(0.5, length(lam)) reflectance_to_radiance(refl, sp$irradiance, lam)sp <- solar_irradiance("clear_noon") lam <- sp$wavelength refl <- rep(0.5, length(lam)) reflectance_to_radiance(refl, sp$irradiance, lam)
Snell's-law refraction of the solar beam entering water:
. As the in-air zenith
approaches 90 degrees the refracted angle approaches the edge of Snell's
window (about 48.6 degrees for n = 1.333); see
snells_window.
refracted_solar_angle(sun_zenith, n = 1.333)refracted_solar_angle(sun_zenith, n = 1.333)
sun_zenith |
In-air solar zenith angle in degrees, |
n |
Refractive index of water. Default 1.333. |
Refracted (in-water) solar zenith angle in degrees. Vectorised.
snells_window, underwater_polarization
refracted_solar_angle(0) # 0 refracted_solar_angle(90) # ~48.6 (edge of Snell's window)refracted_solar_angle(0) # 0 refracted_solar_angle(90) # ~48.6 (edge of Snell's window)
Interpolates spectral values onto a target wavelength grid using
approx (linear, default) or
spline (cubic). The most common use case is
harmonising spectra from two instruments before arithmetic or comparison
(e.g. aligning a TriOS RAMSES spectrum at 3.3 nm resolution with a
Jerlov-based Kd vector at 25 nm steps).
resample_spectrum( x, lambda, from = NULL, method = c("linear", "cubic"), rule = 1 ) ## S3 method for class 'lux_spectrum' resample_spectrum( x, lambda, from = NULL, method = c("linear", "cubic"), rule = 1 ) ## S3 method for class 'numeric' resample_spectrum( x, lambda, from = NULL, method = c("linear", "cubic"), rule = 1 )resample_spectrum( x, lambda, from = NULL, method = c("linear", "cubic"), rule = 1 ) ## S3 method for class 'lux_spectrum' resample_spectrum( x, lambda, from = NULL, method = c("linear", "cubic"), rule = 1 ) ## S3 method for class 'numeric' resample_spectrum( x, lambda, from = NULL, method = c("linear", "cubic"), rule = 1 )
x |
A |
lambda |
Target wavelength grid in nm (numeric vector). When
|
from |
Source wavelength grid in nm. Required when |
method |
Interpolation method: |
rule |
Integer passed to |
Values are not extrapolated beyond the source wavelength range. By
default (rule = 1) wavelengths outside that range receive
NA; set rule = 2 to extend using the nearest boundary
value instead. Because a lux_spectrum cannot contain non-finite
values, its method raises a validation error when rule = 1 produces
NA. The target grid for a lux_spectrum must also be strictly
increasing and regular. The numeric method can be used to resample raw
values from an irregular source grid before constructing a
lux_spectrum.
A lux_spectrum (when x is a lux_spectrum)
or a named numeric vector, resampled onto lambda.
# Resample a solar spectrum from 10 nm to 5 nm bins sp <- solar_irradiance("clear_noon") x <- as_lux_spectrum(sp) x5 <- resample_spectrum(x, lambda = seq(300, 800, by = 5)) x5 # Align a Jerlov Kd vector to the Naples wavelength grid lam_naples <- from_naples("0m")$lambda lam_naples <- lam_naples[lam_naples >= 350 & lam_naples <= 700] lam_jerlov <- seq(350, 700, by = 25) Kd_coarse <- jerlov_Kd("II", lam_jerlov) Kd_fine <- resample_spectrum(Kd_coarse, lambda = lam_naples, from = lam_jerlov, rule = 1)# Resample a solar spectrum from 10 nm to 5 nm bins sp <- solar_irradiance("clear_noon") x <- as_lux_spectrum(sp) x5 <- resample_spectrum(x, lambda = seq(300, 800, by = 5)) x5 # Align a Jerlov Kd vector to the Naples wavelength grid lam_naples <- from_naples("0m")$lambda lam_naples <- lam_naples[lam_naples >= 350 & lam_naples <= 700] lam_jerlov <- seq(350, 700, by = 25) Kd_coarse <- jerlov_Kd("II", lam_jerlov) Kd_fine <- resample_spectrum(Kd_coarse, lambda = lam_naples, from = lam_jerlov, rule = 1)
Opens an interactive Shiny application for analysing underwater light environments. It has five guided tabs:
Depth Propagation – attenuate a built-in or uploaded irradiance spectrum through a Jerlov water type and download the per-depth lux and photon-flux table.
Species Perception – photoreceptor quantum catch for a chosen species under a selected bundled light-source condition at that source's stated reference depth.
Colour discrimination – Vorobyev-Osorio just-noticeable difference (JND) between two reflectances under the in-water light field.
Visibility – Secchi depth, photic depth, and horizontal visual-range scenario estimate.
Detection – explicitly unvalidated object-background contrast-threshold distance scenarios.
Requires the shiny package.
run_app(...)run_app(...)
... |
Arguments passed to |
Invisible NULL; called for its side effect of starting the app.
## Not run: run_app() ## End(Not run)## Not run: run_app() ## End(Not run)
The angle between two directions given as (zenith, azimuth):
For the single-scattering polarization model this is the relevant scattering
angle (the degree of polarization peaks near ).
scattering_angle(view_zenith, view_azimuth, sun_zenith, sun_azimuth)scattering_angle(view_zenith, view_azimuth, sun_zenith, sun_azimuth)
view_zenith, view_azimuth
|
Viewing direction in degrees. |
sun_zenith, sun_azimuth
|
Solar-beam direction in degrees (use
|
Angle in degrees, [0, 180]. Vectorised over its arguments.
scattering_angle(view_zenith = 90, view_azimuth = 0, sun_zenith = 0, sun_azimuth = 0) # 90scattering_angle(view_zenith = 90, view_azimuth = 0, sun_zenith = 0, sun_azimuth = 0) # 90
Drop-in replacement for irradiance2lux using the CIE 1951
scotopic luminous efficiency function V'(lambda). The scotopic luminous
efficacy K_m' = 1700 lm/W replaces the photopic K_m = 683 lm/W.
scotopic_lux(irradiance, ...) ## Default S3 method: scotopic_lux(irradiance, ...) ## S3 method for class 'numeric' scotopic_lux( irradiance, lambda, photonic = FALSE, molar_unit = "photons", total = TRUE, binwidth = NULL, integration = c("rectangle", "trapezoid"), verbose = FALSE, ... ) ## S3 method for class 'lux_spectrum' scotopic_lux( irradiance, total = TRUE, integration = c("rectangle", "trapezoid"), verbose = FALSE, ... )scotopic_lux(irradiance, ...) ## Default S3 method: scotopic_lux(irradiance, ...) ## S3 method for class 'numeric' scotopic_lux( irradiance, lambda, photonic = FALSE, molar_unit = "photons", total = TRUE, binwidth = NULL, integration = c("rectangle", "trapezoid"), verbose = FALSE, ... ) ## S3 method for class 'lux_spectrum' scotopic_lux( irradiance, total = TRUE, integration = c("rectangle", "trapezoid"), verbose = FALSE, ... )
irradiance |
Spectral irradiance ( |
... |
Ignored. |
lambda |
Wavelength in nm (not needed for |
photonic |
Logical; if |
molar_unit |
Molar unit string when |
total |
Logical; sum per-bin values into a scalar. Default
|
binwidth |
Wavelength bin width in nm. Inferred from a regular
multi-bin wavelength grid when |
integration |
Integration method passed to
|
verbose |
Logical; emit diagnostic messages. Default |
Scotopic illuminance uses the CIE 1951 scotopic luminous efficiency function
, which peaks at 507 nm. The maximum scotopic luminous
efficacy is lm W:
Computed internally by calling irradiance2lux with
LEF = CIE_scotopic and applying the efficacy ratio .
A scalar scotopic lux value (when total = TRUE) or numeric
vector.
The scotopic/photopic crossover occurs near 10 lx. Below that threshold — twilight, moonlight, deep water — scotopic readings better reflect perceived brightness.
data(Naples) scotopic_lux(Naples$depth_10m, Naples$wv, photonic = TRUE, molar_unit = "umol")data(Naples) scotopic_lux(Naples$depth_10m, Naples$wv, photonic = TRUE, molar_unit = "umol")
Estimates the Secchi disk visibility depth using the Tyler (1968)
approximation . When a spectral irradiance
is supplied the function first computes an irradiance-weighted mean Kd
across the visible range before applying the approximation.
secchi_depth(Kd, lambda = NULL, irradiance = NULL, spectrum = NULL)secchi_depth(Kd, lambda = NULL, irradiance = NULL, spectrum = NULL)
Kd |
Diffuse attenuation coefficient(s) in 1/m. |
lambda |
Optional wavelength vector (nm) matching |
irradiance |
Optional spectral irradiance for PAR-weighted mean Kd. |
spectrum |
Optional |
Secchi depth in metres.
Tyler JE (1968) Limnol. Oceanogr. 13:1-6. Preisendorfer RW (1986) Limnol. Oceanogr. 31:909-926.
visual_range, jerlov_Kd,
photic_depth
secchi_depth(jerlov_Kd("IA", lambda = 490))secchi_depth(jerlov_Kd("IA", lambda = 490))
The fraction of beam attenuation due to scattering rather than absorption,
(Johnsen 2012, ch. 5). Values near 1
indicate a strongly scattering, weakly absorbing medium (e.g. fog or milk);
values near 0 a strongly absorbing one.
single_scattering_albedo(absorption, scattering)single_scattering_albedo(absorption, scattering)
absorption |
Absorption coefficient |
scattering |
Scattering coefficient |
Single-scattering albedo (dimensionless, 0-1).
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 5.
single_scattering_albedo(absorption = 0.1, scattering = 0.05) # 0.333single_scattering_albedo(absorption = 0.1, scattering = 0.05) # 0.333
Underwater, the entire above-water hemisphere is refracted into a cone — the
"Snell's window" or "optical manhole" — of half-angle
(Johnsen 2012, ch. 5). For seawater
() this is about 48.6 degrees, so the whole sky is
compressed into a ~97 degree cone overhead.
snells_window(n = 1.333)snells_window(n = 1.333)
n |
Refractive index of the water relative to air. Default 1.333. Vectorised. |
Half-angle of Snell's window, in degrees.
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 5.
fresnel_reflectance, wavelength_in_medium
snells_window() # ~48.6 degreessnells_window() # ~48.6 degrees
Returns a reference spectrum from the bundled solar_spectra dataset,
based on ASTM G173-03 global-tilt values scaled to several representative
illumination conditions. Intended for use as the reference shape in
broadband2spectrum and lux2irradiance.
solar_irradiance(condition = "clear_noon")solar_irradiance(condition = "clear_noon")
condition |
Character string naming the illumination condition. One of the five conditions listed in Details. |
Available conditions:
"clear_noon"Full-sun midday irradiance (ASTM G173-03 GTI).
"clear_dawn"Clear sky near sunrise/sunset (~35% of noon).
"overcast"Overcast diffuse sky (~22% of noon).
"underwater_1m"ASTM GTI attenuated through 1 m of Jerlov IA water.
"underwater_10m"ASTM GTI attenuated through 10 m of Jerlov IA water.
The historical underwater sources span 300–800 nm and were generated with constant endpoint extension beyond the bundled Jerlov dataset's supported 350–700 nm domain. Their attributes explicitly record this assumption and the Jerlov table checksum so it is not a silent extrapolation path.
A data frame with columns wavelength (nm) and
irradiance (). Attributes record condition,
reference_depth_m (metres relative to the water surface), and
reference_medium ("air" or "water").
solar_irradiance("clear_noon") broadband2spectrum(480, unit = "W", spectrum = solar_irradiance("overcast"))solar_irradiance("clear_noon") broadband2spectrum(480, unit = "W", spectrum = solar_irradiance("overcast"))
A named list of reference spectral irradiance data frames representing
five standard illumination conditions, derived from the ASTM G173-03
global-tilt spectrum. Used by solar_irradiance.
solar_spectrasolar_spectra
A named list of five data frames, each with columns:
Wavelength in nm (300 to 800, step 10).
Spectral irradiance in .
Names: clear_noon, clear_dawn, overcast,
underwater_1m, underwater_10m.
Each data frame has attributes condition,
reference_depth_m, and reference_medium. Reference depth is
measured in metres from the water surface; the three atmospheric spectra
use depth 0 in "air", and the underwater spectra use depths 1 and
10 in "water", respectively.
ASTM G173-03(2012) Standard Tables for Reference Solar Spectral Irradiances. American Society for Testing and Materials, West Conshohocken, PA.
solar_irradiance("clear_noon")solar_irradiance("clear_noon")
Convenience wrapper around quantum_catch and
species_LEF that returns quantum catches for all (or one)
receptor class of a named species.
species_brightness( irradiance, lambda, species, input_unit = NULL, channel = "by_receptor", binwidth = NULL )species_brightness( irradiance, lambda, species, input_unit = NULL, channel = "by_receptor", binwidth = NULL )
irradiance |
Non-negative spectral irradiance vector, one value per
wavelength bin, in the exact unit declared by |
lambda |
Wavelength bin centres in nm. |
species |
Species name. Must appear in |
input_unit |
Exact unit of |
channel |
One of:
|
binwidth |
Wavelength bin width in nm. Inferred from |
Named numeric vector, scalar, or single numeric depending on
channel.
quantum_catch, species_LEF,
colour_jnd
idx <- Naples$wv >= 400 & Naples$wv <= 700 lam <- Naples$wv[idx] irr <- Naples$depth_0m[idx] species_brightness(irr, lam, "Homo sapiens", input_unit = "umol/m2/s/nm", channel = "by_receptor")idx <- Naples$wv >= 400 & Naples$wv <= 700 lam <- Naples$wv[idx] irr <- Naples$depth_0m[idx] species_brightness(irr, lam, "Homo sapiens", input_unit = "umol/m2/s/nm", channel = "by_receptor")
Records whether every bundled species has a supported default chromatic and achromatic model. Unsupported combinations remain explicit rather than being inferred from receptor-role labels.
species_channel_supportspecies_channel_support
A data frame with 16 rows and 5 columns:
Scientific name matching species_sensitivities.
Either "chromatic" or "achromatic".
Either "supported" or "unavailable".
Evidence summary or reason no default is available.
Literature citation or explicit package review decision.
Every supported row must correspond to exactly one default in
species_channels. Every unavailable row must have no default.
Dataset generation and runtime validation enforce this invariant.
data(species_channel_support) subset(species_channel_support, species == "Homo sapiens")data(species_channel_support) subset(species_channel_support, species == "Homo sapiens")
Defines cited receptor combinations that package models may use by default.
Receptors marked with a role in species_sensitivities are not
model-eligible unless they occur in this table.
species_channelsspecies_channels
A data frame with 13 rows and 7 columns:
Scientific name matching
species_sensitivities$species.
Stable channel identifier within a species.
Biological role from the controlled vocabulary.
Receptor member matching a species/receptor pair in
species_sensitivities.
Finite positive channel membership weight. Chromatic JND currently uses receptor-noise weighting rather than this field.
Whether this is the default channel for its species and role. A species can have at most one default channel per role.
Literature citation supporting the receptor set.
The curated set contains default chromatic channels for Homo sapiens, Danio rerio, and Apis mellifera, plus behavioural achromatic channels for adult light-adapted Danio rerio, Apis mellifera, and Drosophila melanogaster. Species with incomplete or ambiguous receptor sets are deliberately absent and therefore fail rather than falling back to every known receptor.
Krauss A, Neumeyer C (2003) Wavelength dependence of the optomotor response in zebrafish (Danio rerio). Vision Research 43:1275-1284. doi:10.1016/S0042-6989(03)00090-7
Niggebrugge C, Hempel de Ibarra N (2003) Colour-dependent target detection by bees. Journal of Comparative Physiology A 189:915-918. doi:10.1007/s00359-003-0466-3
Yamaguchi S, Wolf R, Desplan C, Heisenberg M (2008) Motion vision is independent of color in Drosophila. PNAS 105:4910-4915. doi:10.1073/pnas.0711484105
data(species_channels) subset(species_channels, species == "Homo sapiens")data(species_channels) subset(species_channels, species == "Homo sapiens")
Looks up peak wavelength and chromophore from the bundled
species_sensitivities table, then applies the Govardovskii (2000)
template to return a normalised sensitivity curve.
species_LEF(species, receptor = NULL, lambda = seq(300, 700, by = 1))species_LEF(species, receptor = NULL, lambda = seq(300, 700, by = 1))
species |
Species name (character). Must match a row in
|
receptor |
Photoreceptor class (e.g. |
lambda |
Wavelength grid in nm. Default |
A data frame (single receptor) or named list of data frames (all
receptors). Each data frame has columns lambda and S.
species_LEF("Homo sapiens", receptor = "L-cone") species_LEF("Danio rerio")species_LEF("Homo sapiens", receptor = "L-cone") species_LEF("Danio rerio")
Peak wavelengths and chromophore types for visual photoreceptors of
representative species from vertebrate and invertebrate taxa.
Used by species_LEF and species_brightness.
species_sensitivitiesspecies_sensitivities
A data frame with 27 rows and 6 columns:
Scientific name.
Receptor class (e.g. "L-cone", "rod").
Peak absorbance wavelength in nm.
"A1" (11-cis retinal) or "A2" (11-cis 3,4-didehydroretinal).
Primary biological role from the controlled vocabulary
"chromatic", "achromatic", "irradiance",
"polarization", or "fluorescence".
Literature citation for the lambda_max value.
Each row's lambda_max is taken from the primary-literature citation
recorded in its source column; luxR reconstructs full sensitivity
curves from these peaks using the Govardovskii et al. (2000) visual-pigment
template (see govardovskii_template).
Per-receptor lambda_max sources: Bowmaker & Dartnall (1980) and Bailes & Lucas (2013) (Homo sapiens); Robinson et al. (1993) (Danio rerio); Peitsch et al. (1992) (Apis mellifera); Salcedo et al. (1999) (Drosophila melanogaster); Hart et al. (2004) (Callorhinchus milii); Yokoyama et al. (1999) (Latimeria chalumnae); Yoshiura et al. (2011) (Xenopus laevis); Hawryshyn et al. (2003) (Oncorhynchus mykiss).
A role label does not by itself validate a receptor for a model. Complete,
cited channel memberships are recorded separately in
species_channels. The generic mantis-shrimp record and
Aequorea victoria GFP previously included in this table were
removed because they are not supported visual-pigment channel records.
Govardovskii VI, Fyhrquist N, Reuter T, Kuzmin DG, Donner K (2000) In search of the visual pigment template. Visual Neuroscience 17:509-528.
Full citation keys and their verification status are recorded in
data-raw/species_source_map.csv. The legacy Hart et al. (2004) and
Yoshiura et al. (2011) attributions remain explicitly unverified pending
primary-source confirmation; the values are not silently relabelled.
Robinson J, Schmitt EA, Hárosi FI, Reece RJ, Dowling JE (1993). “Zebrafish Ultraviolet Visual Pigment: Absorption Spectrum, Sequence, and Localization.” Proceedings of the National Academy of Sciences, 90(13), 6009–6012. doi:10.1073/pnas.90.13.6009. 2026-06-04.
Hawryshyn CW, Martens G, Allison WT, Anholt BR (2003). “Regeneration of Ultraviolet-Sensitive Cones in the Retinal Cone Mosaic of Thyroxin-Challenged Post-Juvenile Rainbow Trout (Oncorhynchus Mykiss).” Journal of Experimental Biology, 206(15), 2665–2673. ISSN 0022-0949. doi:10.1242/jeb.00470. 2026-06-04.
data(species_sensitivities) subset(species_sensitivities, species == "Danio rerio")data(species_sensitivities) subset(species_sensitivities, species == "Danio rerio")
Assembles a species' validated chromatic receptors into the sensitivity
matrix C that colourvision models consume (a data frame whose
first column is wavelength and whose remaining columns are one photoreceptor
each). The receptors and their Govardovskii templates are exactly those
colour_jnd uses, so a colourvision model built on this matrix
reproduces luxR's own quantum catches.
species_sensitivity_matrix( species, lambda = seq(300, 700, 1), receptor = NULL, weight = c("photon", "energy") )species_sensitivity_matrix( species, lambda = seq(300, 700, 1), receptor = NULL, weight = c("photon", "energy") )
species |
Character scalar naming a species in
|
lambda |
Numeric wavelength grid (nm); must be finite, strictly
increasing, and unique. Defaults to |
receptor |
Optional character vector selecting a subset of the species'
validated chromatic receptors; passed through to the same channel
resolution |
weight |
One of |
luxR integrates photon catch (proportional to ) while
colourvision integrates energy (). With
weight = "photon" (the default) the columns are the sensitivities
multiplied by wavelength (), so colourvision's energy integral
reproduces luxR's photon catch; the proportionality constant cancels in the
log RNL model. Use weight = "energy" for the raw sensitivities.
A data.frame with a wl column plus one column per
receptor, named and ordered as luxR's channel definitions.
as_colourvision, colour_jnd,
colourvision::RNLmodel
## Not run: C <- species_sensitivity_matrix("Apis mellifera") ## End(Not run)## Not run: C <- species_sensitivity_matrix("Apis mellifera") ## End(Not run)
The fraction of above-surface downwelling irradiance that enters the water
after Fresnel reflection at a flat air-water interface — the surface term of
an above-vs-below-water light budget: (Johnsen 2012, ch. 5).
surface_transmittance(angle = NULL, n = 1.333, source = c("direct", "diffuse"))surface_transmittance(angle = NULL, n = 1.333, source = c("direct", "diffuse"))
angle |
Solar/incidence angle from the surface normal, in degrees (0-90).
Required for |
n |
Refractive index of water relative to air. Default 1.333. |
source |
|
For a "direct" (collimated, e.g. solar-beam) source at a given
incidence angle, . For a "diffuse" (isotropic
sky) source it is the cosine-weighted hemispherical average of
, about 0.93 for seawater (i.e. ~7% is reflected). This is
the planar-irradiance transmittance; in-water radiance is additionally
boosted by because the refracted beam is compressed into
snells_window.
Transmitted fraction of downwelling irradiance (0-1).
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 5.
fresnel_reflectance, snells_window
surface_transmittance(angle = 0) # overhead sun: ~0.98 surface_transmittance(source = "diffuse") # isotropic sky: ~0.93surface_transmittance(angle = 0) # overhead sun: ~0.98 surface_transmittance(source = "diffuse") # isotropic sky: ~0.93
Fraction of collimated light transmitted through a path of length
path_length in a medium with attenuation coefficient:
(Johnsen 2012, ch. 4). The fraction absorbed
(or attenuated) is ; note that absorbances add over stacked paths
while transmittances multiply.
transmittance(coefficient, path_length)transmittance(coefficient, path_length)
coefficient |
Attenuation (or absorption) coefficient (1/length). Vectorised. |
path_length |
Path length in the same length unit. Vectorised. |
Transmittance (0-1).
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 4.
transmittance(coefficient = 0.15, path_length = 10) 1 - transmittance(0.064, 10) # hatchetfish rod: ~47% absorbedtransmittance(coefficient = 0.15, path_length = 10) 1 - transmittance(0.064, 10) # hatchetfish rod: ~47% absorbed
A lightweight, analytic single-scattering model of linear polarization in the water column. The degree of polarization follows the Rayleigh-like form
where is the scattering angle between the line of sight and the
refracted solar beam (scattering_angle); polarization is maximal
at and vanishes toward the sun and anti-sun. With
depth, multiple scattering randomizes the field, so the model relaxes toward an
asymptotic floor:
The e-vector (angle of polarization) is perpendicular to the scattering plane.
underwater_polarization( view_zenith, view_azimuth = 0, sun_zenith, sun_azimuth = 0, depth = 0, dop_max = 0.4, dop_asym = 0.03, z_p = 20, n = 1.333 )underwater_polarization( view_zenith, view_azimuth = 0, sun_zenith, sun_azimuth = 0, depth = 0, dop_max = 0.4, dop_asym = 0.03, z_p = 20, n = 1.333 )
view_zenith, view_azimuth
|
Viewing direction in degrees (zenith: 0 = up, 90 = horizontal, 180 = down). Vectorised. Scalars are broadcast; implicit partial recycling is rejected. |
sun_zenith |
In-air solar zenith angle in degrees. Refracted internally
via |
sun_azimuth |
Solar azimuth in degrees. Default 0. |
depth |
Depth in metres ( |
dop_max |
Maximum (surface, 90-degree-scattering) degree of polarization.
Default 0.4 is an illustrative value at the upper end of the approximately
0.25–0.40 maxima reported at 15 m in clear tropical marine water by Cronin
and Shashar (2001). Because this model defines |
dop_asym |
Asymptotic deep-field degree of polarization. Default 0.03 is a heuristic illustrative floor, not an empirically calibrated constant. |
z_p |
Depth scale (m) over which DoP relaxes toward |
n |
Refractive index of water. Default 1.333 is a conventional visible- light approximation. Refractive index varies with wavelength, temperature, and salinity. |
This is an explicitly uncalibrated, first-order approximation for teaching, geometry exploration, and field intuition. It is not a vector radiative-transfer solution and must not be interpreted as a quantitative prediction without calibration to measurements from the relevant site, wavelength, water type, surface state, and viewing geometry. It omits the wavelength dependence of scattering and refraction, particulate and dissolved constituents, surface waves and polarized skylight, bottom reflection, and a physical multiple-scattering calculation.
A data frame with one row per viewing direction and columns
scattering_angle (deg), dop (0–1), and aop (deg,
(-90, 90]; NA when looking along the solar beam). The data
frame has a "luxR.polarization" attribute recording model status,
model version, parameters, and the validity statement.
Cronin TW, Shashar N (2001) The linearly polarized light field in clear, tropical marine waters. Journal of Experimental Biology 204:2461-2467.
Johnsen S (2012) The Optics of Life. Princeton University Press, ch. 8.
degree_of_polarization, scattering_angle,
refracted_solar_angle, snells_window
# DoP across viewing zenith with the sun overhead, near the surface: underwater_polarization(view_zenith = seq(0, 90, 15), view_azimuth = 0, sun_zenith = 0, depth = 1)# DoP across viewing zenith with the sun overhead, near the surface: underwater_polarization(view_zenith = seq(0, 90, 15), view_azimuth = 0, sun_zenith = 0, depth = 1)
Formats a unit from the controlled vocabulary as a plotmath language
object with SI negative exponents, suitable for passing directly as the
xlab or ylab of a base graphics plot.
unit_expression(unit)unit_expression(unit)
unit |
A single unit string from the controlled vocabulary. See
|
A language object suitable for use as a plot label.
unit_label for the plain-text equivalent.
plot(400:700, rep(1, 301), type = "l", xlab = "Wavelength (nm)", ylab = unit_expression("W/m2/nm"))plot(400:700, rep(1, 301), type = "l", xlab = "Wavelength (nm)", ylab = unit_expression("W/m2/nm"))
Formats a unit from the controlled vocabulary as a human-readable label
using SI negative exponents, for use in table headers, user interfaces,
and prose. The canonical unit strings stored in
lux_spectrum$unit are deliberately ASCII ("umol/m2/s/nm") so
they remain stable identifiers; this function is the display counterpart.
unit_label(unit)unit_label(unit)
unit |
A single unit string from the controlled vocabulary. See
|
A length-1 character vector holding the rendered label, e.g.
for
"umol/m2/s/nm".
unit_expression for the plotmath equivalent used in
plot axis labels.
unit_label("umol/m2/s/nm") unit_label("W/m2/sr/nm")unit_label("umol/m2/s/nm") unit_label("W/m2/sr/nm")
Applies the Koschmieder–Berek law to estimate the maximum horizontal
sighting distance in water:
where is the inherent contrast threshold and is the
beam attenuation coefficient. Because is rarely measured directly,
a conversion factor kd_to_c maps diffuse attenuation Kd to .
visual_range(Kd, contrast_threshold = 0.02, kd_to_c = 1.5, beam_c = NULL)visual_range(Kd, contrast_threshold = 0.02, kd_to_c = 1.5, beam_c = NULL)
Kd |
Diffuse attenuation coefficient in 1/m (scalar or vector). |
contrast_threshold |
Inherent contrast threshold of the visual system or camera (dimensionless, typically 0.01–0.05). Default 0.02. |
kd_to_c |
Ratio |
beam_c |
Optional measured beam attenuation coefficient
|
This is a contrast-threshold scenario estimate, not an empirically validated prediction of detection by a particular observer or camera.
The coefficient that governs horizontal sighting distance is the beam
attenuation coefficient , not the diffuse attenuation
(Johnsen 2012, ch. 5: as you move away from an object, veiling light
replaces it at a rate equal to ). If beam_c is not given, this
function approximates kd_to_c. Note that
always, so treating as (kd_to_c = 1)
is an upper bound that overestimates range. The model assumes
horizontal viewing; for an upward-looking observer the effective
coefficient is (smaller, so ranges are longer), which is not
modelled here.
Heuristic visual-range estimate in metres. Same length as Kd.
Duntley SQ (1963) Light in the sea. J. Opt. Soc. Am. 53:214–233.
Zaneveld JRV, Pegau WS (2003) Robust underwater visibility parameter. Opt. Express 11:2997–3009.
Johnsen S (2012) The Optics of Life: A Biologist's Guide to Light in
Nature. Princeton University Press. (Ch. 5: sighting distance and .)
# Visual range for Jerlov IA water at 490 nm visual_range(jerlov_Kd("IA", lambda = 490)) # Compare water types types <- c("I", "IA", "II", "C2") sapply(types, function(t) visual_range(jerlov_Kd(t, lambda = 490)))# Visual range for Jerlov IA water at 490 nm visual_range(jerlov_Kd("IA", lambda = 490)) # Compare water types types <- c("I", "IA", "II", "C2") sapply(types, function(t) visual_range(jerlov_Kd(t, lambda = 490)))
Converts W m nm spectral irradiance to a
photon-count flux expressed in molar units (mol, mmol, or photons
m s nm). The inverse operation is
n2W_spec_irradiance. The same per-bin conversion factor
applies to spectral radiance (W m sr
nm).
W2mol_spec_irradiance(W, ...) ## S3 method for class 'numeric' W2mol_spec_irradiance(W, lambda, molar_unit = "umol", ...) ## S3 method for class 'lux_spectrum' W2mol_spec_irradiance(W, molar_unit = "umol", ...)W2mol_spec_irradiance(W, ...) ## S3 method for class 'numeric' W2mol_spec_irradiance(W, lambda, molar_unit = "umol", ...) ## S3 method for class 'lux_spectrum' W2mol_spec_irradiance(W, molar_unit = "umol", ...)
W |
Spectral irradiance in |
... |
Ignored. |
lambda |
Wavelength in nm for each bin. |
molar_unit |
Target molar unit: |
The inverse of n2W_spec_irradiance: photon flux is recovered
via , then divided by Avogadro's constant scaled to
the requested molar unit:
where mol and
for "umol", for "mmol", or for "mol".
Numeric vector of photon flux in the requested molar unit per nm,
or a lux_spectrum with the updated unit. Radiance outputs retain
the "/sr" term.
sp <- solar_irradiance("clear_noon") W2mol_spec_irradiance(sp$irradiance, sp$wavelength, molar_unit = "umol")sp <- solar_irradiance("clear_noon") W2mol_spec_irradiance(sp$irradiance, sp$wavelength, molar_unit = "umol")
Inverse of photon2W.
W2photon(W, lambda)W2photon(W, lambda)
W |
Spectral irradiance in |
lambda |
Wavelength(s) in nm. |
Numeric vector of photon counts in .
W2photon(1, 555)W2photon(1, 555)
Returns the in-medium wavelength after accounting for the refractive index. Frequency (and photon energy) are unchanged; only the spatial period shortens.
wavelength_in_medium(lambda, n)wavelength_in_medium(lambda, n)
lambda |
Wavelength in vacuum or air (nm, or any consistent unit). Vectorised. |
n |
Refractive index of the medium (dimensionless). Must be >= 1. |
Wavelength in the medium, in the same unit as lambda.
wavelength_in_medium(500, n = 1.336) # seawater: ~374 nm in medium wavelength_in_medium(c(400, 500, 600), n = 1.5)wavelength_in_medium(500, n = 1.336) # seawater: ~374 nm in medium wavelength_in_medium(c(400, 500, 600), n = 1.5)
Computes the Weber contrast: . Positive values indicate a target brighter than its
background; negative values indicate a darker target. Commonly used for
isolated objects on a uniform background.
weber_contrast(I_target, I_background)weber_contrast(I_target, I_background)
I_target |
Luminance or radiance of the target (scalar or vector). |
I_background |
Luminance or radiance of the background (scalar or vector). Must be non-zero. |
Contrast value(s); unbounded but typically in .
[michelson_contrast()]
weber_contrast(I_target = 8, I_background = 4)weber_contrast(I_target = 8, I_background = 4)