#![allow(dead_code, unused)] use serde::{Deserialize, Serialize}; #[tokio::main] async fn main() -> anyhow::Result<()> { for n in 0..16 { let measurement: u16 = 65_535 / 16 * n; let millikelvin = (((measurement as u32) * 21_875u32) >> 13) + 228_150u32; let temperature = Temperature::from_millikelvin(millikelvin); println!("raw = {measurement}, converted = {millikelvin}, temperature = {temperature}"); } let measurement: u16 = 65_535; let millikelvin = (((measurement as u32) * 21_875u32) >> 13) + 228_150u32; let temperature = Temperature::from_millikelvin(millikelvin); println!("raw = {measurement}, converted = {millikelvin}, temperature = {temperature}"); Ok(()) } #[derive(Clone, Debug, thiserror::Error)] pub enum TemperatureError { #[error("provided input is invalid: {0}")] InvalidInput(f32), #[error("conversion between units changed the value noticeably")] ConversionError, } /// type for temperature /// represents the temperature in Millikelvin #[derive(Clone, Copy, Debug, Deserialize, Serialize)] pub struct Temperature{ value: u32, } impl Temperature { #[allow(non_upper_case_globals)] const CELSIUS_OFFSET_C: f32 = 273.15f32; /// create a new instance of `Temperature` from a value in Millikelvin /// this function is an alias for `from_millikelvin()` pub fn new(millikelvin: u32) -> Self { Self::from_millikelvin(millikelvin) } /// create a new instance of `Temperature` from a value in Millikelvin pub fn from_millikelvin(millikelvin: u32) -> Self { Self { value: millikelvin } } /// create a new instance of `Temperature` from a value in degrees Celsius pub fn from_celsius(celsius: f32) -> Result { if celsius < -Self::CELSIUS_OFFSET_C { return Err(TemperatureError::InvalidInput(celsius)) } Ok(Self { value: Self::celsius_to_millikelvin(celsius)? }) } /// returns the temperature value in Millikelvin pub fn get_millikelvin(&self) -> u32 { self.value } /// returns the temperature value in degrees celsius pub fn get_celsius(&self) -> Result { Self::millikelvin_to_celsius(self.value) } fn celsius_to_millikelvin(celsius: f32) -> Result { const ACCEPTABLE_INACCURACY: f32 = 1.0 / 16.0; let millikelvin: u32 = Self::celsius_to_millikelvin_unchecked(celsius); let celsius_converted: f32 = Self::millikelvin_to_celsius_unchecked(millikelvin); if f32::abs(celsius - celsius_converted) > ACCEPTABLE_INACCURACY { return Err(TemperatureError::ConversionError); } Ok(millikelvin) } fn millikelvin_to_celsius(millikelvin: u32) -> Result { const ACCEPTABLE_INACCURACY: u32 = 80; let celsius: f32 = Self::millikelvin_to_celsius_unchecked(millikelvin); let millikelvin_converted: u32 = Self::celsius_to_millikelvin_unchecked(celsius); if millikelvin.wrapping_sub(millikelvin_converted) > ACCEPTABLE_INACCURACY { return Err(TemperatureError::ConversionError); } Ok(celsius) } fn celsius_to_millikelvin_unchecked(celsius: f32) -> u32 { ((celsius + Self::CELSIUS_OFFSET_C) * 1000.0) as u32 } fn millikelvin_to_celsius_unchecked(millikelvin: u32) -> f32 { let millicelsius: f32 = (millikelvin as f32) / 1000.0; millicelsius - Self::CELSIUS_OFFSET_C } } impl std::fmt::Display for Temperature { // display `Temperatures` in degrees Celsius fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Ok(value) = Self::millikelvin_to_celsius(self.value) { write!(f, "{:.2?}°C", value)?; } else { return Err(std::fmt::Error::default()); } Ok(()) } }