///|
/// Apply a finite affine intensity calibration; scale may be negative.
pub fn affine_intensity(
input : Spectrum,
scale : Double,
offset : Double,
) -> Spectrum raise SpectralError {
finite(scale, "intensity scale")
finite(offset, "intensity offset")
let y = Array::make(input.length(), 0.0)
for i in 0.. Spectrum raise SpectralError {
let center = mean(input.values)
let deviation = sample_deviation(input.values)
if deviation <= 1.0e-14 * root_mean_square(input.values) || deviation == 0.0 {
raise Numerical("SNV requires nonconstant intensities")
}
let y = Array::makei(input.length(), fn(i) {
(input.values[i] - center) / deviation
})
spectrum(input.axis, y)
}
///|
/// Normalize the full vector to unit Euclidean norm, retaining sign.
pub fn unit_vector(input : Spectrum) -> Spectrum raise SpectralError {
let rms = root_mean_square(input.values)
if rms == 0.0 {
raise Numerical("cannot normalize a zero spectrum")
}
let norm = rms * input.length().to_double().sqrt()
let y = input.values.map(fn(v) { v / norm })
spectrum(input.axis, y)
}
///|
/// Map min/max onto [0,1]. A constant spectrum is rejected explicitly.
pub fn range_normalize(input : Spectrum) -> Spectrum raise SpectralError {
let mut low = input.values[0]
let mut high = low
for v in input.values {
if v < low {
low = v
}
if v > high {
high = v
}
}
let span = high - low
if span == 0.0 {
raise Numerical("constant spectrum has no range")
}
spectrum(input.axis, input.values.map(fn(v) { (v - low) / span }))
}
///|
/// Convert transmittance to absorbance. Values must lie in (0,1] (fraction)
/// or (0,100] (percent). Zero is rejected instead of being silently clipped.
pub fn absorbance(
input : Spectrum,
percent~ : Bool = false,
) -> Spectrum raise SpectralError {
let denominator = if percent { 100.0 } else { 1.0 }
let y = Array::make(input.length(), 0.0)
for i in 0.. 1.0 {
raise Invalid("transmittance outside physical interval")
}
y[i] = -@math.log10(t)
}
spectrum(input.axis, y)
}
///|
/// Convert absorbance to fractional or percent transmittance.
pub fn transmittance(
input : Spectrum,
percent~ : Bool = false,
) -> Spectrum raise SpectralError {
let scale = if percent { 100.0 } else { 1.0 }
let y = Array::make(input.length(), 0.0)
for i in 0..