1*c2a715b0Schristos /*	$NetBSD: sensirion_voc_algorithm.c,v 1.2 2021/10/18 14:14:07 christos Exp $
214fbe9c5Sbrad  */
314fbe9c5Sbrad 
414fbe9c5Sbrad /*
514fbe9c5Sbrad  * Copyright (c) 2021, Sensirion AG
614fbe9c5Sbrad  * All rights reserved.
714fbe9c5Sbrad  *
814fbe9c5Sbrad  * Redistribution and use in source and binary forms, with or without
914fbe9c5Sbrad  * modification, are permitted provided that the following conditions are met:
1014fbe9c5Sbrad  *
1114fbe9c5Sbrad  * * Redistributions of source code must retain the above copyright notice, this
1214fbe9c5Sbrad  *   list of conditions and the following disclaimer.
1314fbe9c5Sbrad  *
1414fbe9c5Sbrad  * * Redistributions in binary form must reproduce the above copyright notice,
1514fbe9c5Sbrad  *   this list of conditions and the following disclaimer in the documentation
1614fbe9c5Sbrad  *   and/or other materials provided with the distribution.
1714fbe9c5Sbrad  *
1814fbe9c5Sbrad  * * Neither the name of Sensirion AG nor the names of its
1914fbe9c5Sbrad  *   contributors may be used to endorse or promote products derived from
2014fbe9c5Sbrad  *   this software without specific prior written permission.
2114fbe9c5Sbrad  *
2214fbe9c5Sbrad  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2314fbe9c5Sbrad  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2414fbe9c5Sbrad  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2514fbe9c5Sbrad  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
2614fbe9c5Sbrad  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2714fbe9c5Sbrad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2814fbe9c5Sbrad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2914fbe9c5Sbrad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3014fbe9c5Sbrad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3114fbe9c5Sbrad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3214fbe9c5Sbrad  * POSSIBILITY OF SUCH DAMAGE.
3314fbe9c5Sbrad  */
3414fbe9c5Sbrad 
3514fbe9c5Sbrad #include "sensirion_voc_algorithm.h"
3614fbe9c5Sbrad 
3714fbe9c5Sbrad /* The fixed point arithmetic parts of this code were originally created by
3814fbe9c5Sbrad  * https://github.com/PetteriAimonen/libfixmath
3914fbe9c5Sbrad  */
4014fbe9c5Sbrad 
4114fbe9c5Sbrad /*!< the maximum value of fix16_t */
4214fbe9c5Sbrad #define FIX16_MAXIMUM 0x7FFFFFFF
4314fbe9c5Sbrad /*!< the minimum value of fix16_t */
4414fbe9c5Sbrad #define FIX16_MINIMUM 0x80000000
4514fbe9c5Sbrad /*!< the value used to indicate overflows when FIXMATH_NO_OVERFLOW is not
4614fbe9c5Sbrad  * specified */
4714fbe9c5Sbrad #define FIX16_OVERFLOW 0x80000000
4814fbe9c5Sbrad /*!< fix16_t value of 1 */
4914fbe9c5Sbrad #define FIX16_ONE 0x00010000
5014fbe9c5Sbrad 
fix16_from_int(int32_t a)5114fbe9c5Sbrad static inline fix16_t fix16_from_int(int32_t a) {
5214fbe9c5Sbrad     return a * FIX16_ONE;
5314fbe9c5Sbrad }
5414fbe9c5Sbrad 
fix16_cast_to_int(fix16_t a)5514fbe9c5Sbrad static inline int32_t fix16_cast_to_int(fix16_t a) {
5614fbe9c5Sbrad     return (a >= 0) ? (a >> 16) : -((-a) >> 16);
5714fbe9c5Sbrad }
5814fbe9c5Sbrad 
5914fbe9c5Sbrad /*! Multiplies the two given fix16_t's and returns the result. */
6014fbe9c5Sbrad static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1);
6114fbe9c5Sbrad 
6214fbe9c5Sbrad /*! Divides the first given fix16_t by the second and returns the result. */
6314fbe9c5Sbrad static fix16_t fix16_div(fix16_t inArg0, fix16_t inArg1);
6414fbe9c5Sbrad 
6514fbe9c5Sbrad /*! Returns the square root of the given fix16_t. */
6614fbe9c5Sbrad static fix16_t fix16_sqrt(fix16_t inValue);
6714fbe9c5Sbrad 
6814fbe9c5Sbrad /*! Returns the exponent (e^) of the given fix16_t. */
6914fbe9c5Sbrad static fix16_t fix16_exp(fix16_t inValue);
7014fbe9c5Sbrad 
fix16_mul(fix16_t inArg0,fix16_t inArg1)7114fbe9c5Sbrad static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) {
7214fbe9c5Sbrad     // Each argument is divided to 16-bit parts.
7314fbe9c5Sbrad     //					AB
7414fbe9c5Sbrad     //			*	 CD
7514fbe9c5Sbrad     // -----------
7614fbe9c5Sbrad     //					BD	16 * 16 -> 32 bit products
7714fbe9c5Sbrad     //				 CB
7814fbe9c5Sbrad     //				 AD
7914fbe9c5Sbrad     //				AC
8014fbe9c5Sbrad     //			 |----| 64 bit product
8114fbe9c5Sbrad     uint32_t absArg0 = (uint32_t)((inArg0 >= 0) ? inArg0 : (-inArg0));
8214fbe9c5Sbrad     uint32_t absArg1 = (uint32_t)((inArg1 >= 0) ? inArg1 : (-inArg1));
8314fbe9c5Sbrad     uint32_t A = (absArg0 >> 16), C = (absArg1 >> 16);
8414fbe9c5Sbrad     uint32_t B = (absArg0 & 0xFFFF), D = (absArg1 & 0xFFFF);
8514fbe9c5Sbrad 
8614fbe9c5Sbrad     uint32_t AC = A * C;
8714fbe9c5Sbrad     uint32_t AD_CB = A * D + C * B;
8814fbe9c5Sbrad     uint32_t BD = B * D;
8914fbe9c5Sbrad 
9014fbe9c5Sbrad     uint32_t product_hi = AC + (AD_CB >> 16);
9114fbe9c5Sbrad 
9214fbe9c5Sbrad     // Handle carry from lower 32 bits to upper part of result.
9314fbe9c5Sbrad     uint32_t ad_cb_temp = AD_CB << 16;
9414fbe9c5Sbrad     uint32_t product_lo = BD + ad_cb_temp;
9514fbe9c5Sbrad     if (product_lo < BD)
9614fbe9c5Sbrad         product_hi++;
9714fbe9c5Sbrad 
9814fbe9c5Sbrad #ifndef FIXMATH_NO_OVERFLOW
9914fbe9c5Sbrad     // The upper 17 bits should all be zero.
10014fbe9c5Sbrad     if (product_hi >> 15)
10114fbe9c5Sbrad         return (fix16_t)FIX16_OVERFLOW;
10214fbe9c5Sbrad #endif
10314fbe9c5Sbrad 
10414fbe9c5Sbrad #ifdef FIXMATH_NO_ROUNDING
10514fbe9c5Sbrad     fix16_t result = (fix16_t)((product_hi << 16) | (product_lo >> 16));
10614fbe9c5Sbrad     if ((inArg0 < 0) != (inArg1 < 0))
10714fbe9c5Sbrad         result = -result;
10814fbe9c5Sbrad     return result;
10914fbe9c5Sbrad #else
11014fbe9c5Sbrad     // Adding 0x8000 (= 0.5) and then using right shift
11114fbe9c5Sbrad     // achieves proper rounding to result.
11214fbe9c5Sbrad     // Handle carry from lower to upper part.
11314fbe9c5Sbrad     uint32_t product_lo_tmp = product_lo;
11414fbe9c5Sbrad     product_lo += 0x8000;
11514fbe9c5Sbrad     if (product_lo < product_lo_tmp)
11614fbe9c5Sbrad         product_hi++;
11714fbe9c5Sbrad 
11814fbe9c5Sbrad     // Discard the lowest 16 bits and convert back to signed result.
11914fbe9c5Sbrad     fix16_t result = (fix16_t)((product_hi << 16) | (product_lo >> 16));
12014fbe9c5Sbrad     if ((inArg0 < 0) != (inArg1 < 0))
12114fbe9c5Sbrad         result = -result;
12214fbe9c5Sbrad     return result;
12314fbe9c5Sbrad #endif
12414fbe9c5Sbrad }
12514fbe9c5Sbrad 
fix16_div(fix16_t a,fix16_t b)12614fbe9c5Sbrad static fix16_t fix16_div(fix16_t a, fix16_t b) {
12714fbe9c5Sbrad     // This uses the basic binary restoring division algorithm.
12814fbe9c5Sbrad     // It appears to be faster to do the whole division manually than
12914fbe9c5Sbrad     // trying to compose a 64-bit divide out of 32-bit divisions on
13014fbe9c5Sbrad     // platforms without hardware divide.
13114fbe9c5Sbrad 
13214fbe9c5Sbrad     if (b == 0)
13314fbe9c5Sbrad         return (fix16_t)FIX16_MINIMUM;
13414fbe9c5Sbrad 
13514fbe9c5Sbrad     uint32_t remainder = (uint32_t)((a >= 0) ? a : (-a));
13614fbe9c5Sbrad     uint32_t divider = (uint32_t)((b >= 0) ? b : (-b));
13714fbe9c5Sbrad 
13814fbe9c5Sbrad     uint32_t quotient = 0;
13914fbe9c5Sbrad     uint32_t bit = 0x10000;
14014fbe9c5Sbrad 
14114fbe9c5Sbrad     /* The algorithm requires D >= R */
14214fbe9c5Sbrad     while (divider < remainder) {
14314fbe9c5Sbrad         divider <<= 1;
14414fbe9c5Sbrad         bit <<= 1;
14514fbe9c5Sbrad     }
14614fbe9c5Sbrad 
14714fbe9c5Sbrad #ifndef FIXMATH_NO_OVERFLOW
14814fbe9c5Sbrad     if (!bit)
14914fbe9c5Sbrad         return (fix16_t)FIX16_OVERFLOW;
15014fbe9c5Sbrad #endif
15114fbe9c5Sbrad 
15214fbe9c5Sbrad     if (divider & 0x80000000) {
15314fbe9c5Sbrad         // Perform one step manually to avoid overflows later.
15414fbe9c5Sbrad         // We know that divider's bottom bit is 0 here.
15514fbe9c5Sbrad         if (remainder >= divider) {
15614fbe9c5Sbrad             quotient |= bit;
15714fbe9c5Sbrad             remainder -= divider;
15814fbe9c5Sbrad         }
15914fbe9c5Sbrad         divider >>= 1;
16014fbe9c5Sbrad         bit >>= 1;
16114fbe9c5Sbrad     }
16214fbe9c5Sbrad 
16314fbe9c5Sbrad     /* Main division loop */
16414fbe9c5Sbrad     while (bit && remainder) {
16514fbe9c5Sbrad         if (remainder >= divider) {
16614fbe9c5Sbrad             quotient |= bit;
16714fbe9c5Sbrad             remainder -= divider;
16814fbe9c5Sbrad         }
16914fbe9c5Sbrad 
17014fbe9c5Sbrad         remainder <<= 1;
17114fbe9c5Sbrad         bit >>= 1;
17214fbe9c5Sbrad     }
17314fbe9c5Sbrad 
17414fbe9c5Sbrad #ifndef FIXMATH_NO_ROUNDING
17514fbe9c5Sbrad     if (remainder >= divider) {
17614fbe9c5Sbrad         quotient++;
17714fbe9c5Sbrad     }
17814fbe9c5Sbrad #endif
17914fbe9c5Sbrad 
18014fbe9c5Sbrad     fix16_t result = (fix16_t)quotient;
18114fbe9c5Sbrad 
18214fbe9c5Sbrad     /* Figure out the sign of result */
18314fbe9c5Sbrad     if ((a < 0) != (b < 0)) {
18414fbe9c5Sbrad #ifndef FIXMATH_NO_OVERFLOW
18514fbe9c5Sbrad         if (result == FIX16_MINIMUM)
18614fbe9c5Sbrad             return (fix16_t)FIX16_OVERFLOW;
18714fbe9c5Sbrad #endif
18814fbe9c5Sbrad 
18914fbe9c5Sbrad         result = -result;
19014fbe9c5Sbrad     }
19114fbe9c5Sbrad 
19214fbe9c5Sbrad     return result;
19314fbe9c5Sbrad }
19414fbe9c5Sbrad 
fix16_sqrt(fix16_t x)19514fbe9c5Sbrad static fix16_t fix16_sqrt(fix16_t x) {
19614fbe9c5Sbrad     // It is assumed that x is not negative
19714fbe9c5Sbrad 
19814fbe9c5Sbrad     uint32_t num = (uint32_t)x;
19914fbe9c5Sbrad     uint32_t result = 0;
20014fbe9c5Sbrad     uint32_t bit;
20114fbe9c5Sbrad     uint8_t n;
20214fbe9c5Sbrad 
20314fbe9c5Sbrad     bit = (uint32_t)1 << 30;
20414fbe9c5Sbrad     while (bit > num)
20514fbe9c5Sbrad         bit >>= 2;
20614fbe9c5Sbrad 
20714fbe9c5Sbrad     // The main part is executed twice, in order to avoid
20814fbe9c5Sbrad     // using 64 bit values in computations.
20914fbe9c5Sbrad     for (n = 0; n < 2; n++) {
21014fbe9c5Sbrad         // First we get the top 24 bits of the answer.
21114fbe9c5Sbrad         while (bit) {
21214fbe9c5Sbrad             if (num >= result + bit) {
21314fbe9c5Sbrad                 num -= result + bit;
21414fbe9c5Sbrad                 result = (result >> 1) + bit;
21514fbe9c5Sbrad             } else {
21614fbe9c5Sbrad                 result = (result >> 1);
21714fbe9c5Sbrad             }
21814fbe9c5Sbrad             bit >>= 2;
21914fbe9c5Sbrad         }
22014fbe9c5Sbrad 
22114fbe9c5Sbrad         if (n == 0) {
22214fbe9c5Sbrad             // Then process it again to get the lowest 8 bits.
22314fbe9c5Sbrad             if (num > 65535) {
22414fbe9c5Sbrad                 // The remainder 'num' is too large to be shifted left
22514fbe9c5Sbrad                 // by 16, so we have to add 1 to result manually and
22614fbe9c5Sbrad                 // adjust 'num' accordingly.
22714fbe9c5Sbrad                 // num = a - (result + 0.5)^2
22814fbe9c5Sbrad                 //	 = num + result^2 - (result + 0.5)^2
22914fbe9c5Sbrad                 //	 = num - result - 0.5
23014fbe9c5Sbrad                 num -= result;
23114fbe9c5Sbrad                 num = (num << 16) - 0x8000;
23214fbe9c5Sbrad                 result = (result << 16) + 0x8000;
23314fbe9c5Sbrad             } else {
23414fbe9c5Sbrad                 num <<= 16;
23514fbe9c5Sbrad                 result <<= 16;
23614fbe9c5Sbrad             }
23714fbe9c5Sbrad 
23814fbe9c5Sbrad             bit = 1 << 14;
23914fbe9c5Sbrad         }
24014fbe9c5Sbrad     }
24114fbe9c5Sbrad 
24214fbe9c5Sbrad #ifndef FIXMATH_NO_ROUNDING
24314fbe9c5Sbrad     // Finally, if next bit would have been 1, round the result upwards.
24414fbe9c5Sbrad     if (num > result) {
24514fbe9c5Sbrad         result++;
24614fbe9c5Sbrad     }
24714fbe9c5Sbrad #endif
24814fbe9c5Sbrad 
24914fbe9c5Sbrad     return (fix16_t)result;
25014fbe9c5Sbrad }
25114fbe9c5Sbrad 
fix16_exp(fix16_t x)25214fbe9c5Sbrad static fix16_t fix16_exp(fix16_t x) {
25314fbe9c5Sbrad     // Function to approximate exp(); optimized more for code size than speed
25414fbe9c5Sbrad 
25514fbe9c5Sbrad     // exp(x) for x = +/- {1, 1/8, 1/64, 1/512}
25614fbe9c5Sbrad #define NUM_EXP_VALUES 4
25714fbe9c5Sbrad     static const fix16_t exp_pos_values[NUM_EXP_VALUES] = {
25814fbe9c5Sbrad         F16(2.7182818), F16(1.1331485), F16(1.0157477), F16(1.0019550)};
25914fbe9c5Sbrad     static const fix16_t exp_neg_values[NUM_EXP_VALUES] = {
26014fbe9c5Sbrad         F16(0.3678794), F16(0.8824969), F16(0.9844964), F16(0.9980488)};
26114fbe9c5Sbrad     const fix16_t* exp_values;
26214fbe9c5Sbrad 
26314fbe9c5Sbrad     fix16_t res, arg;
26414fbe9c5Sbrad     uint16_t i;
26514fbe9c5Sbrad 
26614fbe9c5Sbrad     if (x >= F16(10.3972))
26714fbe9c5Sbrad         return FIX16_MAXIMUM;
26814fbe9c5Sbrad     if (x <= F16(-11.7835))
26914fbe9c5Sbrad         return 0;
27014fbe9c5Sbrad 
27114fbe9c5Sbrad     if (x < 0) {
27214fbe9c5Sbrad         x = -x;
27314fbe9c5Sbrad         exp_values = exp_neg_values;
27414fbe9c5Sbrad     } else {
27514fbe9c5Sbrad         exp_values = exp_pos_values;
27614fbe9c5Sbrad     }
27714fbe9c5Sbrad 
27814fbe9c5Sbrad     res = FIX16_ONE;
27914fbe9c5Sbrad     arg = FIX16_ONE;
28014fbe9c5Sbrad     for (i = 0; i < NUM_EXP_VALUES; i++) {
28114fbe9c5Sbrad         while (x >= arg) {
28214fbe9c5Sbrad             res = fix16_mul(res, exp_values[i]);
28314fbe9c5Sbrad             x -= arg;
28414fbe9c5Sbrad         }
28514fbe9c5Sbrad         arg >>= 3;
28614fbe9c5Sbrad     }
28714fbe9c5Sbrad     return res;
28814fbe9c5Sbrad }
28914fbe9c5Sbrad 
29014fbe9c5Sbrad static void VocAlgorithm__init_instances(VocAlgorithmParams* params);
29114fbe9c5Sbrad static void
29214fbe9c5Sbrad VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams* params);
29314fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator___init_instances(
29414fbe9c5Sbrad     VocAlgorithmParams* params);
29514fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator__set_parameters(
29614fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t std_initial,
29714fbe9c5Sbrad     fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes);
29814fbe9c5Sbrad static void
29914fbe9c5Sbrad VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams* params,
30014fbe9c5Sbrad                                                   fix16_t mean, fix16_t std,
30114fbe9c5Sbrad                                                   fix16_t uptime_gamma);
30214fbe9c5Sbrad static fix16_t
30314fbe9c5Sbrad VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams* params);
30414fbe9c5Sbrad static fix16_t
30514fbe9c5Sbrad VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams* params);
30614fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
30714fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t voc_index_from_prior);
30814fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator__process(
30914fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior);
31014fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
31114fbe9c5Sbrad     VocAlgorithmParams* params);
31214fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
31314fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K);
31414fbe9c5Sbrad static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
31514fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t sample);
31614fbe9c5Sbrad static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params);
31714fbe9c5Sbrad static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
31814fbe9c5Sbrad                                                     fix16_t SRAW_STD,
31914fbe9c5Sbrad                                                     fix16_t SRAW_MEAN);
32014fbe9c5Sbrad static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
32114fbe9c5Sbrad                                                 fix16_t sraw);
32214fbe9c5Sbrad static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params);
32314fbe9c5Sbrad static void
32414fbe9c5Sbrad VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams* params,
32514fbe9c5Sbrad                                              fix16_t offset);
32614fbe9c5Sbrad static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
32714fbe9c5Sbrad                                                      fix16_t sample);
32814fbe9c5Sbrad static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params);
32914fbe9c5Sbrad static void
33014fbe9c5Sbrad VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams* params);
33114fbe9c5Sbrad static fix16_t
33214fbe9c5Sbrad VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams* params,
33314fbe9c5Sbrad                                         fix16_t sample);
33414fbe9c5Sbrad 
VocAlgorithm_init(VocAlgorithmParams * params)33514fbe9c5Sbrad void VocAlgorithm_init(VocAlgorithmParams* params) {
33614fbe9c5Sbrad 
33714fbe9c5Sbrad     params->mVoc_Index_Offset = F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT);
33814fbe9c5Sbrad     params->mTau_Mean_Variance_Hours =
33914fbe9c5Sbrad         F16(VocAlgorithm_TAU_MEAN_VARIANCE_HOURS);
34014fbe9c5Sbrad     params->mGating_Max_Duration_Minutes =
34114fbe9c5Sbrad         F16(VocAlgorithm_GATING_MAX_DURATION_MINUTES);
34214fbe9c5Sbrad     params->mSraw_Std_Initial = F16(VocAlgorithm_SRAW_STD_INITIAL);
34314fbe9c5Sbrad     params->mUptime = F16(0.);
34414fbe9c5Sbrad     params->mSraw = F16(0.);
34514fbe9c5Sbrad     params->mVoc_Index = 0;
34614fbe9c5Sbrad     VocAlgorithm__init_instances(params);
34714fbe9c5Sbrad }
34814fbe9c5Sbrad 
VocAlgorithm__init_instances(VocAlgorithmParams * params)34914fbe9c5Sbrad static void VocAlgorithm__init_instances(VocAlgorithmParams* params) {
35014fbe9c5Sbrad 
35114fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator__init(params);
35214fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator__set_parameters(
35314fbe9c5Sbrad         params, params->mSraw_Std_Initial, params->mTau_Mean_Variance_Hours,
35414fbe9c5Sbrad         params->mGating_Max_Duration_Minutes);
35514fbe9c5Sbrad     VocAlgorithm__mox_model__init(params);
35614fbe9c5Sbrad     VocAlgorithm__mox_model__set_parameters(
35714fbe9c5Sbrad         params, VocAlgorithm__mean_variance_estimator__get_std(params),
35814fbe9c5Sbrad         VocAlgorithm__mean_variance_estimator__get_mean(params));
35914fbe9c5Sbrad     VocAlgorithm__sigmoid_scaled__init(params);
36014fbe9c5Sbrad     VocAlgorithm__sigmoid_scaled__set_parameters(params,
36114fbe9c5Sbrad                                                  params->mVoc_Index_Offset);
36214fbe9c5Sbrad     VocAlgorithm__adaptive_lowpass__init(params);
36314fbe9c5Sbrad     VocAlgorithm__adaptive_lowpass__set_parameters(params);
36414fbe9c5Sbrad }
36514fbe9c5Sbrad 
VocAlgorithm_get_states(VocAlgorithmParams * params,int32_t * state0,int32_t * state1)36614fbe9c5Sbrad void VocAlgorithm_get_states(VocAlgorithmParams* params, int32_t* state0,
36714fbe9c5Sbrad                              int32_t* state1) {
36814fbe9c5Sbrad 
36914fbe9c5Sbrad     *state0 = VocAlgorithm__mean_variance_estimator__get_mean(params);
37014fbe9c5Sbrad     *state1 = VocAlgorithm__mean_variance_estimator__get_std(params);
37114fbe9c5Sbrad     return;
37214fbe9c5Sbrad }
37314fbe9c5Sbrad 
VocAlgorithm_set_states(VocAlgorithmParams * params,int32_t state0,int32_t state1)37414fbe9c5Sbrad void VocAlgorithm_set_states(VocAlgorithmParams* params, int32_t state0,
37514fbe9c5Sbrad                              int32_t state1) {
37614fbe9c5Sbrad 
37714fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator__set_states(
37814fbe9c5Sbrad         params, state0, state1, F16(VocAlgorithm_PERSISTENCE_UPTIME_GAMMA));
37914fbe9c5Sbrad     params->mSraw = state0;
38014fbe9c5Sbrad }
38114fbe9c5Sbrad 
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)38214fbe9c5Sbrad void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams* params,
38314fbe9c5Sbrad                                         int32_t voc_index_offset,
38414fbe9c5Sbrad                                         int32_t learning_time_hours,
38514fbe9c5Sbrad                                         int32_t gating_max_duration_minutes,
38614fbe9c5Sbrad                                         int32_t std_initial) {
38714fbe9c5Sbrad 
38814fbe9c5Sbrad     params->mVoc_Index_Offset = (fix16_from_int(voc_index_offset));
38914fbe9c5Sbrad     params->mTau_Mean_Variance_Hours = (fix16_from_int(learning_time_hours));
39014fbe9c5Sbrad     params->mGating_Max_Duration_Minutes =
39114fbe9c5Sbrad         (fix16_from_int(gating_max_duration_minutes));
39214fbe9c5Sbrad     params->mSraw_Std_Initial = (fix16_from_int(std_initial));
39314fbe9c5Sbrad     VocAlgorithm__init_instances(params);
39414fbe9c5Sbrad }
39514fbe9c5Sbrad 
VocAlgorithm_process(VocAlgorithmParams * params,int32_t sraw,int32_t * voc_index)39614fbe9c5Sbrad void VocAlgorithm_process(VocAlgorithmParams* params, int32_t sraw,
39714fbe9c5Sbrad                           int32_t* voc_index) {
39814fbe9c5Sbrad 
39914fbe9c5Sbrad     if ((params->mUptime <= F16(VocAlgorithm_INITIAL_BLACKOUT))) {
40014fbe9c5Sbrad         params->mUptime =
40114fbe9c5Sbrad             (params->mUptime + F16(VocAlgorithm_SAMPLING_INTERVAL));
40214fbe9c5Sbrad     } else {
40314fbe9c5Sbrad         if (((sraw > 0) && (sraw < 65000))) {
40414fbe9c5Sbrad             if ((sraw < 20001)) {
40514fbe9c5Sbrad                 sraw = 20001;
40614fbe9c5Sbrad             } else if ((sraw > 52767)) {
40714fbe9c5Sbrad                 sraw = 52767;
40814fbe9c5Sbrad             }
40914fbe9c5Sbrad             params->mSraw = (fix16_from_int((sraw - 20000)));
41014fbe9c5Sbrad         }
41114fbe9c5Sbrad         params->mVoc_Index =
41214fbe9c5Sbrad             VocAlgorithm__mox_model__process(params, params->mSraw);
41314fbe9c5Sbrad         params->mVoc_Index =
41414fbe9c5Sbrad             VocAlgorithm__sigmoid_scaled__process(params, params->mVoc_Index);
41514fbe9c5Sbrad         params->mVoc_Index =
41614fbe9c5Sbrad             VocAlgorithm__adaptive_lowpass__process(params, params->mVoc_Index);
41714fbe9c5Sbrad         if ((params->mVoc_Index < F16(0.5))) {
41814fbe9c5Sbrad             params->mVoc_Index = F16(0.5);
41914fbe9c5Sbrad         }
42014fbe9c5Sbrad         if ((params->mSraw > F16(0.))) {
42114fbe9c5Sbrad             VocAlgorithm__mean_variance_estimator__process(
42214fbe9c5Sbrad                 params, params->mSraw, params->mVoc_Index);
42314fbe9c5Sbrad             VocAlgorithm__mox_model__set_parameters(
42414fbe9c5Sbrad                 params, VocAlgorithm__mean_variance_estimator__get_std(params),
42514fbe9c5Sbrad                 VocAlgorithm__mean_variance_estimator__get_mean(params));
42614fbe9c5Sbrad         }
42714fbe9c5Sbrad     }
42814fbe9c5Sbrad     *voc_index = (fix16_cast_to_int((params->mVoc_Index + F16(0.5))));
42914fbe9c5Sbrad     return;
43014fbe9c5Sbrad }
43114fbe9c5Sbrad 
43214fbe9c5Sbrad static void
VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams * params)43314fbe9c5Sbrad VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams* params) {
43414fbe9c5Sbrad 
43514fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator__set_parameters(params, F16(0.),
43614fbe9c5Sbrad                                                           F16(0.), F16(0.));
43714fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator___init_instances(params);
43814fbe9c5Sbrad }
43914fbe9c5Sbrad 
VocAlgorithm__mean_variance_estimator___init_instances(VocAlgorithmParams * params)44014fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator___init_instances(
44114fbe9c5Sbrad     VocAlgorithmParams* params) {
44214fbe9c5Sbrad 
44314fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator___sigmoid__init(params);
44414fbe9c5Sbrad }
44514fbe9c5Sbrad 
VocAlgorithm__mean_variance_estimator__set_parameters(VocAlgorithmParams * params,fix16_t std_initial,fix16_t tau_mean_variance_hours,fix16_t gating_max_duration_minutes)44614fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator__set_parameters(
44714fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t std_initial,
44814fbe9c5Sbrad     fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes) {
44914fbe9c5Sbrad 
45014fbe9c5Sbrad     params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes =
45114fbe9c5Sbrad         gating_max_duration_minutes;
45214fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Initialized = false;
45314fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Mean = F16(0.);
45414fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Sraw_Offset = F16(0.);
45514fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Std = std_initial;
45614fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Gamma =
45714fbe9c5Sbrad         (fix16_div(F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
45814fbe9c5Sbrad                         (VocAlgorithm_SAMPLING_INTERVAL / 3600.))),
45914fbe9c5Sbrad                    (tau_mean_variance_hours +
46014fbe9c5Sbrad                     F16((VocAlgorithm_SAMPLING_INTERVAL / 3600.)))));
46114fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Gamma_Initial_Mean =
46214fbe9c5Sbrad         F16(((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
46314fbe9c5Sbrad               VocAlgorithm_SAMPLING_INTERVAL) /
46414fbe9c5Sbrad              (VocAlgorithm_TAU_INITIAL_MEAN + VocAlgorithm_SAMPLING_INTERVAL)));
46514fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Gamma_Initial_Variance = F16(
46614fbe9c5Sbrad         ((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
46714fbe9c5Sbrad           VocAlgorithm_SAMPLING_INTERVAL) /
46814fbe9c5Sbrad          (VocAlgorithm_TAU_INITIAL_VARIANCE + VocAlgorithm_SAMPLING_INTERVAL)));
46914fbe9c5Sbrad     params->m_Mean_Variance_Estimator__Gamma_Mean = F16(0.);
47014fbe9c5Sbrad     params->m_Mean_Variance_Estimator__Gamma_Variance = F16(0.);
47114fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Uptime_Gamma = F16(0.);
47214fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
47314fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
47414fbe9c5Sbrad }
47514fbe9c5Sbrad 
47614fbe9c5Sbrad static void
VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams * params,fix16_t mean,fix16_t std,fix16_t uptime_gamma)47714fbe9c5Sbrad VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams* params,
47814fbe9c5Sbrad                                                   fix16_t mean, fix16_t std,
47914fbe9c5Sbrad                                                   fix16_t uptime_gamma) {
48014fbe9c5Sbrad 
48114fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Mean = mean;
48214fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Std = std;
48314fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Uptime_Gamma = uptime_gamma;
48414fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Initialized = true;
48514fbe9c5Sbrad }
48614fbe9c5Sbrad 
48714fbe9c5Sbrad static fix16_t
VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams * params)48814fbe9c5Sbrad VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams* params) {
48914fbe9c5Sbrad 
49014fbe9c5Sbrad     return params->m_Mean_Variance_Estimator___Std;
49114fbe9c5Sbrad }
49214fbe9c5Sbrad 
49314fbe9c5Sbrad static fix16_t
VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams * params)49414fbe9c5Sbrad VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams* params) {
49514fbe9c5Sbrad 
49614fbe9c5Sbrad     return (params->m_Mean_Variance_Estimator___Mean +
49714fbe9c5Sbrad             params->m_Mean_Variance_Estimator___Sraw_Offset);
49814fbe9c5Sbrad }
49914fbe9c5Sbrad 
VocAlgorithm__mean_variance_estimator___calculate_gamma(VocAlgorithmParams * params,fix16_t voc_index_from_prior)50014fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
50114fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t voc_index_from_prior) {
50214fbe9c5Sbrad 
50314fbe9c5Sbrad     fix16_t uptime_limit;
50414fbe9c5Sbrad     fix16_t sigmoid_gamma_mean;
50514fbe9c5Sbrad     fix16_t gamma_mean;
50614fbe9c5Sbrad     fix16_t gating_threshold_mean;
50714fbe9c5Sbrad     fix16_t sigmoid_gating_mean;
50814fbe9c5Sbrad     fix16_t sigmoid_gamma_variance;
50914fbe9c5Sbrad     fix16_t gamma_variance;
51014fbe9c5Sbrad     fix16_t gating_threshold_variance;
51114fbe9c5Sbrad     fix16_t sigmoid_gating_variance;
51214fbe9c5Sbrad 
51314fbe9c5Sbrad     uptime_limit = F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX -
51414fbe9c5Sbrad                         VocAlgorithm_SAMPLING_INTERVAL));
51514fbe9c5Sbrad     if ((params->m_Mean_Variance_Estimator___Uptime_Gamma < uptime_limit)) {
51614fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Uptime_Gamma =
51714fbe9c5Sbrad             (params->m_Mean_Variance_Estimator___Uptime_Gamma +
51814fbe9c5Sbrad              F16(VocAlgorithm_SAMPLING_INTERVAL));
51914fbe9c5Sbrad     }
52014fbe9c5Sbrad     if ((params->m_Mean_Variance_Estimator___Uptime_Gating < uptime_limit)) {
52114fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Uptime_Gating =
52214fbe9c5Sbrad             (params->m_Mean_Variance_Estimator___Uptime_Gating +
52314fbe9c5Sbrad              F16(VocAlgorithm_SAMPLING_INTERVAL));
52414fbe9c5Sbrad     }
52514fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
52614fbe9c5Sbrad         params, F16(1.), F16(VocAlgorithm_INIT_DURATION_MEAN),
52714fbe9c5Sbrad         F16(VocAlgorithm_INIT_TRANSITION_MEAN));
52814fbe9c5Sbrad     sigmoid_gamma_mean =
52914fbe9c5Sbrad         VocAlgorithm__mean_variance_estimator___sigmoid__process(
53014fbe9c5Sbrad             params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
53114fbe9c5Sbrad     gamma_mean =
53214fbe9c5Sbrad         (params->m_Mean_Variance_Estimator___Gamma +
53314fbe9c5Sbrad          (fix16_mul((params->m_Mean_Variance_Estimator___Gamma_Initial_Mean -
53414fbe9c5Sbrad                      params->m_Mean_Variance_Estimator___Gamma),
53514fbe9c5Sbrad                     sigmoid_gamma_mean)));
53614fbe9c5Sbrad     gating_threshold_mean =
53714fbe9c5Sbrad         (F16(VocAlgorithm_GATING_THRESHOLD) +
53814fbe9c5Sbrad          (fix16_mul(
53914fbe9c5Sbrad              F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
54014fbe9c5Sbrad                   VocAlgorithm_GATING_THRESHOLD)),
54114fbe9c5Sbrad              VocAlgorithm__mean_variance_estimator___sigmoid__process(
54214fbe9c5Sbrad                  params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
54314fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
54414fbe9c5Sbrad         params, F16(1.), gating_threshold_mean,
54514fbe9c5Sbrad         F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
54614fbe9c5Sbrad     sigmoid_gating_mean =
54714fbe9c5Sbrad         VocAlgorithm__mean_variance_estimator___sigmoid__process(
54814fbe9c5Sbrad             params, voc_index_from_prior);
54914fbe9c5Sbrad     params->m_Mean_Variance_Estimator__Gamma_Mean =
55014fbe9c5Sbrad         (fix16_mul(sigmoid_gating_mean, gamma_mean));
55114fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
55214fbe9c5Sbrad         params, F16(1.), F16(VocAlgorithm_INIT_DURATION_VARIANCE),
55314fbe9c5Sbrad         F16(VocAlgorithm_INIT_TRANSITION_VARIANCE));
55414fbe9c5Sbrad     sigmoid_gamma_variance =
55514fbe9c5Sbrad         VocAlgorithm__mean_variance_estimator___sigmoid__process(
55614fbe9c5Sbrad             params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
55714fbe9c5Sbrad     gamma_variance =
55814fbe9c5Sbrad         (params->m_Mean_Variance_Estimator___Gamma +
55914fbe9c5Sbrad          (fix16_mul(
56014fbe9c5Sbrad              (params->m_Mean_Variance_Estimator___Gamma_Initial_Variance -
56114fbe9c5Sbrad               params->m_Mean_Variance_Estimator___Gamma),
56214fbe9c5Sbrad              (sigmoid_gamma_variance - sigmoid_gamma_mean))));
56314fbe9c5Sbrad     gating_threshold_variance =
56414fbe9c5Sbrad         (F16(VocAlgorithm_GATING_THRESHOLD) +
56514fbe9c5Sbrad          (fix16_mul(
56614fbe9c5Sbrad              F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
56714fbe9c5Sbrad                   VocAlgorithm_GATING_THRESHOLD)),
56814fbe9c5Sbrad              VocAlgorithm__mean_variance_estimator___sigmoid__process(
56914fbe9c5Sbrad                  params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
57014fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
57114fbe9c5Sbrad         params, F16(1.), gating_threshold_variance,
57214fbe9c5Sbrad         F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
57314fbe9c5Sbrad     sigmoid_gating_variance =
57414fbe9c5Sbrad         VocAlgorithm__mean_variance_estimator___sigmoid__process(
57514fbe9c5Sbrad             params, voc_index_from_prior);
57614fbe9c5Sbrad     params->m_Mean_Variance_Estimator__Gamma_Variance =
57714fbe9c5Sbrad         (fix16_mul(sigmoid_gating_variance, gamma_variance));
57814fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Gating_Duration_Minutes =
57914fbe9c5Sbrad         (params->m_Mean_Variance_Estimator___Gating_Duration_Minutes +
58014fbe9c5Sbrad          (fix16_mul(F16((VocAlgorithm_SAMPLING_INTERVAL / 60.)),
58114fbe9c5Sbrad                     ((fix16_mul((F16(1.) - sigmoid_gating_mean),
58214fbe9c5Sbrad                                 F16((1. + VocAlgorithm_GATING_MAX_RATIO)))) -
58314fbe9c5Sbrad                      F16(VocAlgorithm_GATING_MAX_RATIO)))));
58414fbe9c5Sbrad     if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes <
58514fbe9c5Sbrad          F16(0.))) {
58614fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
58714fbe9c5Sbrad     }
58814fbe9c5Sbrad     if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes >
58914fbe9c5Sbrad          params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes)) {
59014fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
59114fbe9c5Sbrad     }
59214fbe9c5Sbrad }
59314fbe9c5Sbrad 
VocAlgorithm__mean_variance_estimator__process(VocAlgorithmParams * params,fix16_t sraw,fix16_t voc_index_from_prior)59414fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator__process(
59514fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior) {
59614fbe9c5Sbrad 
59714fbe9c5Sbrad     fix16_t delta_sgp;
59814fbe9c5Sbrad     fix16_t c;
59914fbe9c5Sbrad     fix16_t additional_scaling;
60014fbe9c5Sbrad 
601*c2a715b0Schristos     if (!params->m_Mean_Variance_Estimator___Initialized) {
60214fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Initialized = true;
60314fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Sraw_Offset = sraw;
60414fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Mean = F16(0.);
60514fbe9c5Sbrad     } else {
60614fbe9c5Sbrad         if (((params->m_Mean_Variance_Estimator___Mean >= F16(100.)) ||
60714fbe9c5Sbrad              (params->m_Mean_Variance_Estimator___Mean <= F16(-100.)))) {
60814fbe9c5Sbrad             params->m_Mean_Variance_Estimator___Sraw_Offset =
60914fbe9c5Sbrad                 (params->m_Mean_Variance_Estimator___Sraw_Offset +
61014fbe9c5Sbrad                  params->m_Mean_Variance_Estimator___Mean);
61114fbe9c5Sbrad             params->m_Mean_Variance_Estimator___Mean = F16(0.);
61214fbe9c5Sbrad         }
61314fbe9c5Sbrad         sraw = (sraw - params->m_Mean_Variance_Estimator___Sraw_Offset);
61414fbe9c5Sbrad         VocAlgorithm__mean_variance_estimator___calculate_gamma(
61514fbe9c5Sbrad             params, voc_index_from_prior);
61614fbe9c5Sbrad         delta_sgp = (fix16_div(
61714fbe9c5Sbrad             (sraw - params->m_Mean_Variance_Estimator___Mean),
61814fbe9c5Sbrad             F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING)));
61914fbe9c5Sbrad         if ((delta_sgp < F16(0.))) {
62014fbe9c5Sbrad             c = (params->m_Mean_Variance_Estimator___Std - delta_sgp);
62114fbe9c5Sbrad         } else {
62214fbe9c5Sbrad             c = (params->m_Mean_Variance_Estimator___Std + delta_sgp);
62314fbe9c5Sbrad         }
62414fbe9c5Sbrad         additional_scaling = F16(1.);
62514fbe9c5Sbrad         if ((c > F16(1440.))) {
62614fbe9c5Sbrad             additional_scaling = F16(4.);
62714fbe9c5Sbrad         }
62814fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Std = (fix16_mul(
62914fbe9c5Sbrad             fix16_sqrt((fix16_mul(
63014fbe9c5Sbrad                 additional_scaling,
63114fbe9c5Sbrad                 (F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) -
63214fbe9c5Sbrad                  params->m_Mean_Variance_Estimator__Gamma_Variance)))),
63314fbe9c5Sbrad             fix16_sqrt((
63414fbe9c5Sbrad                 (fix16_mul(
63514fbe9c5Sbrad                     params->m_Mean_Variance_Estimator___Std,
63614fbe9c5Sbrad                     (fix16_div(
63714fbe9c5Sbrad                         params->m_Mean_Variance_Estimator___Std,
63814fbe9c5Sbrad                         (fix16_mul(
63914fbe9c5Sbrad                             F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING),
64014fbe9c5Sbrad                             additional_scaling)))))) +
64114fbe9c5Sbrad                 (fix16_mul(
64214fbe9c5Sbrad                     (fix16_div(
64314fbe9c5Sbrad                         (fix16_mul(
64414fbe9c5Sbrad                             params->m_Mean_Variance_Estimator__Gamma_Variance,
64514fbe9c5Sbrad                             delta_sgp)),
64614fbe9c5Sbrad                         additional_scaling)),
64714fbe9c5Sbrad                     delta_sgp))))));
64814fbe9c5Sbrad         params->m_Mean_Variance_Estimator___Mean =
64914fbe9c5Sbrad             (params->m_Mean_Variance_Estimator___Mean +
65014fbe9c5Sbrad              (fix16_mul(params->m_Mean_Variance_Estimator__Gamma_Mean,
65114fbe9c5Sbrad                         delta_sgp)));
65214fbe9c5Sbrad     }
65314fbe9c5Sbrad }
65414fbe9c5Sbrad 
VocAlgorithm__mean_variance_estimator___sigmoid__init(VocAlgorithmParams * params)65514fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
65614fbe9c5Sbrad     VocAlgorithmParams* params) {
65714fbe9c5Sbrad 
65814fbe9c5Sbrad     VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
65914fbe9c5Sbrad         params, F16(0.), F16(0.), F16(0.));
66014fbe9c5Sbrad }
66114fbe9c5Sbrad 
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(VocAlgorithmParams * params,fix16_t L,fix16_t X0,fix16_t K)66214fbe9c5Sbrad static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
66314fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K) {
66414fbe9c5Sbrad 
66514fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Sigmoid__L = L;
66614fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Sigmoid__K = K;
66714fbe9c5Sbrad     params->m_Mean_Variance_Estimator___Sigmoid__X0 = X0;
66814fbe9c5Sbrad }
66914fbe9c5Sbrad 
VocAlgorithm__mean_variance_estimator___sigmoid__process(VocAlgorithmParams * params,fix16_t sample)67014fbe9c5Sbrad static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
67114fbe9c5Sbrad     VocAlgorithmParams* params, fix16_t sample) {
67214fbe9c5Sbrad 
67314fbe9c5Sbrad     fix16_t x;
67414fbe9c5Sbrad 
67514fbe9c5Sbrad     x = (fix16_mul(params->m_Mean_Variance_Estimator___Sigmoid__K,
67614fbe9c5Sbrad                    (sample - params->m_Mean_Variance_Estimator___Sigmoid__X0)));
67714fbe9c5Sbrad     if ((x < F16(-50.))) {
67814fbe9c5Sbrad         return params->m_Mean_Variance_Estimator___Sigmoid__L;
67914fbe9c5Sbrad     } else if ((x > F16(50.))) {
68014fbe9c5Sbrad         return F16(0.);
68114fbe9c5Sbrad     } else {
68214fbe9c5Sbrad         return (fix16_div(params->m_Mean_Variance_Estimator___Sigmoid__L,
68314fbe9c5Sbrad                           (F16(1.) + fix16_exp(x))));
68414fbe9c5Sbrad     }
68514fbe9c5Sbrad }
68614fbe9c5Sbrad 
VocAlgorithm__mox_model__init(VocAlgorithmParams * params)68714fbe9c5Sbrad static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params) {
68814fbe9c5Sbrad 
68914fbe9c5Sbrad     VocAlgorithm__mox_model__set_parameters(params, F16(1.), F16(0.));
69014fbe9c5Sbrad }
69114fbe9c5Sbrad 
VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams * params,fix16_t SRAW_STD,fix16_t SRAW_MEAN)69214fbe9c5Sbrad static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
69314fbe9c5Sbrad                                                     fix16_t SRAW_STD,
69414fbe9c5Sbrad                                                     fix16_t SRAW_MEAN) {
69514fbe9c5Sbrad 
69614fbe9c5Sbrad     params->m_Mox_Model__Sraw_Std = SRAW_STD;
69714fbe9c5Sbrad     params->m_Mox_Model__Sraw_Mean = SRAW_MEAN;
69814fbe9c5Sbrad }
69914fbe9c5Sbrad 
VocAlgorithm__mox_model__process(VocAlgorithmParams * params,fix16_t sraw)70014fbe9c5Sbrad static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
70114fbe9c5Sbrad                                                 fix16_t sraw) {
70214fbe9c5Sbrad 
70314fbe9c5Sbrad     return (fix16_mul((fix16_div((sraw - params->m_Mox_Model__Sraw_Mean),
70414fbe9c5Sbrad                                  (-(params->m_Mox_Model__Sraw_Std +
70514fbe9c5Sbrad                                     F16(VocAlgorithm_SRAW_STD_BONUS))))),
70614fbe9c5Sbrad                       F16(VocAlgorithm_VOC_INDEX_GAIN)));
70714fbe9c5Sbrad }
70814fbe9c5Sbrad 
VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams * params)70914fbe9c5Sbrad static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params) {
71014fbe9c5Sbrad 
71114fbe9c5Sbrad     VocAlgorithm__sigmoid_scaled__set_parameters(params, F16(0.));
71214fbe9c5Sbrad }
71314fbe9c5Sbrad 
71414fbe9c5Sbrad static void
VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams * params,fix16_t offset)71514fbe9c5Sbrad VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams* params,
71614fbe9c5Sbrad                                              fix16_t offset) {
71714fbe9c5Sbrad 
71814fbe9c5Sbrad     params->m_Sigmoid_Scaled__Offset = offset;
71914fbe9c5Sbrad }
72014fbe9c5Sbrad 
VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams * params,fix16_t sample)72114fbe9c5Sbrad static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
72214fbe9c5Sbrad                                                      fix16_t sample) {
72314fbe9c5Sbrad 
72414fbe9c5Sbrad     fix16_t x;
72514fbe9c5Sbrad     fix16_t shift;
72614fbe9c5Sbrad 
72714fbe9c5Sbrad     x = (fix16_mul(F16(VocAlgorithm_SIGMOID_K),
72814fbe9c5Sbrad                    (sample - F16(VocAlgorithm_SIGMOID_X0))));
72914fbe9c5Sbrad     if ((x < F16(-50.))) {
73014fbe9c5Sbrad         return F16(VocAlgorithm_SIGMOID_L);
73114fbe9c5Sbrad     } else if ((x > F16(50.))) {
73214fbe9c5Sbrad         return F16(0.);
73314fbe9c5Sbrad     } else {
73414fbe9c5Sbrad         if ((sample >= F16(0.))) {
73514fbe9c5Sbrad             shift = (fix16_div(
73614fbe9c5Sbrad                 (F16(VocAlgorithm_SIGMOID_L) -
73714fbe9c5Sbrad                  (fix16_mul(F16(5.), params->m_Sigmoid_Scaled__Offset))),
73814fbe9c5Sbrad                 F16(4.)));
73914fbe9c5Sbrad             return ((fix16_div((F16(VocAlgorithm_SIGMOID_L) + shift),
74014fbe9c5Sbrad                                (F16(1.) + fix16_exp(x)))) -
74114fbe9c5Sbrad                     shift);
74214fbe9c5Sbrad         } else {
74314fbe9c5Sbrad             return (fix16_mul(
74414fbe9c5Sbrad                 (fix16_div(params->m_Sigmoid_Scaled__Offset,
74514fbe9c5Sbrad                            F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT))),
74614fbe9c5Sbrad                 (fix16_div(F16(VocAlgorithm_SIGMOID_L),
74714fbe9c5Sbrad                            (F16(1.) + fix16_exp(x))))));
74814fbe9c5Sbrad         }
74914fbe9c5Sbrad     }
75014fbe9c5Sbrad }
75114fbe9c5Sbrad 
VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams * params)75214fbe9c5Sbrad static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params) {
75314fbe9c5Sbrad 
75414fbe9c5Sbrad     VocAlgorithm__adaptive_lowpass__set_parameters(params);
75514fbe9c5Sbrad }
75614fbe9c5Sbrad 
75714fbe9c5Sbrad static void
VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams * params)75814fbe9c5Sbrad VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams* params) {
75914fbe9c5Sbrad 
76014fbe9c5Sbrad     params->m_Adaptive_Lowpass__A1 =
76114fbe9c5Sbrad         F16((VocAlgorithm_SAMPLING_INTERVAL /
76214fbe9c5Sbrad              (VocAlgorithm_LP_TAU_FAST + VocAlgorithm_SAMPLING_INTERVAL)));
76314fbe9c5Sbrad     params->m_Adaptive_Lowpass__A2 =
76414fbe9c5Sbrad         F16((VocAlgorithm_SAMPLING_INTERVAL /
76514fbe9c5Sbrad              (VocAlgorithm_LP_TAU_SLOW + VocAlgorithm_SAMPLING_INTERVAL)));
76614fbe9c5Sbrad     params->m_Adaptive_Lowpass___Initialized = false;
76714fbe9c5Sbrad }
76814fbe9c5Sbrad 
76914fbe9c5Sbrad static fix16_t
VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams * params,fix16_t sample)77014fbe9c5Sbrad VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams* params,
77114fbe9c5Sbrad                                         fix16_t sample) {
77214fbe9c5Sbrad 
77314fbe9c5Sbrad     fix16_t abs_delta;
77414fbe9c5Sbrad     fix16_t F1;
77514fbe9c5Sbrad     fix16_t tau_a;
77614fbe9c5Sbrad     fix16_t a3;
77714fbe9c5Sbrad 
778*c2a715b0Schristos     if (!params->m_Adaptive_Lowpass___Initialized) {
77914fbe9c5Sbrad         params->m_Adaptive_Lowpass___X1 = sample;
78014fbe9c5Sbrad         params->m_Adaptive_Lowpass___X2 = sample;
78114fbe9c5Sbrad         params->m_Adaptive_Lowpass___X3 = sample;
78214fbe9c5Sbrad         params->m_Adaptive_Lowpass___Initialized = true;
78314fbe9c5Sbrad     }
78414fbe9c5Sbrad     params->m_Adaptive_Lowpass___X1 =
78514fbe9c5Sbrad         ((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A1),
78614fbe9c5Sbrad                     params->m_Adaptive_Lowpass___X1)) +
78714fbe9c5Sbrad          (fix16_mul(params->m_Adaptive_Lowpass__A1, sample)));
78814fbe9c5Sbrad     params->m_Adaptive_Lowpass___X2 =
78914fbe9c5Sbrad         ((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A2),
79014fbe9c5Sbrad                     params->m_Adaptive_Lowpass___X2)) +
79114fbe9c5Sbrad          (fix16_mul(params->m_Adaptive_Lowpass__A2, sample)));
79214fbe9c5Sbrad     abs_delta =
79314fbe9c5Sbrad         (params->m_Adaptive_Lowpass___X1 - params->m_Adaptive_Lowpass___X2);
79414fbe9c5Sbrad     if ((abs_delta < F16(0.))) {
79514fbe9c5Sbrad         abs_delta = (-abs_delta);
79614fbe9c5Sbrad     }
79714fbe9c5Sbrad     F1 = fix16_exp((fix16_mul(F16(VocAlgorithm_LP_ALPHA), abs_delta)));
79814fbe9c5Sbrad     tau_a =
79914fbe9c5Sbrad         ((fix16_mul(F16((VocAlgorithm_LP_TAU_SLOW - VocAlgorithm_LP_TAU_FAST)),
80014fbe9c5Sbrad                     F1)) +
80114fbe9c5Sbrad          F16(VocAlgorithm_LP_TAU_FAST));
80214fbe9c5Sbrad     a3 = (fix16_div(F16(VocAlgorithm_SAMPLING_INTERVAL),
80314fbe9c5Sbrad                     (F16(VocAlgorithm_SAMPLING_INTERVAL) + tau_a)));
80414fbe9c5Sbrad     params->m_Adaptive_Lowpass___X3 =
80514fbe9c5Sbrad         ((fix16_mul((F16(1.) - a3), params->m_Adaptive_Lowpass___X3)) +
80614fbe9c5Sbrad          (fix16_mul(a3, sample)));
80714fbe9c5Sbrad     return params->m_Adaptive_Lowpass___X3;
80814fbe9c5Sbrad }
809