1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/// A temperature measurement.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Temperature(i32);

/// A humidity measurement.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Humidity(i32);

/// A combined temperature / humidity measurement.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Measurement {
    /// The measured temperature.
    pub temperature: Temperature,
    /// The measured humidity.
    pub humidity: Humidity,
}

impl Temperature {
    /// Create a new `Temperature` from a raw measurement result.
    pub(crate) fn from_raw(raw: u16) -> Self {
        Self(convert_temperature(raw))
    }

    /// Return temperature in milli-degrees celsius.
    pub fn as_millidegrees_celsius(&self) -> i32 {
        self.0
    }

    /// Return temperature in degrees celsius.
    pub fn as_degrees_celsius(&self) -> f32 {
        self.0 as f32 / 1000.0
    }
}

impl Humidity {
    /// Create a new `Humidity` from a raw measurement result.
    pub(crate) fn from_raw(raw: u16) -> Self {
        Self(convert_humidity(raw))
    }

    /// Return relative humidity in 1/1000 %RH.
    pub fn as_millipercent(&self) -> i32 {
        self.0
    }

    /// Return relative humidity in %RH.
    pub fn as_percent(&self) -> f32 {
        self.0 as f32 / 1000.0
    }
}

/// Convert raw temperature measurement to milli-degrees celsius.
///
/// Formula (datasheet 5.11): -45 + 175 * (val / 2^16),
/// optimized for fixed point math.
#[inline]
fn convert_temperature(temp_raw: u16) -> i32 {
    (((temp_raw as u32) * 21875) >> 13) as i32 - 45000
}

/// Convert raw humidity measurement to relative humidity.
///
/// Formula (datasheet 5.11): 100 * (val / 2^16),
/// optimized for fixed point math.
#[inline]
fn convert_humidity(humi_raw: u16) -> i32 {
    (((humi_raw as u32) * 12500) >> 13) as i32
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Test conversion of raw measurement results into °C.
    #[test]
    fn test_convert_temperature() {
        let test_data = [
            (0x0000, -45000),
            // Datasheet setion 5.11 "Conversion of Sensor Output"
            (((0b0110_0100 as u16) << 8) | 0b1000_1011, 23730),
        ];
        for td in &test_data {
            assert_eq!(convert_temperature(td.0), td.1);
        }
    }

    /// Test conversion of raw measurement results into %RH.
    #[test]
    fn test_convert_humidity() {
        let test_data = [
            (0x0000, 0),
            // Datasheet setion 5.11 "Conversion of Sensor Output"
            (((0b1010_0001 as u16) << 8) | 0b0011_0011, 62968),
        ];
        for td in &test_data {
            assert_eq!(convert_humidity(td.0), td.1);
        }
    }

    /// Test conversion of raw measurement results into °C and %RH.
    #[test]
    fn measurement_conversion() {
        // Datasheet setion 5.11 "Conversion of Sensor Output"
        let temperature = convert_temperature(((0b0110_0100 as u16) << 8) | 0b1000_1011);
        let humidity = convert_humidity(((0b1010_0001 as u16) << 8) | 0b0011_0011);
        assert_eq!(temperature, 23730);
        assert_eq!(humidity, 62968);
    }

    #[test]
    fn temperature() {
        let temp = Temperature(24123);
        assert_eq!(temp.as_millidegrees_celsius(), 24123);
        assert_eq!(temp.as_degrees_celsius(), 24.123);
    }

    #[test]
    fn humidity() {
        let humi = Humidity(65432);
        assert_eq!(humi.as_millipercent(), 65432);
        assert_eq!(humi.as_percent(), 65.432);
    }
}