Migrated from uom to custom quantity implementation.

This commit is contained in:
Zachary Sunforge
2024-07-09 22:37:00 -07:00
parent 8a23429e5f
commit da5fca74ef
15 changed files with 612 additions and 132 deletions

View File

@ -9,7 +9,7 @@ members = [
]
[workspace.package]
version = "0.3.4"
version = "0.3.5"
edition = "2021"
repository = "https://git.bfpower.io/BFPOWER/physical"
readme = "README.md"
@ -22,11 +22,8 @@ version = "0.2.*"
default-features = false
[workspace.dependencies.libm]
version = "0.2.*"
# Units of measurement
[workspace.dependencies.uom]
version = "0.36.*"
default-features = false
features = ["f32", "si"]
[workspace.dependencies.float-cmp]
version = "0.9.*"
# Logging
[workspace.dependencies.tracing]
version = "0.1.*"
@ -61,7 +58,6 @@ git = "https://git.bfpower.io/BFPOWER/bfpower-drivers.git"
features = ["defmt"]
[workspace.dependencies.ads1256]
git = "https://git.bfpower.io/BFPOWER/bfpower-drivers.git"
features = ["uom"]
# Embassy
[workspace.dependencies.embassy-futures]
version = "0.1.*"
@ -85,7 +81,9 @@ features = ["defmt", "unstable-pac"]
[workspace.dependencies.embassy-nrf]
version = "0.1.*"
features = ["defmt"]
# Macros
# Meta
[workspace.dependencies.derive_more]
version = "0.99.*"
[workspace.dependencies.syn]
version = "2.0.*"
features = ["extra-traits", "parsing"]
@ -107,6 +105,8 @@ readme.workspace = true
license.workspace = true
[features]
std = ["num-traits/std"]
libm = ["dep:libm", "num-traits/libm"]
resistive-divider = []
thermocouple-k = ["libm"]
thermistor = ["libm"]
@ -114,11 +114,22 @@ lm35 = []
pid = []
stm32 = []
[dependencies]
uom = { workspace = true }
num-traits = { workspace = true }
libm = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
[dependencies.num-traits]
workspace = true
[dependencies.derive_more]
workspace = true
[dependencies.defmt]
workspace = true
optional = true
[dependencies.libm]
workspace = true
optional = true
[dependencies.serde]
workspace = true
optional = true
[dev-dependencies.float-cmp]
workspace = true
#---------------------------------------------------------------------------------------------------------------------
#----- Profiles ------------------------

View File

@ -21,8 +21,6 @@ workspace = true
workspace = true
[dependencies.defmt]
workspace = true
[dependencies.uom]
workspace = true
[dependencies.embassy-stm32]
workspace = true
optional = true

View File

@ -1,13 +1,9 @@
use uom::si::electric_potential::millivolt;
use uom::si::quantities::ElectricPotential;
use crate::quantity::{MilliVolts, Quantity};
pub fn reading_to_voltage(
reading: u16,
reference_voltage: ElectricPotential<f32>,
v_ref_int_scale: f32,
) -> ElectricPotential<f32> {
let reference_mv = reference_voltage.get::<millivolt>();
let reading = f32::from(reading);
ElectricPotential::new::<millivolt>(reading * v_ref_int_scale / reference_mv)
reading: u32,
reference_voltage: MilliVolts<u32>,
v_ref_int_scale: u32,
) -> MilliVolts<u16> {
MilliVolts((reading * v_ref_int_scale / reference_voltage.value()) as u16)
}

View File

@ -1,31 +1,19 @@
use uom::si::electric_potential::volt;
use uom::si::electrical_resistance::ohm;
use uom::si::quantities::{ElectricPotential, ElectricalResistance};
use crate::quantity::{Ohms, Quantity, Volts};
/// Given the resistance of the second resistor in a resistive voltage divider, calculate the resistance of the first resistor.
pub fn solve_r1(
voltage_src: ElectricPotential<f32>,
voltage_read: ElectricPotential<f32>,
r2: ElectricalResistance<f32>,
) -> ElectricalResistance<f32> {
let volts_src = voltage_src.get::<volt>();
let volts_read = voltage_read.get::<volt>();
let ohms2 = r2.get::<ohm>();
let ohms1 = ohms2 * (volts_src / volts_read - 1.0);
ElectricalResistance::new::<ohm>(ohms1)
voltage_src: Volts<f32>,
voltage_read: Volts<f32>,
r2: Ohms<f32>,
) -> Ohms<f32> {
Ohms(r2.value() * (voltage_src.value() / voltage_read.value() - 1.0))
}
/// Given the resistance of the first resistor in a resistive voltage divider, calculate the resistance of the second resistor.
pub fn solve_r2(
voltage_src: ElectricPotential<f32>,
voltage_read: ElectricPotential<f32>,
r1: ElectricalResistance<f32>,
) -> ElectricalResistance<f32> {
let volts_src = voltage_src.get::<volt>();
let volts_read = voltage_read.get::<volt>();
let ohms1 = r1.get::<ohm>();
let ohms2 = ohms1 * (1.0 / (volts_src / volts_read) - 1.0);
ElectricalResistance::new::<ohm>(ohms2)
voltage_src: Volts<f32>,
voltage_read: Volts<f32>,
r1: Ohms<f32>,
) -> Ohms<f32> {
Ohms(r1.value() * (1.0 / (voltage_src.value() / voltage_read.value()) - 1.0))
}

View File

@ -1,11 +1,11 @@
#![no_std]
#![cfg_attr(not(feature = "std"), no_std)]
pub mod transducer;
pub mod control;
pub mod error;
pub mod adc;
pub mod uom;
pub mod circuit;
pub mod quantity;
pub use error::CriticalError;

View File

@ -0,0 +1,22 @@
use derive_more::{Add, AddAssign, Display, Sub, SubAssign};
use crate::quantity::{Quantity, Value};
// ---------------------------------------------------------------------------------------------------------------------
// ----- Watts per Square Meter ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct WattsPerSquareMeter<V: Value>(pub V);
impl <V: Value> Quantity<V> for WattsPerSquareMeter<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
"W/m²"
}
}

205
src/quantity/mod.rs Normal file
View File

@ -0,0 +1,205 @@
mod irradiance;
mod resistance;
mod temperature;
mod voltage;
mod volume_rate;
#[cfg(feature = "defmt")]
pub use defmt_impl::*;
pub use irradiance::*;
pub use resistance::*;
pub use temperature::*;
pub use voltage::*;
pub use volume_rate::*;
use core::fmt::Display;
use core::marker::PhantomData;
use core::ops::{Add, Sub};
use num_traits::{FromPrimitive, Num, NumCast};
const DECA: u8 = 10;
const DECI: u8 = 10;
const HECTO: u8 = 100;
const CENTI: u8 = 100;
const KILO: u16 = 1_000;
const MILLI: u16 = 1_000;
const MEGA: u32 = 1_000_000;
const MICRO: u32 = 1_000_000;
const GIGA: u32 = 1_000_000_000;
const NANO: u32 = 1_000_000_000;
pub trait Quantity<V: Value>: Copy + PartialEq + PartialOrd + Add + Sub {
fn value(self) -> V;
fn symbol() -> &'static str;
/// Returns a wrapper that implements [Display] and [defmt::Format] for [Quantity]s with core number values.
///
/// - `rounding` - Sets the number of decimal places to round to when formatting (currently ignored with defmt).
#[inline(always)]
fn fmt(self, rounding: Option<usize>) -> Fmt<V, Self> {
Fmt::new(self, rounding)
}
}
pub trait Value: Num + Copy + PartialOrd + FromPrimitive + NumCast + Display {
//----- Temperature ----------------------------------
#[inline(always)]
fn kelvins(self) -> Kelvins<Self> {
Kelvins(self)
}
#[inline(always)]
fn deci_kelvins(self) -> DeciKelvins<Self> {
DeciKelvins(self)
}
#[inline(always)]
fn degrees_celsius(self) -> DegreesCelsius<Self> {
DegreesCelsius(self)
}
#[inline(always)]
fn deci_degrees_celsius(self) -> DeciDegreesCelsius<Self> {
DeciDegreesCelsius(self)
}
//----- Voltage ----------------------------------
#[inline(always)]
fn volts(self) -> Volts<Self> {
Volts(self)
}
#[inline(always)]
fn millivolts(self) -> MilliVolts<Self> {
MilliVolts(self)
}
//----- Resistance ----------------------------------
#[inline(always)]
fn ohms(self) -> Ohms<Self> {
Ohms(self)
}
//----- Volume rate ----------------------------------
#[inline(always)]
fn liters_per_minute(self) -> LitersPerMinute<Self> {
LitersPerMinute(self)
}
//----- Irradiance ----------------------------------
#[inline(always)]
fn watts_per_square_meter(self) -> WattsPerSquareMeter<Self> {
WattsPerSquareMeter(self)
}
}
impl<V> Value for V where V: Num + Copy + PartialOrd + FromPrimitive + NumCast + Display {}
#[derive(Copy, Clone)]
pub struct Fmt<V: Value, Q: Quantity<V>> {
quantity: Q,
rounding: Option<usize>,
_phantom: PhantomData<V>,
}
impl<V: Value, Q: Quantity<V>> Fmt<V, Q> {
#[inline(always)]
fn new(quantity: Q, rounding: Option<usize>) -> Self {
Self {
quantity,
rounding,
_phantom: PhantomData,
}
}
}
impl<V: Value, Q: Quantity<V>> Display for Fmt<V, Q> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.rounding {
Some(places) => {
write!(f, "{:.prec$} {}", self.quantity.value(), Q::symbol(), prec = places)
},
None => write!(f, "{} {}", self.quantity.value(), Q::symbol()),
}
}
}
#[cfg(feature = "defmt")]
mod defmt_impl {
use super::*;
use defmt::{write, Format, Formatter};
impl<Q: Quantity<u8>> Format for Fmt<u8, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<u16>> Format for Fmt<u16, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<u32>> Format for Fmt<u32, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<u64>> Format for Fmt<u64, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<u128>> Format for Fmt<u128, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<i8>> Format for Fmt<i8, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<i16>> Format for Fmt<i16, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<i32>> Format for Fmt<i32, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<i64>> Format for Fmt<i64, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<i128>> Format for Fmt<i128, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<f32>> Format for Fmt<f32, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
impl<Q: Quantity<f64>> Format for Fmt<f64, Q> {
fn format(&self, fmt: Formatter) {
write!(fmt, "{} {}", self.quantity.value(), Q::symbol());
}
}
}

View File

@ -0,0 +1,22 @@
use derive_more::{Add, AddAssign, Display, Sub, SubAssign};
use crate::quantity::{Quantity, Value};
// ---------------------------------------------------------------------------------------------------------------------
// ----- Ohms ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct Ohms<V: Value>(pub V);
impl <V: Value> Quantity<V> for Ohms<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
"Ω"
}
}

165
src/quantity/temperature.rs Normal file
View File

@ -0,0 +1,165 @@
use derive_more::{Add, AddAssign, Display, Sub, SubAssign};
use num_traits::float::FloatCore;
use crate::quantity::{DECI, Quantity, Value};
const KELVIN_CELSIUS_OFFSET: f64 = 273.15;
// ---------------------------------------------------------------------------------------------------------------------
// ----- Kelvins ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct Kelvins<V: Value>(pub V);
impl <V: Value> Quantity<V> for Kelvins<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
"K"
}
}
impl <V: Value> Kelvins<V> {
#[inline]
pub fn to_degrees_celsius(self) -> DegreesCelsius<V> {
//TODO: Make const
let offset = V::from_f64(KELVIN_CELSIUS_OFFSET).unwrap();
DegreesCelsius(self.0 - offset)
}
#[inline]
pub fn to_deci_kelvins(self) -> DeciKelvins<V> {
let multiplier = V::from_u8(DECI).unwrap();
DeciKelvins(self.0 * multiplier)
}
}
// ---------------------------------------------------------------------------------------------------------------------
// ----- Deci Kelvins ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct DeciKelvins<V: Value>(pub V);
impl <V: Value> Quantity<V> for DeciKelvins<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
"dK"
}
}
impl<V: Value> DeciKelvins<V> {
#[inline]
pub fn to_kelvins(self) -> Kelvins<V> {
let divisor = V::from_u8(DECI).unwrap();
Kelvins(self.0 / divisor)
}
}
// ---------------------------------------------------------------------------------------------------------------------
// ----- Degrees Celsius ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct DegreesCelsius<V: Value>(pub V);
impl <V: Value> Quantity<V> for DegreesCelsius<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
""
}
}
impl <V: Value> DegreesCelsius<V> {
#[inline]
pub fn to_kelvins(self) -> Kelvins<V> {
//TODO: Make const
let offset = V::from_f64(KELVIN_CELSIUS_OFFSET).unwrap();
Kelvins(self.0 + offset)
}
#[inline]
pub fn to_deci_degrees_celsius(self) -> DeciDegreesCelsius<V> {
let multiplier = V::from_u8(DECI).unwrap();
DeciDegreesCelsius(self.0 * multiplier)
}
}
// ---------------------------------------------------------------------------------------------------------------------
// ----- Deci Degrees Celsius ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct DeciDegreesCelsius<V: Value>(pub V);
impl <V: Value> Quantity<V> for DeciDegreesCelsius<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
"d℃"
}
}
impl<V: Value> DeciDegreesCelsius<V> {
#[inline]
pub fn to_degrees_celsius(self) -> DegreesCelsius<V> {
let divisor = V::from_u8(DECI).unwrap();
DegreesCelsius(self.0 / divisor)
}
}
// ---------------------------------------------------------------------------------------------------------------------
// ----- Tests ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use float_cmp::assert_approx_eq;
use super::*;
#[test]
fn convert_f32() {
let kelvins: Kelvins<f32> = 298.15.kelvins();
let deci_kelvins: DeciKelvins<f32> = 2_981.5.deci_kelvins();
let celsius: DegreesCelsius<f32> = 25.0.degrees_celsius();
let deci_celsius: DeciDegreesCelsius<f32> = 250.0.deci_degrees_celsius();
assert_approx_eq!(f32, kelvins.to_degrees_celsius().0, celsius.0);
assert_approx_eq!(f32, celsius.to_kelvins().0, kelvins.0);
assert_approx_eq!(f32, deci_kelvins.to_kelvins().0, kelvins.0);
assert_approx_eq!(f32, kelvins.to_deci_kelvins().0, deci_kelvins.0);
assert_approx_eq!(f32, deci_celsius.to_degrees_celsius().0, celsius.0);
assert_approx_eq!(f32, celsius.to_deci_degrees_celsius().0, deci_celsius.0);
}
#[test]
fn convert_u16() {
let kelvins: Kelvins<u16> = 298.kelvins();
let degrees_celsius: DegreesCelsius<u16> = 25.degrees_celsius();
assert_eq!(degrees_celsius.0, kelvins.to_degrees_celsius().0);
}
}

86
src/quantity/voltage.rs Normal file
View File

@ -0,0 +1,86 @@
use derive_more::{Add, AddAssign, Display, Sub, SubAssign};
use crate::quantity::{Quantity, Value};
const VOLT_MV_RATIO: u16 = 1_000;
// ---------------------------------------------------------------------------------------------------------------------
// ----- Volts ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct Volts<V: Value>(pub V);
impl <V: Value> Quantity<V> for Volts<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
"V"
}
}
impl<V: Value> Volts<V> {
#[inline]
pub fn to_millivolts(self) -> MilliVolts<V> {
let multiplier = V::from_u16(VOLT_MV_RATIO).unwrap();
MilliVolts(self.0 * multiplier)
}
}
// ---------------------------------------------------------------------------------------------------------------------
// ----- MilliVolts ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct MilliVolts<V: Value>(pub V);
impl <V: Value> Quantity<V> for MilliVolts<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
"mV"
}
}
impl<V: Value> MilliVolts<V> {
pub fn to_volts(self) -> Volts<V> {
let divisor = V::from_u16(VOLT_MV_RATIO).unwrap();
Volts(self.0 / divisor)
}
}
// ---------------------------------------------------------------------------------------------------------------------
// ----- Tests ------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use float_cmp::assert_approx_eq;
use super::*;
#[test]
fn convert_u32() {
let volts: Volts<u32> = 3.volts();
let millivolts: MilliVolts<u32> = 3_000.millivolts();
assert_eq!(volts.to_millivolts().0, millivolts.0);
assert_eq!(millivolts.to_volts().0, volts.0);
}
#[test]
fn convert_f64() {
let volts: Volts<f64> = 3.0.volts();
let millivolts: MilliVolts<f64> = 3_000.0.millivolts();
assert_approx_eq!(f64, volts.to_millivolts().0, millivolts.0);
assert_approx_eq!(f64, millivolts.to_volts().0, volts.0);
}
}

View File

@ -0,0 +1,22 @@
use derive_more::{Add, AddAssign, Display, Sub, SubAssign};
use crate::quantity::{Quantity, Value};
// ---------------------------------------------------------------------------------------------------------------------
// ----- Liters per Minute------------------------
// ---------------------------------------------------------------------------------------------------------------------
#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)]
#[display(fmt = "{} {}", _0, "Self::symbol()")]
#[repr(transparent)]
pub struct LitersPerMinute<V: Value>(pub V);
impl <V: Value> Quantity<V> for LitersPerMinute<V> {
#[inline(always)]
fn value(self) -> V {
self.0
}
#[inline(always)]
fn symbol() -> &'static str {
"L/min"
}
}

View File

@ -1,21 +1,15 @@
use crate::error::InvalidValue;
use uom::si::electric_potential::volt;
use uom::si::quantities::{ElectricPotential, ThermodynamicTemperature};
use uom::si::thermodynamic_temperature::degree_celsius;
use crate::quantity::{DeciDegreesCelsius, MilliVolts, Quantity};
#[inline]
pub fn convert(
voltage: ElectricPotential<f32>,
) -> Result<ThermodynamicTemperature<f32>, InvalidValue> {
const MIN_VOLTS: f32 = -0.55;
const MAX_VOLTS: f32 = 1.50;
const SCALE_FACTOR: f32 = 100.0;
voltage: MilliVolts<i16>,
) -> Result<DeciDegreesCelsius<i16>, InvalidValue> {
const MIN_VOLTAGE: MilliVolts<i16> = MilliVolts(-550);
const MAX_VOLTAGE: MilliVolts<i16> = MilliVolts(1_500);
let volts = voltage.get::<volt>();
if volts >= MIN_VOLTS && volts <= MAX_VOLTS {
let celsius = volts * SCALE_FACTOR;
Ok(ThermodynamicTemperature::new::<degree_celsius>(celsius))
if voltage >= MIN_VOLTAGE && voltage <= MAX_VOLTAGE {
Ok(DeciDegreesCelsius(voltage.value()))
} else {
Err(InvalidValue)
}

View File

@ -1,33 +1,25 @@
use libm::{log, logf};
use uom::si::electrical_resistance::ohm;
use uom::si::quantities::{ThermodynamicTemperature, ElectricalResistance};
use uom::si::thermodynamic_temperature::kelvin;
use crate::quantity::{Kelvins, Ohms};
/// Convert thermistor resistance to a temperature using beta parameter equation
pub fn convert_beta(
resistance: ElectricalResistance<f32>,
resistance: Ohms<f32>,
beta: f32,
reference_temp: ThermodynamicTemperature<f32>,
reference_resist: ElectricalResistance<f32>,
) -> ThermodynamicTemperature<f32> {
let ohms = resistance.get::<ohm>();
let reference_kelvin = reference_temp.get::<kelvin>();
let reference_ohms = reference_resist.get::<ohm>();
let result_kelvin = 1.0 / ((1.0 / reference_kelvin) + (1.0 / beta) * logf(ohms / reference_ohms));
ThermodynamicTemperature::new::<kelvin>(result_kelvin)
reference_temp: Kelvins<f32>,
reference_resist: Ohms<f32>,
) -> Kelvins<f32> {
let kelvins = 1.0 / ((1.0 / reference_temp.0) + (1.0 / beta) * logf(resistance.0 / reference_resist.0));
Kelvins(kelvins)
}
/// Convert thermistor resistance to a temperature using Steinhart-Hart equation
pub fn convert_steinhart(
resistance: ElectricalResistance<f32>,
resistance: Ohms<f64>,
a: f64,
b: f64,
c: f64,
) -> ThermodynamicTemperature<f32> {
let ohms = resistance.get::<ohm>() as f64;
let log_omhs = log(ohms);
) -> Kelvins<f32> {
let log_omhs = log(resistance.0);
let kelvins = 1.0 / (a + b * log_omhs + c * log_omhs * log_omhs * log_omhs);
ThermodynamicTemperature::new::<kelvin>(kelvins as f32)
Kelvins(kelvins as f32)
}

View File

@ -2,14 +2,12 @@
use libm::pow;
use crate::error::InvalidValue;
use uom::si::electric_potential::millivolt;
use uom::si::quantities::{ElectricPotential, ThermodynamicTemperature};
use uom::si::thermodynamic_temperature::degree_celsius;
use crate::quantity::{DegreesCelsius, MilliVolts, Quantity};
fn _convert(
voltage: ElectricPotential<f32>,
) -> Result<ThermodynamicTemperature<f32>, InvalidValue> {
let mv = voltage.get::<millivolt>() as f64;
voltage: MilliVolts<f64>,
) -> Result<DegreesCelsius<f32>, InvalidValue> {
let mv = voltage.value();
let mv_pow2 = mv * mv;
let mv_pow3 = mv_pow2 * mv;
let mv_pow4 = mv_pow3 * mv;
@ -29,7 +27,7 @@ fn _convert(
+ -1.0450598E-2 * mv_pow7
+ -5.1920577E-4 * mv_pow8;
Ok(ThermodynamicTemperature::new::<degree_celsius>(celsius as f32))
Ok(DegreesCelsius(celsius as f32))
} else if mv > 0.0 && mv < 20.644 {
let mv_pow7 = mv_pow6 * mv;
let mv_pow8 = mv_pow7 * mv;
@ -45,7 +43,7 @@ fn _convert(
+ 1.057734E-6 * mv_pow8
+ -1.052755E-8 * mv_pow9;
Ok(ThermodynamicTemperature::new::<degree_celsius>(celsius as f32))
Ok(DegreesCelsius(celsius as f32))
} else if mv >= 20.644 && mv <= 54.886 {
let celsius = 1.318058e2
+ 4.830222E+1 * mv
@ -55,7 +53,7 @@ fn _convert(
+ 8.802193E-6 * mv_pow5
+ -3.110810E-8 * mv_pow6;
Ok(ThermodynamicTemperature::new::<degree_celsius>(celsius as f32))
Ok(DegreesCelsius(celsius as f32))
} else {
Err(InvalidValue)
}
@ -70,13 +68,12 @@ fn _convert(
/// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html).
#[inline]
pub fn convert_direct(
voltage: ElectricPotential<f32>,
r_junction: ThermodynamicTemperature<f32>,
) -> Result<ThermodynamicTemperature<f32>, InvalidValue> {
let base_celsius = _convert(voltage)?.get::<degree_celsius>();
let r_junction_celsius = r_junction.get::<degree_celsius>();
voltage: MilliVolts<f64>,
r_junction: DegreesCelsius<f32>,
) -> Result<DegreesCelsius<f32>, InvalidValue> {
let base_temp = _convert(voltage)?;
Ok(ThermodynamicTemperature::new::<degree_celsius>(base_celsius + r_junction_celsius))
Ok(base_temp + r_junction)
}
/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial and
@ -87,11 +84,11 @@ pub fn convert_direct(
/// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html).
#[inline]
pub fn convert_seebeck(
voltage: ElectricPotential<f32>,
r_junction: ThermodynamicTemperature<f32>,
) -> Result<ThermodynamicTemperature<f32>, InvalidValue> {
voltage: MilliVolts<f64>,
r_junction: DegreesCelsius<f32>,
) -> Result<DegreesCelsius<f32>, InvalidValue> {
let voltage_correction = temp_to_voltage_seebeck(r_junction)?;
_convert(voltage + voltage_correction)
_convert(MilliVolts(voltage.0 + voltage_correction.0 as f64))
}
/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial and
@ -102,17 +99,17 @@ pub fn convert_seebeck(
/// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html).
#[inline]
pub fn convert_polynomial(
voltage: ElectricPotential<f32>,
r_junction: ThermodynamicTemperature<f32>,
) -> Result<ThermodynamicTemperature<f32>, InvalidValue> {
voltage: MilliVolts<f64>,
r_junction: DegreesCelsius<f64>,
) -> Result<DegreesCelsius<f32>, InvalidValue> {
let voltage_correction = temp_to_voltage_poly(r_junction)?;
_convert(voltage + voltage_correction)
_convert(MilliVolts(voltage.0 + voltage_correction.0 as f64))
}
pub fn temp_to_voltage_poly(
temperature: ThermodynamicTemperature<f32>,
) -> Result<ElectricPotential<f32>, InvalidValue> {
let celsius = temperature.get::<degree_celsius>() as f64;
temperature: DegreesCelsius<f64>,
) -> Result<MilliVolts<f32>, InvalidValue> {
let celsius = temperature.value();
let cel_pow2 = celsius * celsius;
let cel_pow3 = cel_pow2 * celsius;
let cel_pow4 = cel_pow3 * celsius;
@ -136,7 +133,7 @@ pub fn temp_to_voltage_poly(
+ -0.198892668780E-19 * cel_pow9
+ -0.163226974860E-22 * cel_pow10;
Ok(ElectricPotential::new::<millivolt>(mv as f32))
Ok(MilliVolts(mv as f32))
} else if celsius >= 0.0 && celsius <= 1372.0 {
let base = celsius - 0.126968600000E+03;
let exp = -0.118343200000E-03 * (base * base);
@ -154,7 +151,7 @@ pub fn temp_to_voltage_poly(
+ -0.121047212750E-25 * cel_pow9
+ addition;
Ok(ElectricPotential::new::<millivolt>(mv as f32))
Ok(MilliVolts(mv as f32))
} else {
Err(InvalidValue)
}
@ -162,12 +159,10 @@ pub fn temp_to_voltage_poly(
#[inline]
pub fn temp_to_voltage_seebeck(
temperature: ThermodynamicTemperature<f32>,
) -> Result<ElectricPotential<f32>, InvalidValue> {
let celsius = temperature.get::<degree_celsius>();
if celsius >= -2.0 && celsius <= 800.0 {
let mv = 0.041 * celsius;
Ok(ElectricPotential::new::<millivolt>(mv))
temperature: DegreesCelsius<f32>,
) -> Result<MilliVolts<f32>, InvalidValue> {
if temperature.value() >= -2.0 && temperature.value() <= 800.0 {
Ok(MilliVolts(0.041 * temperature.value()))
} else {
Err(InvalidValue)
}

View File

@ -1,16 +0,0 @@
//TODO: Everything in here is implemented standalone and not integrated with uom library because it is spawned from macro
// soup + has shit documentation and I cannot figure out how to add a custom unit to SI. This should be integrated with
// UoM
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Default)]
pub struct Irradiance(f32);
pub fn watts_per_sq_meter(value: f32) -> Irradiance {
Irradiance(value)
}
impl Irradiance {
fn to_watts_per_sq_meter(self) -> f32 {
self.0
}
}