(rust) temperature type and conversions
This commit is contained in:
+103
-26
@@ -1,38 +1,115 @@
|
|||||||
#![allow(dead_code, unused)]
|
#![allow(dead_code, unused)]
|
||||||
|
|
||||||
use std::net::{IpAddr, SocketAddr, TcpStream};
|
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
use serde::{Deserialize, Serialize};
|
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]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
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 {
|
println!("raw = {measurement}, converted = {millikelvin}, temperature = {temperature}");
|
||||||
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)?;
|
|
||||||
}
|
}
|
||||||
|
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(())
|
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<Self, TemperatureError> {
|
||||||
|
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<f32, TemperatureError> {
|
||||||
|
Self::millikelvin_to_celsius(self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn celsius_to_millikelvin(celsius: f32) -> Result<u32, TemperatureError> {
|
||||||
|
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<f32, TemperatureError> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user