sen66_interface/configuration/tuning.rs
1use crate::{
2 error::DataError,
3 util::{check_deserialization, check_range},
4};
5
6/// Configuration for the VOC Index algorithm.
7#[derive(Debug, PartialEq)]
8pub struct VocTuning(Tuning);
9
10impl VocTuning {
11 /// Creates a new [`VocTuning`](VocTuning) Index configuration:
12 /// - `index_offset`: VOC Index representing typical conditions. Range: 1 - 250, Default: 100.
13 /// - `learning_time_offset`: Time constant to estimate the offset from the history in hours.
14 /// After twice the learning time events are forgotten. Range: 1 - 1,000h, Default: 12h
15 /// - `learning_time_gain`: Time constant to estimate the gain from the history in hours.
16 /// After twice the learning time events are forgotten. Range 1 - 1,000h, Default: 12h
17 /// - `gating_max_durations`: Maximum duration the estimator freezes on a high VOC index
18 /// signal. Zero disables the gating. Range 0 - 3,000min, Default 180min.
19 /// - `initial_standard_deviation`: Initial estimate for the standard deviation. Range 10 -
20 /// 5,000, Default: 50.
21 /// - `gain_factor`: Factor to amplify/attunate the VOC index output. Range 1 - 1,000, Default:
22 /// 230.
23 ///
24 /// # Errors
25 ///
26 /// - [`ValueOutOfRange`](crate::error::DataError::ValueOutOfRange)`: If the values with scaling
27 /// are not in range.
28 pub fn new(
29 index_offset: i16,
30 learning_time_offset: i16,
31 learning_time_gain: i16,
32 gating_max_durations: i16,
33 initial_standard_deviation: i16,
34 gain_factor: i16,
35 ) -> Result<Self, DataError> {
36 Ok(Self(Tuning::new(
37 index_offset,
38 learning_time_offset,
39 learning_time_gain,
40 gating_max_durations,
41 initial_standard_deviation,
42 gain_factor,
43 )?))
44 }
45}
46
47impl From<VocTuning> for [u16; 6] {
48 fn from(value: VocTuning) -> Self {
49 <[u16; 6]>::from(value.0)
50 }
51}
52
53impl TryFrom<&[u8]> for VocTuning {
54 type Error = DataError;
55
56 /// Parse the VOC tuning parameters from the received data.
57 ///
58 /// # Errors
59 ///
60 /// - [`CrcFailed`](crate::error::DataError::CrcFailed): If the received data CRC indicates
61 /// corruption.
62 /// - [`ReceivedBufferWrongSize`](crate::error::DataError::ReceivedBufferWrongSize): If the
63 /// received data buffer is not the expected size.
64 fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
65 Ok(VocTuning(Tuning::try_from(data)?))
66 }
67}
68
69impl Default for VocTuning {
70 /// Creates a default [`VocTuning`](VocTuning) with:
71 /// - `index_offset`: 100
72 /// - `learning_time_offset`: 12h
73 /// - `learning_time_gain`: 12h
74 /// - `gating_max_durations`: 180min
75 /// - `initial_standard_deviation`: 50
76 /// - `gain_factor`: 230
77 fn default() -> Self {
78 Self(Tuning {
79 index_offset: 100,
80 learning_time_offset: 12,
81 learning_time_gain: 12,
82 gating_max_durations: 180,
83 initial_standard_deviation: 50,
84 gain_factor: 230,
85 })
86 }
87}
88
89/// Configuration for the NOx Index algorithm.
90#[derive(Debug, PartialEq)]
91pub struct NoxTuning(Tuning);
92
93impl NoxTuning {
94 /// Creates a new [`NoxTuning`](NoxTuning) Index configuration:
95 /// - `index_offset`: NOx Index representing typical conditions. Range 1 - 250, Default: 1.
96 /// - `learning_time_offset`: Time constant to estimate the offset from the history in hours.
97 /// After twice the learning time events are forgotten. Range 1 - 1,000h, Default 12h.
98 /// - `learning_time_gain`: Time constant to estimate the gain from the history in hours.
99 /// After twice the learning time events are forgotten. Range 1 - 1,000h, Default 12h.
100 /// - `gating_max_durations`: Maximum duration the estimator freezes on a high NOx index
101 /// signal. Zero disables the gating. Range 0 - 3,000min, Default: 720min.
102 /// - `gain_factor`: Factor to amplify/attunate the NOx index output. Range 1 - 1,000, Default:
103 /// 230.
104 ///
105 /// # Errors
106 ///
107 /// - [`ValueOutOfRange`](crate::error::DataError::ValueOutOfRange)`: If the values with scaling
108 /// are not in range.
109 pub fn new(
110 index_offset: i16,
111 learning_time_offset: i16,
112 learning_time_gain: i16,
113 gating_max_durations: i16,
114 gain_factor: i16,
115 ) -> Result<Self, DataError> {
116 Ok(Self(Tuning::new(
117 index_offset,
118 learning_time_offset,
119 learning_time_gain,
120 gating_max_durations,
121 50,
122 gain_factor,
123 )?))
124 }
125}
126
127impl From<NoxTuning> for [u16; 6] {
128 fn from(value: NoxTuning) -> Self {
129 <[u16; 6]>::from(value.0)
130 }
131}
132
133impl TryFrom<&[u8]> for NoxTuning {
134 type Error = DataError;
135
136 /// Parse the NOx tuning parameters from the received data.
137 ///
138 /// # Errors
139 ///
140 /// - [`CrcFailed`](crate::error::DataError::CrcFailed): If the received data CRC indicates
141 /// corruption.
142 /// - [`ReceivedBufferWrongSize`](crate::error::DataError::ReceivedBufferWrongSize): If the
143 /// received data buffer is not the expected size.
144 fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
145 Ok(NoxTuning(Tuning::try_from(data)?))
146 }
147}
148
149impl Default for NoxTuning {
150 /// Creates a default [`NoxTuning`](NoxTuning) with:
151 /// - `index_offset`: 1
152 /// - `learning_time_offset`: 12h
153 /// - `learning_time_gain`: 12h
154 /// - `gating_max_durations`: 720min
155 /// - `initial_standard_deviation`: 50
156 /// - `gain_factor`: 230
157 fn default() -> Self {
158 Self(Tuning {
159 index_offset: 100,
160 learning_time_offset: 12,
161 learning_time_gain: 12,
162 gating_max_durations: 180,
163 initial_standard_deviation: 50,
164 gain_factor: 230,
165 })
166 }
167}
168
169#[derive(Debug, PartialEq)]
170struct Tuning {
171 index_offset: i16,
172 learning_time_offset: i16,
173 learning_time_gain: i16,
174 gating_max_durations: i16,
175 initial_standard_deviation: i16,
176 gain_factor: i16,
177}
178
179impl Tuning {
180 fn new(
181 index_offset: i16,
182 learning_time_offset: i16,
183 learning_time_gain: i16,
184 gating_max_durations: i16,
185 initial_standard_deviation: i16,
186 gain_factor: i16,
187 ) -> Result<Self, DataError> {
188 Ok(Self {
189 index_offset: check_range(index_offset, 1, 250, "VOC Index Offset", "")?,
190 learning_time_offset: check_range(
191 learning_time_offset,
192 1,
193 1_000,
194 "VOC Learning Time Offset",
195 "h",
196 )?,
197 learning_time_gain: check_range(
198 learning_time_gain,
199 1,
200 1_000,
201 "VOC Learning Time Gain",
202 "h",
203 )?,
204 gating_max_durations: check_range(
205 gating_max_durations,
206 0,
207 3_000,
208 "VOC Gating Max Duration",
209 "min",
210 )?,
211 initial_standard_deviation: check_range(
212 initial_standard_deviation,
213 10,
214 5_000,
215 "VOC Initial Standard Deviation",
216 "",
217 )?,
218 gain_factor: check_range(gain_factor, 1, 1_000, "VOC Gain Factor", "")?,
219 })
220 }
221}
222
223impl From<Tuning> for [u16; 6] {
224 fn from(value: Tuning) -> Self {
225 [
226 value.index_offset as u16,
227 value.learning_time_offset as u16,
228 value.learning_time_gain as u16,
229 value.gating_max_durations as u16,
230 value.initial_standard_deviation as u16,
231 value.gain_factor as u16,
232 ]
233 }
234}
235
236impl TryFrom<&[u8]> for Tuning {
237 type Error = DataError;
238
239 fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
240 check_deserialization(data, 18)?;
241 Tuning::new(
242 i16::from_be_bytes([data[0], data[1]]),
243 i16::from_be_bytes([data[3], data[4]]),
244 i16::from_be_bytes([data[6], data[7]]),
245 i16::from_be_bytes([data[9], data[10]]),
246 i16::from_be_bytes([data[12], data[13]]),
247 i16::from_be_bytes([data[15], data[16]]),
248 )
249 }
250}