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