#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Calibration {
scale: f32,
offset: f32,
}
impl Calibration {
pub fn linear(scale: f32, offset: f32) -> Self {
Self { scale, offset }
}
pub fn two_point(raw_low: f32, value_low: f32, raw_high: f32, value_high: f32) -> Self {
let span = raw_high - raw_low;
let scale = if span == 0.0 {
0.0
} else {
(value_high - value_low) / span
};
Self {
scale,
offset: value_low - scale * raw_low,
}
}
pub fn apply(&self, raw: f32) -> f32 {
self.scale * raw + self.offset
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn two_point_maps_its_endpoints_exactly() {
let calibration = Calibration::two_point(0.5, 0.0, 2.5, 100.0);
assert!((calibration.apply(0.5) - 0.0).abs() < 1e-4);
assert!((calibration.apply(2.5) - 100.0).abs() < 1e-4);
}
#[test]
fn two_point_interpolates_linearly() {
let calibration = Calibration::two_point(0.5, 0.0, 2.5, 100.0);
assert!((calibration.apply(1.5) - 50.0).abs() < 1e-4);
}
#[test]
fn equal_raw_points_fall_back_to_a_constant() {
let calibration = Calibration::two_point(1.0, 42.0, 1.0, 99.0);
assert!((calibration.apply(5.0) - 42.0).abs() < 1e-4);
}
#[test]
fn linear_applies_scale_and_offset() {
let calibration = Calibration::linear(2.0, -1.0);
assert!((calibration.apply(3.0) - 5.0).abs() < 1e-4);
}
}