use crate::units::{Decibels, Millicelsius, Millivolts, Percent};
#[must_use]
pub fn battery_percent(voltage: Millivolts) -> Percent {
let level = (i32::from(voltage.0) - 3300) * 100 / 800;
Percent::new(u8::try_from(level.clamp(0, 100)).unwrap_or(100))
}
pub fn volume_register(volume: Percent) -> u8 {
let step = u16::from(volume.get()) * 0xBF / 100;
u8::try_from(step).unwrap_or(0xBF)
}
pub const fn microphone_gain_step(gain: Decibels) -> u8 {
if gain.0 > 30 { 10 } else { gain.0 / 3 }
}
pub const fn milli_g(raw: i32) -> i32 {
raw * 1000 / 4096
}
pub const fn millidegrees_per_second(raw: i32) -> i32 {
raw * 15_625 / 256
}
pub const fn millicelsius(raw: i16) -> Option<Millicelsius> {
if raw == i16::MIN {
None
} else {
Some(Millicelsius(23_000 + raw as i32 * 1000 / 512))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_battery_is_empty_at_3_3_volts_and_full_at_4_1() {
let percent = |millivolts| battery_percent(Millivolts(millivolts)).get();
assert_eq!(percent(0), 0);
assert_eq!(percent(3300), 0);
assert_eq!(percent(3700), 50);
assert_eq!(percent(4100), 100);
assert_eq!(percent(4200), 100);
assert_eq!(percent(u16::MAX), 100);
}
#[test]
fn the_volume_runs_from_silent_to_0_db() {
assert_eq!(volume_register(Percent::ZERO), 0x00);
assert_eq!(volume_register(Percent::new(50)), 0x5F);
assert_eq!(volume_register(Percent::FULL), 0xBF);
assert_eq!(volume_register(Percent::new(255)), 0xBF);
}
#[test]
fn the_microphone_gain_rounds_down_to_3_db_steps() {
let step = |db| microphone_gain_step(Decibels(db));
assert_eq!(step(0), 0);
assert_eq!(step(2), 0);
assert_eq!(step(24), 8);
assert_eq!(step(30), 10);
assert_eq!(step(255), 10);
}
#[test]
fn the_temperature_is_23_degrees_at_zero_and_512_counts_a_degree() {
assert_eq!(millicelsius(0), Some(Millicelsius(23_000)));
assert_eq!(millicelsius(512), Some(Millicelsius(24_000)));
assert_eq!(millicelsius(-256), Some(Millicelsius(22_500)));
assert_eq!(millicelsius(i16::MAX), Some(Millicelsius(86_998)));
assert_eq!(millicelsius(i16::MIN), None, "no reading");
}
#[test]
fn motion_readings_scale_to_their_ranges() {
assert_eq!(milli_g(4096), 1000);
assert_eq!(milli_g(-4096), -1000);
assert_eq!(milli_g(i32::from(i16::MIN)), -8000);
assert_eq!(millidegrees_per_second(16_384), 1_000_000);
assert_eq!(millidegrees_per_second(i32::from(i16::MAX)), 1_999_938);
assert_eq!(millidegrees_per_second(i32::from(i16::MIN)), -2_000_000);
}
}