From cb463b02074f5afeb51de4e9e4610ab6c9eae606 Mon Sep 17 00:00:00 2001 From: scbj Date: Wed, 26 Nov 2025 12:35:48 +0100 Subject: [PATCH] (rust) temperature type and conversions --- rust/src/main.rs | 129 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/rust/src/main.rs b/rust/src/main.rs index dfbd9dc..289b939 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,38 +1,115 @@ #![allow(dead_code, unused)] -use std::net::{IpAddr, SocketAddr, TcpStream}; - -use clap::Parser; use serde::{Deserialize, Serialize}; -use serde_json::Value; -use tokio::{fs, io::AsyncWriteExt, net::TcpListener, sync::mpsc}; - -#[derive(Clone, Debug, Parser)] -pub struct Args { - #[arg(long, default_value_t = false)] - pub host: bool, - - #[arg(short, long)] - pub address: SocketAddr, -} #[tokio::main] async fn main() -> anyhow::Result<()> { - let args: Args = Parser::parse(); + 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); - if args.host { - let listener: TcpListener = TcpListener::bind(args.address).await?; - - loop { - if let Ok((mut stream, _)) = listener.accept().await { - println!("local address = {}, peer address = {}", stream.local_addr()?, stream.peer_addr()?); - stream.shutdown(); - } - } - } else { - let mut stream: TcpStream = TcpStream::connect(args.address)?; + 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(()) + } +}