scd30_interface/data/
temperature_offset.rs1use byteorder::{BigEndian, ByteOrder};
2
3use crate::{error::DataError, util::check_deserialization};
4
5const MIN_TEMPERATURE_OFFSET: f32 = 0.0;
6const MAX_TEMPERATURE_OFFSET: f32 = 0.1 * u16::MAX as f32;
7const TEMPERATURE_OFFSET_VAL: &str = "Temperature offset";
8const TEMPERATURE_UNIT: &str = "°C";
9
10#[derive(Debug, PartialEq)]
13pub struct TemperatureOffset(u16);
14
15impl TemperatureOffset {
16 pub const fn to_be_bytes(&self) -> [u8; 2] {
18 self.0.to_be_bytes()
19 }
20}
21
22#[cfg(feature = "defmt")]
23impl defmt::Format for TemperatureOffset {
24 fn format(&self, f: defmt::Formatter) {
25 defmt::write!(f, "{}°C", self.0 as f32 / 100.0)
26 }
27}
28
29impl TryFrom<f32> for TemperatureOffset {
30 type Error = DataError;
31
32 fn try_from(offset: f32) -> Result<Self, Self::Error> {
40 if !(MIN_TEMPERATURE_OFFSET..=MAX_TEMPERATURE_OFFSET).contains(&offset) {
41 Err(DataError::ValueOutOfRange {
42 parameter: TEMPERATURE_OFFSET_VAL,
43 min: MIN_TEMPERATURE_OFFSET as u16,
44 max: (MAX_TEMPERATURE_OFFSET * 100.0) as u16,
45 unit: TEMPERATURE_UNIT,
46 })
47 } else {
48 Ok(Self((offset * 100.0) as u16))
49 }
50 }
51}
52
53impl TryFrom<f64> for TemperatureOffset {
54 type Error = DataError;
55
56 fn try_from(offset: f64) -> Result<Self, Self::Error> {
64 if MIN_TEMPERATURE_OFFSET as f64 > offset || offset > MAX_TEMPERATURE_OFFSET as f64 {
65 Err(DataError::ValueOutOfRange {
66 parameter: TEMPERATURE_OFFSET_VAL,
67 min: MIN_TEMPERATURE_OFFSET as u16,
68 max: (MAX_TEMPERATURE_OFFSET * 100.0) as u16,
69 unit: TEMPERATURE_UNIT,
70 })
71 } else {
72 Ok(Self((offset * 100.0) as u16))
73 }
74 }
75}
76
77impl TryFrom<&[u8]> for TemperatureOffset {
78 type Error = DataError;
79
80 fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
88 check_deserialization(data, 3)?;
89 Ok(Self(BigEndian::read_u16(&data[..2])))
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use core::u16;
96
97 use super::*;
98
99 #[test]
100 fn deserialize_sample_works() {
101 let data = [0x01, 0xF4, 0x33];
102 let offset = TemperatureOffset::try_from(&data[..]).unwrap();
103 assert_eq!(offset, TemperatureOffset(500));
104 }
105
106 #[test]
107 fn serialize_sample_works() {
108 let offset = TemperatureOffset(500);
109 assert_eq!(offset.to_be_bytes(), [0x01, 0xF4]);
110 }
111
112 #[test]
113 fn create_allowed_value_from_f32_works() {
114 let values = [(0.0f32, 0), (0.1, 10), (10.0, 1000), (6553.5, u16::MAX)];
115 for (value, result) in values {
116 assert_eq!(
117 TemperatureOffset::try_from(value).unwrap(),
118 TemperatureOffset(result)
119 );
120 }
121 }
122 #[test]
123 fn create_allowed_value_from_f64_works() {
124 let values = [(0.0, 0), (0.1, 10), (10.0, 1000), (6553.5, u16::MAX)];
125 for (value, result) in values {
126 assert_eq!(
127 TemperatureOffset::try_from(value).unwrap(),
128 TemperatureOffset(result)
129 );
130 }
131 }
132
133 #[test]
134 fn create_from_f32_non_null_out_of_spec_value_errors() {
135 let values = [-0.1f32, 6554.0];
136 for value in values {
137 assert_eq!(
138 TemperatureOffset::try_from(value).unwrap_err(),
139 DataError::ValueOutOfRange {
140 parameter: TEMPERATURE_OFFSET_VAL,
141 min: 0,
142 max: u16::MAX,
143 unit: TEMPERATURE_UNIT
144 }
145 );
146 }
147 }
148
149 #[test]
150 fn create_from_f64_non_null_out_of_spec_value_errors() {
151 let values = [-0.1, 6554.0];
152 for value in values {
153 assert_eq!(
154 TemperatureOffset::try_from(value).unwrap_err(),
155 DataError::ValueOutOfRange {
156 parameter: TEMPERATURE_OFFSET_VAL,
157 min: 0,
158 max: u16::MAX,
159 unit: TEMPERATURE_UNIT
160 }
161 );
162 }
163 }
164}