#[allow(unused_imports)] use num_traits::float::FloatCore;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Measurement {
pub co2eq_ppm: u16,
pub tvoc_ppb: u16,
}
impl Measurement {
pub(crate) fn from_bytes(buf: &[u8; 6]) -> Self {
let co2eq_ppm = (u16::from(buf[0]) << 8) | u16::from(buf[1]);
let tvoc_ppb = (u16::from(buf[3]) << 8) | u16::from(buf[4]);
Self {
co2eq_ppm,
tvoc_ppb,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RawSignals {
pub h2: u16,
pub ethanol: u16,
}
impl RawSignals {
pub(crate) fn from_bytes(buf: &[u8; 6]) -> Self {
let h2 = (u16::from(buf[0]) << 8) | u16::from(buf[1]);
let ethanol = (u16::from(buf[3]) << 8) | u16::from(buf[4]);
Self { h2, ethanol }
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Baseline {
pub co2eq: u16,
pub tvoc: u16,
}
impl Baseline {
pub(crate) fn from_bytes(buf: &[u8; 6]) -> Self {
let measurement = Measurement::from_bytes(buf);
Baseline {
co2eq: measurement.co2eq_ppm,
tvoc: measurement.tvoc_ppb,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Humidity {
integer: u8, fractional: u8, }
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum HumidityError {
ZeroValue,
OutOfRange,
}
impl Humidity {
pub fn new(integer: u8, fractional: u8) -> Result<Self, HumidityError> {
if integer == 0 && fractional == 0 {
return Err(HumidityError::ZeroValue);
}
Ok(Humidity {
integer,
fractional,
})
}
pub fn from_f32(val: f32) -> Result<Self, HumidityError> {
if val.is_nan() {
return Err(HumidityError::OutOfRange);
}
let integer = if !(0.0..256.0).contains(&val) {
return Err(HumidityError::OutOfRange);
} else {
val.trunc() as u8
};
let fractional_f32 = val.fract() * 256.0f32;
let fractional = if fractional_f32 > 255.0 {
255
} else if fractional_f32 < 0.0 {
0
} else {
fractional_f32 as u8
};
Humidity::new(integer, fractional)
}
pub fn as_bytes(&self) -> [u8; 2] {
[self.integer, self.fractional]
}
}
impl From<Humidity> for f32 {
fn from(val: Humidity) -> Self {
f32::from(val.integer) + (f32::from(val.fractional) / 256.0)
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum ProductType {
Sgp30,
Unknown(u8),
}
impl ProductType {
pub fn parse(val: u8) -> Self {
match val {
0 => ProductType::Sgp30,
_ => ProductType::Unknown(val),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FeatureSet {
pub product_type: ProductType,
pub product_version: u8,
}
impl FeatureSet {
pub fn parse(msb: u8, lsb: u8) -> Self {
FeatureSet {
product_type: ProductType::parse(msb >> 4),
product_version: lsb,
}
}
}
#[cfg(test)]
mod tests {
use std::f32;
use super::*;
#[test]
fn humidity_as_bytes() {
assert_eq!(Humidity::new(0x00, 0x01).unwrap().as_bytes(), [0x00, 0x01]);
assert_eq!(Humidity::new(0xFF, 0xFF).unwrap().as_bytes(), [0xFF, 0xFF]);
assert_eq!(Humidity::new(0x10, 0x80).unwrap().as_bytes(), [0x10, 0x80]);
}
#[test]
fn humidity_from_f32_ok() {
assert_eq!(
Humidity::from_f32(0.00390625f32),
Ok(Humidity::new(0x00, 0x01).unwrap())
);
assert_eq!(
Humidity::from_f32(255.99609375f32),
Ok(Humidity::new(0xFF, 0xFF).unwrap())
);
assert_eq!(
Humidity::from_f32(16.5f32),
Ok(Humidity::new(0x10, 0x80).unwrap())
);
assert_eq!(
Humidity::from_f32(16.999999f32),
Ok(Humidity::new(0x10, 0xFF).unwrap())
);
}
#[test]
fn humidity_from_f32_err() {
assert_eq!(Humidity::from_f32(-3.0f32), Err(HumidityError::OutOfRange));
assert_eq!(Humidity::from_f32(0.0f32), Err(HumidityError::ZeroValue));
assert_eq!(Humidity::from_f32(-0.0f32), Err(HumidityError::ZeroValue));
assert_eq!(Humidity::from_f32(f32::NAN), Err(HumidityError::OutOfRange));
}
#[test]
fn humidity_into_f32() {
let float: f32 = Humidity::new(0x00, 0x01).unwrap().into();
assert_eq!(float, 0.00390625f32);
let float: f32 = Humidity::new(0xFF, 0xFF).unwrap().into();
assert_eq!(float, 255.99609375);
let float: f32 = Humidity::new(0x10, 0x80).unwrap().into();
assert_eq!(float, 16.5);
}
}