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