sensirion_voc_algorithm.c revision 1.1 1 /*
2 * $NetBSD: sensirion_voc_algorithm.c,v 1.1 2021/10/14 13:54:46 brad Exp $
3 */
4
5 /*
6 * Copyright (c) 2021, Sensirion AG
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * * Redistributions of source code must retain the above copyright notice, this
13 * list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * * Neither the name of Sensirion AG nor the names of its
20 * contributors may be used to endorse or promote products derived from
21 * this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include "sensirion_voc_algorithm.h"
37
38 /* The fixed point arithmetic parts of this code were originally created by
39 * https://github.com/PetteriAimonen/libfixmath
40 */
41
42 /*!< the maximum value of fix16_t */
43 #define FIX16_MAXIMUM 0x7FFFFFFF
44 /*!< the minimum value of fix16_t */
45 #define FIX16_MINIMUM 0x80000000
46 /*!< the value used to indicate overflows when FIXMATH_NO_OVERFLOW is not
47 * specified */
48 #define FIX16_OVERFLOW 0x80000000
49 /*!< fix16_t value of 1 */
50 #define FIX16_ONE 0x00010000
51
52 static inline fix16_t fix16_from_int(int32_t a) {
53 return a * FIX16_ONE;
54 }
55
56 static inline int32_t fix16_cast_to_int(fix16_t a) {
57 return (a >= 0) ? (a >> 16) : -((-a) >> 16);
58 }
59
60 /*! Multiplies the two given fix16_t's and returns the result. */
61 static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1);
62
63 /*! Divides the first given fix16_t by the second and returns the result. */
64 static fix16_t fix16_div(fix16_t inArg0, fix16_t inArg1);
65
66 /*! Returns the square root of the given fix16_t. */
67 static fix16_t fix16_sqrt(fix16_t inValue);
68
69 /*! Returns the exponent (e^) of the given fix16_t. */
70 static fix16_t fix16_exp(fix16_t inValue);
71
72 static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) {
73 // Each argument is divided to 16-bit parts.
74 // AB
75 // * CD
76 // -----------
77 // BD 16 * 16 -> 32 bit products
78 // CB
79 // AD
80 // AC
81 // |----| 64 bit product
82 uint32_t absArg0 = (uint32_t)((inArg0 >= 0) ? inArg0 : (-inArg0));
83 uint32_t absArg1 = (uint32_t)((inArg1 >= 0) ? inArg1 : (-inArg1));
84 uint32_t A = (absArg0 >> 16), C = (absArg1 >> 16);
85 uint32_t B = (absArg0 & 0xFFFF), D = (absArg1 & 0xFFFF);
86
87 uint32_t AC = A * C;
88 uint32_t AD_CB = A * D + C * B;
89 uint32_t BD = B * D;
90
91 uint32_t product_hi = AC + (AD_CB >> 16);
92
93 // Handle carry from lower 32 bits to upper part of result.
94 uint32_t ad_cb_temp = AD_CB << 16;
95 uint32_t product_lo = BD + ad_cb_temp;
96 if (product_lo < BD)
97 product_hi++;
98
99 #ifndef FIXMATH_NO_OVERFLOW
100 // The upper 17 bits should all be zero.
101 if (product_hi >> 15)
102 return (fix16_t)FIX16_OVERFLOW;
103 #endif
104
105 #ifdef FIXMATH_NO_ROUNDING
106 fix16_t result = (fix16_t)((product_hi << 16) | (product_lo >> 16));
107 if ((inArg0 < 0) != (inArg1 < 0))
108 result = -result;
109 return result;
110 #else
111 // Adding 0x8000 (= 0.5) and then using right shift
112 // achieves proper rounding to result.
113 // Handle carry from lower to upper part.
114 uint32_t product_lo_tmp = product_lo;
115 product_lo += 0x8000;
116 if (product_lo < product_lo_tmp)
117 product_hi++;
118
119 // Discard the lowest 16 bits and convert back to signed result.
120 fix16_t result = (fix16_t)((product_hi << 16) | (product_lo >> 16));
121 if ((inArg0 < 0) != (inArg1 < 0))
122 result = -result;
123 return result;
124 #endif
125 }
126
127 static fix16_t fix16_div(fix16_t a, fix16_t b) {
128 // This uses the basic binary restoring division algorithm.
129 // It appears to be faster to do the whole division manually than
130 // trying to compose a 64-bit divide out of 32-bit divisions on
131 // platforms without hardware divide.
132
133 if (b == 0)
134 return (fix16_t)FIX16_MINIMUM;
135
136 uint32_t remainder = (uint32_t)((a >= 0) ? a : (-a));
137 uint32_t divider = (uint32_t)((b >= 0) ? b : (-b));
138
139 uint32_t quotient = 0;
140 uint32_t bit = 0x10000;
141
142 /* The algorithm requires D >= R */
143 while (divider < remainder) {
144 divider <<= 1;
145 bit <<= 1;
146 }
147
148 #ifndef FIXMATH_NO_OVERFLOW
149 if (!bit)
150 return (fix16_t)FIX16_OVERFLOW;
151 #endif
152
153 if (divider & 0x80000000) {
154 // Perform one step manually to avoid overflows later.
155 // We know that divider's bottom bit is 0 here.
156 if (remainder >= divider) {
157 quotient |= bit;
158 remainder -= divider;
159 }
160 divider >>= 1;
161 bit >>= 1;
162 }
163
164 /* Main division loop */
165 while (bit && remainder) {
166 if (remainder >= divider) {
167 quotient |= bit;
168 remainder -= divider;
169 }
170
171 remainder <<= 1;
172 bit >>= 1;
173 }
174
175 #ifndef FIXMATH_NO_ROUNDING
176 if (remainder >= divider) {
177 quotient++;
178 }
179 #endif
180
181 fix16_t result = (fix16_t)quotient;
182
183 /* Figure out the sign of result */
184 if ((a < 0) != (b < 0)) {
185 #ifndef FIXMATH_NO_OVERFLOW
186 if (result == FIX16_MINIMUM)
187 return (fix16_t)FIX16_OVERFLOW;
188 #endif
189
190 result = -result;
191 }
192
193 return result;
194 }
195
196 static fix16_t fix16_sqrt(fix16_t x) {
197 // It is assumed that x is not negative
198
199 uint32_t num = (uint32_t)x;
200 uint32_t result = 0;
201 uint32_t bit;
202 uint8_t n;
203
204 bit = (uint32_t)1 << 30;
205 while (bit > num)
206 bit >>= 2;
207
208 // The main part is executed twice, in order to avoid
209 // using 64 bit values in computations.
210 for (n = 0; n < 2; n++) {
211 // First we get the top 24 bits of the answer.
212 while (bit) {
213 if (num >= result + bit) {
214 num -= result + bit;
215 result = (result >> 1) + bit;
216 } else {
217 result = (result >> 1);
218 }
219 bit >>= 2;
220 }
221
222 if (n == 0) {
223 // Then process it again to get the lowest 8 bits.
224 if (num > 65535) {
225 // The remainder 'num' is too large to be shifted left
226 // by 16, so we have to add 1 to result manually and
227 // adjust 'num' accordingly.
228 // num = a - (result + 0.5)^2
229 // = num + result^2 - (result + 0.5)^2
230 // = num - result - 0.5
231 num -= result;
232 num = (num << 16) - 0x8000;
233 result = (result << 16) + 0x8000;
234 } else {
235 num <<= 16;
236 result <<= 16;
237 }
238
239 bit = 1 << 14;
240 }
241 }
242
243 #ifndef FIXMATH_NO_ROUNDING
244 // Finally, if next bit would have been 1, round the result upwards.
245 if (num > result) {
246 result++;
247 }
248 #endif
249
250 return (fix16_t)result;
251 }
252
253 static fix16_t fix16_exp(fix16_t x) {
254 // Function to approximate exp(); optimized more for code size than speed
255
256 // exp(x) for x = +/- {1, 1/8, 1/64, 1/512}
257 #define NUM_EXP_VALUES 4
258 static const fix16_t exp_pos_values[NUM_EXP_VALUES] = {
259 F16(2.7182818), F16(1.1331485), F16(1.0157477), F16(1.0019550)};
260 static const fix16_t exp_neg_values[NUM_EXP_VALUES] = {
261 F16(0.3678794), F16(0.8824969), F16(0.9844964), F16(0.9980488)};
262 const fix16_t* exp_values;
263
264 fix16_t res, arg;
265 uint16_t i;
266
267 if (x >= F16(10.3972))
268 return FIX16_MAXIMUM;
269 if (x <= F16(-11.7835))
270 return 0;
271
272 if (x < 0) {
273 x = -x;
274 exp_values = exp_neg_values;
275 } else {
276 exp_values = exp_pos_values;
277 }
278
279 res = FIX16_ONE;
280 arg = FIX16_ONE;
281 for (i = 0; i < NUM_EXP_VALUES; i++) {
282 while (x >= arg) {
283 res = fix16_mul(res, exp_values[i]);
284 x -= arg;
285 }
286 arg >>= 3;
287 }
288 return res;
289 }
290
291 static void VocAlgorithm__init_instances(VocAlgorithmParams* params);
292 static void
293 VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams* params);
294 static void VocAlgorithm__mean_variance_estimator___init_instances(
295 VocAlgorithmParams* params);
296 static void VocAlgorithm__mean_variance_estimator__set_parameters(
297 VocAlgorithmParams* params, fix16_t std_initial,
298 fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes);
299 static void
300 VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams* params,
301 fix16_t mean, fix16_t std,
302 fix16_t uptime_gamma);
303 static fix16_t
304 VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams* params);
305 static fix16_t
306 VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams* params);
307 static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
308 VocAlgorithmParams* params, fix16_t voc_index_from_prior);
309 static void VocAlgorithm__mean_variance_estimator__process(
310 VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior);
311 static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
312 VocAlgorithmParams* params);
313 static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
314 VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K);
315 static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
316 VocAlgorithmParams* params, fix16_t sample);
317 static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params);
318 static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
319 fix16_t SRAW_STD,
320 fix16_t SRAW_MEAN);
321 static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
322 fix16_t sraw);
323 static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params);
324 static void
325 VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams* params,
326 fix16_t offset);
327 static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
328 fix16_t sample);
329 static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params);
330 static void
331 VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams* params);
332 static fix16_t
333 VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams* params,
334 fix16_t sample);
335
336 void VocAlgorithm_init(VocAlgorithmParams* params) {
337
338 params->mVoc_Index_Offset = F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT);
339 params->mTau_Mean_Variance_Hours =
340 F16(VocAlgorithm_TAU_MEAN_VARIANCE_HOURS);
341 params->mGating_Max_Duration_Minutes =
342 F16(VocAlgorithm_GATING_MAX_DURATION_MINUTES);
343 params->mSraw_Std_Initial = F16(VocAlgorithm_SRAW_STD_INITIAL);
344 params->mUptime = F16(0.);
345 params->mSraw = F16(0.);
346 params->mVoc_Index = 0;
347 VocAlgorithm__init_instances(params);
348 }
349
350 static void VocAlgorithm__init_instances(VocAlgorithmParams* params) {
351
352 VocAlgorithm__mean_variance_estimator__init(params);
353 VocAlgorithm__mean_variance_estimator__set_parameters(
354 params, params->mSraw_Std_Initial, params->mTau_Mean_Variance_Hours,
355 params->mGating_Max_Duration_Minutes);
356 VocAlgorithm__mox_model__init(params);
357 VocAlgorithm__mox_model__set_parameters(
358 params, VocAlgorithm__mean_variance_estimator__get_std(params),
359 VocAlgorithm__mean_variance_estimator__get_mean(params));
360 VocAlgorithm__sigmoid_scaled__init(params);
361 VocAlgorithm__sigmoid_scaled__set_parameters(params,
362 params->mVoc_Index_Offset);
363 VocAlgorithm__adaptive_lowpass__init(params);
364 VocAlgorithm__adaptive_lowpass__set_parameters(params);
365 }
366
367 void VocAlgorithm_get_states(VocAlgorithmParams* params, int32_t* state0,
368 int32_t* state1) {
369
370 *state0 = VocAlgorithm__mean_variance_estimator__get_mean(params);
371 *state1 = VocAlgorithm__mean_variance_estimator__get_std(params);
372 return;
373 }
374
375 void VocAlgorithm_set_states(VocAlgorithmParams* params, int32_t state0,
376 int32_t state1) {
377
378 VocAlgorithm__mean_variance_estimator__set_states(
379 params, state0, state1, F16(VocAlgorithm_PERSISTENCE_UPTIME_GAMMA));
380 params->mSraw = state0;
381 }
382
383 void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams* params,
384 int32_t voc_index_offset,
385 int32_t learning_time_hours,
386 int32_t gating_max_duration_minutes,
387 int32_t std_initial) {
388
389 params->mVoc_Index_Offset = (fix16_from_int(voc_index_offset));
390 params->mTau_Mean_Variance_Hours = (fix16_from_int(learning_time_hours));
391 params->mGating_Max_Duration_Minutes =
392 (fix16_from_int(gating_max_duration_minutes));
393 params->mSraw_Std_Initial = (fix16_from_int(std_initial));
394 VocAlgorithm__init_instances(params);
395 }
396
397 void VocAlgorithm_process(VocAlgorithmParams* params, int32_t sraw,
398 int32_t* voc_index) {
399
400 if ((params->mUptime <= F16(VocAlgorithm_INITIAL_BLACKOUT))) {
401 params->mUptime =
402 (params->mUptime + F16(VocAlgorithm_SAMPLING_INTERVAL));
403 } else {
404 if (((sraw > 0) && (sraw < 65000))) {
405 if ((sraw < 20001)) {
406 sraw = 20001;
407 } else if ((sraw > 52767)) {
408 sraw = 52767;
409 }
410 params->mSraw = (fix16_from_int((sraw - 20000)));
411 }
412 params->mVoc_Index =
413 VocAlgorithm__mox_model__process(params, params->mSraw);
414 params->mVoc_Index =
415 VocAlgorithm__sigmoid_scaled__process(params, params->mVoc_Index);
416 params->mVoc_Index =
417 VocAlgorithm__adaptive_lowpass__process(params, params->mVoc_Index);
418 if ((params->mVoc_Index < F16(0.5))) {
419 params->mVoc_Index = F16(0.5);
420 }
421 if ((params->mSraw > F16(0.))) {
422 VocAlgorithm__mean_variance_estimator__process(
423 params, params->mSraw, params->mVoc_Index);
424 VocAlgorithm__mox_model__set_parameters(
425 params, VocAlgorithm__mean_variance_estimator__get_std(params),
426 VocAlgorithm__mean_variance_estimator__get_mean(params));
427 }
428 }
429 *voc_index = (fix16_cast_to_int((params->mVoc_Index + F16(0.5))));
430 return;
431 }
432
433 static void
434 VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams* params) {
435
436 VocAlgorithm__mean_variance_estimator__set_parameters(params, F16(0.),
437 F16(0.), F16(0.));
438 VocAlgorithm__mean_variance_estimator___init_instances(params);
439 }
440
441 static void VocAlgorithm__mean_variance_estimator___init_instances(
442 VocAlgorithmParams* params) {
443
444 VocAlgorithm__mean_variance_estimator___sigmoid__init(params);
445 }
446
447 static void VocAlgorithm__mean_variance_estimator__set_parameters(
448 VocAlgorithmParams* params, fix16_t std_initial,
449 fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes) {
450
451 params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes =
452 gating_max_duration_minutes;
453 params->m_Mean_Variance_Estimator___Initialized = false;
454 params->m_Mean_Variance_Estimator___Mean = F16(0.);
455 params->m_Mean_Variance_Estimator___Sraw_Offset = F16(0.);
456 params->m_Mean_Variance_Estimator___Std = std_initial;
457 params->m_Mean_Variance_Estimator___Gamma =
458 (fix16_div(F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
459 (VocAlgorithm_SAMPLING_INTERVAL / 3600.))),
460 (tau_mean_variance_hours +
461 F16((VocAlgorithm_SAMPLING_INTERVAL / 3600.)))));
462 params->m_Mean_Variance_Estimator___Gamma_Initial_Mean =
463 F16(((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
464 VocAlgorithm_SAMPLING_INTERVAL) /
465 (VocAlgorithm_TAU_INITIAL_MEAN + VocAlgorithm_SAMPLING_INTERVAL)));
466 params->m_Mean_Variance_Estimator___Gamma_Initial_Variance = F16(
467 ((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
468 VocAlgorithm_SAMPLING_INTERVAL) /
469 (VocAlgorithm_TAU_INITIAL_VARIANCE + VocAlgorithm_SAMPLING_INTERVAL)));
470 params->m_Mean_Variance_Estimator__Gamma_Mean = F16(0.);
471 params->m_Mean_Variance_Estimator__Gamma_Variance = F16(0.);
472 params->m_Mean_Variance_Estimator___Uptime_Gamma = F16(0.);
473 params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
474 params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
475 }
476
477 static void
478 VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams* params,
479 fix16_t mean, fix16_t std,
480 fix16_t uptime_gamma) {
481
482 params->m_Mean_Variance_Estimator___Mean = mean;
483 params->m_Mean_Variance_Estimator___Std = std;
484 params->m_Mean_Variance_Estimator___Uptime_Gamma = uptime_gamma;
485 params->m_Mean_Variance_Estimator___Initialized = true;
486 }
487
488 static fix16_t
489 VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams* params) {
490
491 return params->m_Mean_Variance_Estimator___Std;
492 }
493
494 static fix16_t
495 VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams* params) {
496
497 return (params->m_Mean_Variance_Estimator___Mean +
498 params->m_Mean_Variance_Estimator___Sraw_Offset);
499 }
500
501 static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
502 VocAlgorithmParams* params, fix16_t voc_index_from_prior) {
503
504 fix16_t uptime_limit;
505 fix16_t sigmoid_gamma_mean;
506 fix16_t gamma_mean;
507 fix16_t gating_threshold_mean;
508 fix16_t sigmoid_gating_mean;
509 fix16_t sigmoid_gamma_variance;
510 fix16_t gamma_variance;
511 fix16_t gating_threshold_variance;
512 fix16_t sigmoid_gating_variance;
513
514 uptime_limit = F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX -
515 VocAlgorithm_SAMPLING_INTERVAL));
516 if ((params->m_Mean_Variance_Estimator___Uptime_Gamma < uptime_limit)) {
517 params->m_Mean_Variance_Estimator___Uptime_Gamma =
518 (params->m_Mean_Variance_Estimator___Uptime_Gamma +
519 F16(VocAlgorithm_SAMPLING_INTERVAL));
520 }
521 if ((params->m_Mean_Variance_Estimator___Uptime_Gating < uptime_limit)) {
522 params->m_Mean_Variance_Estimator___Uptime_Gating =
523 (params->m_Mean_Variance_Estimator___Uptime_Gating +
524 F16(VocAlgorithm_SAMPLING_INTERVAL));
525 }
526 VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
527 params, F16(1.), F16(VocAlgorithm_INIT_DURATION_MEAN),
528 F16(VocAlgorithm_INIT_TRANSITION_MEAN));
529 sigmoid_gamma_mean =
530 VocAlgorithm__mean_variance_estimator___sigmoid__process(
531 params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
532 gamma_mean =
533 (params->m_Mean_Variance_Estimator___Gamma +
534 (fix16_mul((params->m_Mean_Variance_Estimator___Gamma_Initial_Mean -
535 params->m_Mean_Variance_Estimator___Gamma),
536 sigmoid_gamma_mean)));
537 gating_threshold_mean =
538 (F16(VocAlgorithm_GATING_THRESHOLD) +
539 (fix16_mul(
540 F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
541 VocAlgorithm_GATING_THRESHOLD)),
542 VocAlgorithm__mean_variance_estimator___sigmoid__process(
543 params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
544 VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
545 params, F16(1.), gating_threshold_mean,
546 F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
547 sigmoid_gating_mean =
548 VocAlgorithm__mean_variance_estimator___sigmoid__process(
549 params, voc_index_from_prior);
550 params->m_Mean_Variance_Estimator__Gamma_Mean =
551 (fix16_mul(sigmoid_gating_mean, gamma_mean));
552 VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
553 params, F16(1.), F16(VocAlgorithm_INIT_DURATION_VARIANCE),
554 F16(VocAlgorithm_INIT_TRANSITION_VARIANCE));
555 sigmoid_gamma_variance =
556 VocAlgorithm__mean_variance_estimator___sigmoid__process(
557 params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
558 gamma_variance =
559 (params->m_Mean_Variance_Estimator___Gamma +
560 (fix16_mul(
561 (params->m_Mean_Variance_Estimator___Gamma_Initial_Variance -
562 params->m_Mean_Variance_Estimator___Gamma),
563 (sigmoid_gamma_variance - sigmoid_gamma_mean))));
564 gating_threshold_variance =
565 (F16(VocAlgorithm_GATING_THRESHOLD) +
566 (fix16_mul(
567 F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
568 VocAlgorithm_GATING_THRESHOLD)),
569 VocAlgorithm__mean_variance_estimator___sigmoid__process(
570 params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
571 VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
572 params, F16(1.), gating_threshold_variance,
573 F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
574 sigmoid_gating_variance =
575 VocAlgorithm__mean_variance_estimator___sigmoid__process(
576 params, voc_index_from_prior);
577 params->m_Mean_Variance_Estimator__Gamma_Variance =
578 (fix16_mul(sigmoid_gating_variance, gamma_variance));
579 params->m_Mean_Variance_Estimator___Gating_Duration_Minutes =
580 (params->m_Mean_Variance_Estimator___Gating_Duration_Minutes +
581 (fix16_mul(F16((VocAlgorithm_SAMPLING_INTERVAL / 60.)),
582 ((fix16_mul((F16(1.) - sigmoid_gating_mean),
583 F16((1. + VocAlgorithm_GATING_MAX_RATIO)))) -
584 F16(VocAlgorithm_GATING_MAX_RATIO)))));
585 if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes <
586 F16(0.))) {
587 params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
588 }
589 if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes >
590 params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes)) {
591 params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
592 }
593 }
594
595 static void VocAlgorithm__mean_variance_estimator__process(
596 VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior) {
597
598 fix16_t delta_sgp;
599 fix16_t c;
600 fix16_t additional_scaling;
601
602 if ((params->m_Mean_Variance_Estimator___Initialized == false)) {
603 params->m_Mean_Variance_Estimator___Initialized = true;
604 params->m_Mean_Variance_Estimator___Sraw_Offset = sraw;
605 params->m_Mean_Variance_Estimator___Mean = F16(0.);
606 } else {
607 if (((params->m_Mean_Variance_Estimator___Mean >= F16(100.)) ||
608 (params->m_Mean_Variance_Estimator___Mean <= F16(-100.)))) {
609 params->m_Mean_Variance_Estimator___Sraw_Offset =
610 (params->m_Mean_Variance_Estimator___Sraw_Offset +
611 params->m_Mean_Variance_Estimator___Mean);
612 params->m_Mean_Variance_Estimator___Mean = F16(0.);
613 }
614 sraw = (sraw - params->m_Mean_Variance_Estimator___Sraw_Offset);
615 VocAlgorithm__mean_variance_estimator___calculate_gamma(
616 params, voc_index_from_prior);
617 delta_sgp = (fix16_div(
618 (sraw - params->m_Mean_Variance_Estimator___Mean),
619 F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING)));
620 if ((delta_sgp < F16(0.))) {
621 c = (params->m_Mean_Variance_Estimator___Std - delta_sgp);
622 } else {
623 c = (params->m_Mean_Variance_Estimator___Std + delta_sgp);
624 }
625 additional_scaling = F16(1.);
626 if ((c > F16(1440.))) {
627 additional_scaling = F16(4.);
628 }
629 params->m_Mean_Variance_Estimator___Std = (fix16_mul(
630 fix16_sqrt((fix16_mul(
631 additional_scaling,
632 (F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) -
633 params->m_Mean_Variance_Estimator__Gamma_Variance)))),
634 fix16_sqrt((
635 (fix16_mul(
636 params->m_Mean_Variance_Estimator___Std,
637 (fix16_div(
638 params->m_Mean_Variance_Estimator___Std,
639 (fix16_mul(
640 F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING),
641 additional_scaling)))))) +
642 (fix16_mul(
643 (fix16_div(
644 (fix16_mul(
645 params->m_Mean_Variance_Estimator__Gamma_Variance,
646 delta_sgp)),
647 additional_scaling)),
648 delta_sgp))))));
649 params->m_Mean_Variance_Estimator___Mean =
650 (params->m_Mean_Variance_Estimator___Mean +
651 (fix16_mul(params->m_Mean_Variance_Estimator__Gamma_Mean,
652 delta_sgp)));
653 }
654 }
655
656 static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
657 VocAlgorithmParams* params) {
658
659 VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
660 params, F16(0.), F16(0.), F16(0.));
661 }
662
663 static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
664 VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K) {
665
666 params->m_Mean_Variance_Estimator___Sigmoid__L = L;
667 params->m_Mean_Variance_Estimator___Sigmoid__K = K;
668 params->m_Mean_Variance_Estimator___Sigmoid__X0 = X0;
669 }
670
671 static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
672 VocAlgorithmParams* params, fix16_t sample) {
673
674 fix16_t x;
675
676 x = (fix16_mul(params->m_Mean_Variance_Estimator___Sigmoid__K,
677 (sample - params->m_Mean_Variance_Estimator___Sigmoid__X0)));
678 if ((x < F16(-50.))) {
679 return params->m_Mean_Variance_Estimator___Sigmoid__L;
680 } else if ((x > F16(50.))) {
681 return F16(0.);
682 } else {
683 return (fix16_div(params->m_Mean_Variance_Estimator___Sigmoid__L,
684 (F16(1.) + fix16_exp(x))));
685 }
686 }
687
688 static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params) {
689
690 VocAlgorithm__mox_model__set_parameters(params, F16(1.), F16(0.));
691 }
692
693 static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
694 fix16_t SRAW_STD,
695 fix16_t SRAW_MEAN) {
696
697 params->m_Mox_Model__Sraw_Std = SRAW_STD;
698 params->m_Mox_Model__Sraw_Mean = SRAW_MEAN;
699 }
700
701 static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
702 fix16_t sraw) {
703
704 return (fix16_mul((fix16_div((sraw - params->m_Mox_Model__Sraw_Mean),
705 (-(params->m_Mox_Model__Sraw_Std +
706 F16(VocAlgorithm_SRAW_STD_BONUS))))),
707 F16(VocAlgorithm_VOC_INDEX_GAIN)));
708 }
709
710 static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params) {
711
712 VocAlgorithm__sigmoid_scaled__set_parameters(params, F16(0.));
713 }
714
715 static void
716 VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams* params,
717 fix16_t offset) {
718
719 params->m_Sigmoid_Scaled__Offset = offset;
720 }
721
722 static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
723 fix16_t sample) {
724
725 fix16_t x;
726 fix16_t shift;
727
728 x = (fix16_mul(F16(VocAlgorithm_SIGMOID_K),
729 (sample - F16(VocAlgorithm_SIGMOID_X0))));
730 if ((x < F16(-50.))) {
731 return F16(VocAlgorithm_SIGMOID_L);
732 } else if ((x > F16(50.))) {
733 return F16(0.);
734 } else {
735 if ((sample >= F16(0.))) {
736 shift = (fix16_div(
737 (F16(VocAlgorithm_SIGMOID_L) -
738 (fix16_mul(F16(5.), params->m_Sigmoid_Scaled__Offset))),
739 F16(4.)));
740 return ((fix16_div((F16(VocAlgorithm_SIGMOID_L) + shift),
741 (F16(1.) + fix16_exp(x)))) -
742 shift);
743 } else {
744 return (fix16_mul(
745 (fix16_div(params->m_Sigmoid_Scaled__Offset,
746 F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT))),
747 (fix16_div(F16(VocAlgorithm_SIGMOID_L),
748 (F16(1.) + fix16_exp(x))))));
749 }
750 }
751 }
752
753 static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params) {
754
755 VocAlgorithm__adaptive_lowpass__set_parameters(params);
756 }
757
758 static void
759 VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams* params) {
760
761 params->m_Adaptive_Lowpass__A1 =
762 F16((VocAlgorithm_SAMPLING_INTERVAL /
763 (VocAlgorithm_LP_TAU_FAST + VocAlgorithm_SAMPLING_INTERVAL)));
764 params->m_Adaptive_Lowpass__A2 =
765 F16((VocAlgorithm_SAMPLING_INTERVAL /
766 (VocAlgorithm_LP_TAU_SLOW + VocAlgorithm_SAMPLING_INTERVAL)));
767 params->m_Adaptive_Lowpass___Initialized = false;
768 }
769
770 static fix16_t
771 VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams* params,
772 fix16_t sample) {
773
774 fix16_t abs_delta;
775 fix16_t F1;
776 fix16_t tau_a;
777 fix16_t a3;
778
779 if ((params->m_Adaptive_Lowpass___Initialized == false)) {
780 params->m_Adaptive_Lowpass___X1 = sample;
781 params->m_Adaptive_Lowpass___X2 = sample;
782 params->m_Adaptive_Lowpass___X3 = sample;
783 params->m_Adaptive_Lowpass___Initialized = true;
784 }
785 params->m_Adaptive_Lowpass___X1 =
786 ((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A1),
787 params->m_Adaptive_Lowpass___X1)) +
788 (fix16_mul(params->m_Adaptive_Lowpass__A1, sample)));
789 params->m_Adaptive_Lowpass___X2 =
790 ((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A2),
791 params->m_Adaptive_Lowpass___X2)) +
792 (fix16_mul(params->m_Adaptive_Lowpass__A2, sample)));
793 abs_delta =
794 (params->m_Adaptive_Lowpass___X1 - params->m_Adaptive_Lowpass___X2);
795 if ((abs_delta < F16(0.))) {
796 abs_delta = (-abs_delta);
797 }
798 F1 = fix16_exp((fix16_mul(F16(VocAlgorithm_LP_ALPHA), abs_delta)));
799 tau_a =
800 ((fix16_mul(F16((VocAlgorithm_LP_TAU_SLOW - VocAlgorithm_LP_TAU_FAST)),
801 F1)) +
802 F16(VocAlgorithm_LP_TAU_FAST));
803 a3 = (fix16_div(F16(VocAlgorithm_SAMPLING_INTERVAL),
804 (F16(VocAlgorithm_SAMPLING_INTERVAL) + tau_a)));
805 params->m_Adaptive_Lowpass___X3 =
806 ((fix16_mul((F16(1.) - a3), params->m_Adaptive_Lowpass___X3)) +
807 (fix16_mul(a3, sample)));
808 return params->m_Adaptive_Lowpass___X3;
809 }
810