Migrated from uom to custom quantity implementation.
This commit is contained in:
37
Cargo.toml
37
Cargo.toml
@ -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 ------------------------
|
||||
|
@ -21,8 +21,6 @@ workspace = true
|
||||
workspace = true
|
||||
[dependencies.defmt]
|
||||
workspace = true
|
||||
[dependencies.uom]
|
||||
workspace = true
|
||||
[dependencies.embassy-stm32]
|
||||
workspace = true
|
||||
optional = true
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub mod transducer;
|
||||
pub mod control;
|
||||
@ -7,5 +7,6 @@ pub mod error;
|
||||
pub mod adc;
|
||||
pub mod uom;
|
||||
pub mod circuit;
|
||||
pub mod quantity;
|
||||
|
||||
pub use error::CriticalError;
|
||||
|
22
src/quantity/irradiance.rs
Normal file
22
src/quantity/irradiance.rs
Normal 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 WatterPerSquareMeter<V: Value>(pub V);
|
||||
|
||||
impl <V: Value> Quantity<V> for WatterPerSquareMeter<V> {
|
||||
#[inline(always)]
|
||||
fn value(self) -> V {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn symbol() -> &'static str {
|
||||
"W/m²"
|
||||
}
|
||||
}
|
205
src/quantity/mod.rs
Normal file
205
src/quantity/mod.rs
Normal 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) -> WatterPerSquareMeter<Self> {
|
||||
WatterPerSquareMeter(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());
|
||||
}
|
||||
}
|
||||
}
|
22
src/quantity/resistance.rs
Normal file
22
src/quantity/resistance.rs
Normal 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 {
|
||||
"Ω"
|
||||
}
|
||||
}
|
157
src/quantity/temperature.rs
Normal file
157
src/quantity/temperature.rs
Normal file
@ -0,0 +1,157 @@
|
||||
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 + FloatCore> 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 + FloatCore> 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() {
|
||||
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);
|
||||
}
|
||||
}
|
86
src/quantity/voltage.rs
Normal file
86
src/quantity/voltage.rs
Normal 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);
|
||||
}
|
||||
}
|
22
src/quantity/volume_rate.rs
Normal file
22
src/quantity/volume_rate.rs
Normal 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"
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user