Initial proof of concept
This commit is contained in:
3
src/transducer/mod.rs
Normal file
3
src/transducer/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod part;
|
||||
|
||||
pub use part::*;
|
||||
16
src/transducer/part/lm35.rs
Normal file
16
src/transducer/part/lm35.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use crate::error::InvalidValue;
|
||||
use crate::quantity::{DeciCelsius, MilliVolts, Quantity};
|
||||
|
||||
#[inline]
|
||||
pub fn convert(
|
||||
voltage: MilliVolts<i16>,
|
||||
) -> Result<DeciCelsius<i16>, InvalidValue> {
|
||||
const MIN_VOLTAGE: MilliVolts<i16> = MilliVolts(-550);
|
||||
const MAX_VOLTAGE: MilliVolts<i16> = MilliVolts(1_500);
|
||||
|
||||
if voltage >= MIN_VOLTAGE && voltage <= MAX_VOLTAGE {
|
||||
Ok(DeciCelsius(voltage.value()))
|
||||
} else {
|
||||
Err(InvalidValue)
|
||||
}
|
||||
}
|
||||
10
src/transducer/part/mod.rs
Normal file
10
src/transducer/part/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
mod thermocouple;
|
||||
|
||||
#[cfg(feature = "lm35")]
|
||||
pub mod lm35;
|
||||
|
||||
#[cfg(feature = "thermistor")]
|
||||
pub mod thermistor;
|
||||
|
||||
#[cfg(feature = "thermocouple-k")]
|
||||
pub use thermocouple::type_k as thermocouple_k;
|
||||
38
src/transducer/part/thermistor.rs
Normal file
38
src/transducer/part/thermistor.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use crate::quantity::{Kelvins, Ohms};
|
||||
use libm::{log, logf};
|
||||
|
||||
/// Convert thermistor resistance to a temperature using beta parameter equation
|
||||
pub fn convert_beta(
|
||||
resistance: Ohms<f32>,
|
||||
beta: f32,
|
||||
reference_temp: Kelvins<f32>,
|
||||
reference_resist: Ohms<f32>,
|
||||
) -> Kelvins<f32> {
|
||||
let kelvins = 1.0 / ((logf(resistance.0 / reference_resist.0) / beta) + 1.0 / reference_temp.0);
|
||||
Kelvins(kelvins)
|
||||
}
|
||||
|
||||
/// Convert thermistor resistance to a temperature using Steinhart-Hart equation
|
||||
pub fn convert_steinhart(resistance: Ohms<f64>, a: f64, b: f64, c: f64) -> Kelvins<f32> {
|
||||
let log_omhs = log(resistance.0);
|
||||
let kelvins = 1.0 / (a + b * log_omhs + c * log_omhs * log_omhs * log_omhs);
|
||||
Kelvins(kelvins as f32)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Tests ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use float_cmp::assert_approx_eq;
|
||||
|
||||
use crate::quantity::{OhmsVal, KelvinsVal};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn convert_beta_test() {
|
||||
let temperature = convert_beta(1538.462.ohms(), 3950.0, 298.15.kelvins(), 100_000.0.ohms());
|
||||
|
||||
assert_approx_eq!(f32, 435.31073, temperature.0);
|
||||
}
|
||||
}
|
||||
2
src/transducer/part/thermocouple/mod.rs
Normal file
2
src/transducer/part/thermocouple/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "thermocouple-k")]
|
||||
pub mod type_k;
|
||||
169
src/transducer/part/thermocouple/type_k.rs
Normal file
169
src/transducer/part/thermocouple/type_k.rs
Normal file
@@ -0,0 +1,169 @@
|
||||
//! Note - Thermocouple conversion uses [f64] arithmetic internally.
|
||||
|
||||
use libm::pow;
|
||||
use crate::error::InvalidValue;
|
||||
use crate::quantity::{Celsius, MilliVolts, Quantity};
|
||||
|
||||
fn _convert(
|
||||
voltage: MilliVolts<f64>,
|
||||
) -> Result<Celsius<f32>, InvalidValue> {
|
||||
let mv = voltage.value();
|
||||
let mv_pow2 = mv * mv;
|
||||
let mv_pow3 = mv_pow2 * mv;
|
||||
let mv_pow4 = mv_pow3 * mv;
|
||||
let mv_pow5 = mv_pow4 * mv;
|
||||
let mv_pow6 = mv_pow5 * mv;
|
||||
|
||||
if mv >= -5.891 && mv <= 0.0 {
|
||||
let mv_pow7 = mv_pow6 * mv;
|
||||
let mv_pow8 = mv_pow7 * mv;
|
||||
|
||||
let celsius = 2.5173462E+1 * mv
|
||||
+ -1.1662878 * mv_pow2
|
||||
+ -1.0833638 * mv_pow3
|
||||
+ -8.9773540E-1 * mv_pow4
|
||||
+ -3.7342377E-1 * mv_pow5
|
||||
+ -8.6632643E-2 * mv_pow6
|
||||
+ -1.0450598E-2 * mv_pow7
|
||||
+ -5.1920577E-4 * mv_pow8;
|
||||
|
||||
Ok(Celsius(celsius as f32))
|
||||
} else if mv > 0.0 && mv < 20.644 {
|
||||
let mv_pow7 = mv_pow6 * mv;
|
||||
let mv_pow8 = mv_pow7 * mv;
|
||||
let mv_pow9 = mv_pow8 * mv;
|
||||
|
||||
let celsius = 2.508355E+1 * mv
|
||||
+ 7.860106E-2 * mv_pow2
|
||||
+ -2.503131E-1 * mv_pow3
|
||||
+ 8.315270E-2 * mv_pow4
|
||||
+ -1.228034E-2 * mv_pow5
|
||||
+ 9.804036E-4 * mv_pow6
|
||||
+ -4.413030E-5 * mv_pow7
|
||||
+ 1.057734E-6 * mv_pow8
|
||||
+ -1.052755E-8 * mv_pow9;
|
||||
|
||||
Ok(Celsius(celsius as f32))
|
||||
} else if mv >= 20.644 && mv <= 54.886 {
|
||||
let celsius = 1.318058e2
|
||||
+ 4.830222E+1 * mv
|
||||
+ -1.646031 * mv_pow2
|
||||
+ 5.464731E-2 * mv_pow3
|
||||
+ -9.650715E-4 * mv_pow4
|
||||
+ 8.802193E-6 * mv_pow5
|
||||
+ -3.110810E-8 * mv_pow6;
|
||||
|
||||
Ok(Celsius(celsius as f32))
|
||||
} else {
|
||||
Err(InvalidValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial and
|
||||
/// directly adding the reference junction temperature to the result for offset compensation.
|
||||
///
|
||||
/// Can be useful compared to [convert_seebeck] when the reference temperature or the temperature
|
||||
/// being read by the thermocouple is fairly close to 0.
|
||||
///
|
||||
/// 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: MilliVolts<f64>,
|
||||
r_junction: Celsius<f32>,
|
||||
) -> Result<Celsius<f32>, InvalidValue> {
|
||||
let base_temp = _convert(voltage)?;
|
||||
|
||||
Ok(base_temp + r_junction)
|
||||
}
|
||||
|
||||
/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial and
|
||||
/// using a constant seebeck coefficient to correct the input voltage for offset compensation.
|
||||
///
|
||||
/// Probably the right choice most of the time.
|
||||
///
|
||||
/// 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: MilliVolts<f64>,
|
||||
r_junction: Celsius<f32>,
|
||||
) -> Result<Celsius<f32>, InvalidValue> {
|
||||
let voltage_correction = temp_to_voltage_seebeck(r_junction)?;
|
||||
_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
|
||||
/// using a polynomial to correct the input voltage for offset compensation.
|
||||
///
|
||||
/// This is the most accurate method but uses the most processor cycles by a wide margin.
|
||||
///
|
||||
/// 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: MilliVolts<f64>,
|
||||
r_junction: Celsius<f64>,
|
||||
) -> Result<Celsius<f32>, InvalidValue> {
|
||||
let voltage_correction = temp_to_voltage_poly(r_junction)?;
|
||||
_convert(MilliVolts(voltage.0 + voltage_correction.0 as f64))
|
||||
}
|
||||
|
||||
pub fn temp_to_voltage_poly(
|
||||
temperature: Celsius<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;
|
||||
let cel_pow5 = cel_pow4 * celsius;
|
||||
let cel_pow6 = cel_pow5 * celsius;
|
||||
let cel_pow7 = cel_pow6 * celsius;
|
||||
let cel_pow8 = cel_pow7 * celsius;
|
||||
let cel_pow9 = cel_pow8 * celsius;
|
||||
|
||||
if celsius >= -270.0 && celsius < 0.0 {
|
||||
let cel_pow10 = cel_pow9 * celsius;
|
||||
|
||||
let mv = 0.394501280250E-01 * celsius
|
||||
+ 0.236223735980E-04 * cel_pow2
|
||||
+ -0.328589067840E-06 * cel_pow3
|
||||
+ -0.499048287770E-08 * cel_pow4
|
||||
+ -0.675090591730E-10 * cel_pow5
|
||||
+ -0.574103274280E-12 * cel_pow6
|
||||
+ -0.310888728940E-14 * cel_pow7
|
||||
+ -0.104516093650E-16 * cel_pow8
|
||||
+ -0.198892668780E-19 * cel_pow9
|
||||
+ -0.163226974860E-22 * cel_pow10;
|
||||
|
||||
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);
|
||||
let addition = pow(0.1185976, exp);
|
||||
|
||||
let mv = -0.176004136860E-01
|
||||
+ 0.389212049750E-01 * celsius
|
||||
+ 0.185587700320E-04 * cel_pow2
|
||||
+ -0.994575928740E-07 * cel_pow3
|
||||
+ 0.318409457190E-09 * cel_pow4
|
||||
+ -0.560728448890E-12 * cel_pow5
|
||||
+ 0.560750590590E-15 * cel_pow6
|
||||
+ -0.320207200030E-18 * cel_pow7
|
||||
+ 0.971511471520E-22 * cel_pow8
|
||||
+ -0.121047212750E-25 * cel_pow9
|
||||
+ addition;
|
||||
|
||||
Ok(MilliVolts(mv as f32))
|
||||
} else {
|
||||
Err(InvalidValue)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn temp_to_voltage_seebeck(
|
||||
temperature: Celsius<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