From 7b8086909e21332019c0a324aea73ff7620aaede Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Sun, 25 Jul 2021 05:26:33 -0700 Subject: [PATCH 1/9] added index algorithm --- README.rst | 46 +--- adafruit_sgp40.py | 33 ++- examples/sgp40_indextest.py | 23 ++ voc_index_algorithm.py | 509 ++++++++++++++++++++++++++++++++++++ 4 files changed, 574 insertions(+), 37 deletions(-) create mode 100644 examples/sgp40_indextest.py create mode 100644 voc_index_algorithm.py diff --git a/README.rst b/README.rst index f6ddeb7..0909fbe 100644 --- a/README.rst +++ b/README.rst @@ -1,23 +1,11 @@ Introduction ============ -.. image:: https://readthedocs.org/projects/adafruit-circuitpython-sgp40/badge/?version=latest - :target: https://circuitpython.readthedocs.io/projects/sgp40/en/latest/ - :alt: Documentation Status +CircuitPython library for the Adafruit SGP40 Air Quality Sensor / VOC Index Sensor Breakouts. +This fork adds DFRobots VOC Index algorithm to adafruits CircuitPython library. -.. image:: https://img.shields.io/discord/327254708534116352.svg - :target: https://adafru.it/discord - :alt: Discord - -.. image:: https://github.com/adafruit/Adafruit_CircuitPython_SGP40/workflows/Build%20CI/badge.svg - :target: https://github.com/adafruit/Adafruit_CircuitPython_SGP40/actions - :alt: Build Status - -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - :alt: Code Style: Black - -CircuitPython library for the Adafruit SGP40 Air Quality Sensor / VOC Index Sensor Breakouts +`Original Library `_ +`DFRobot Algorithm `_ Dependencies @@ -60,21 +48,7 @@ To install in a virtual environment in your current project: Usage Example ============= -.. code-block:: python3 - - import time - import board - import adafruit_sgp40 - - i2c = board.I2C() # uses board.SCL and board.SDA - sgp = adafruit_sgp40(i2c) - - while True: - print("Measurement: ", sgp.raw) - print("") - sleep(1) - -For humidity compensated raw gas readings, we'll need a secondary sensor such as the bme280 +For humidity compensated raw gas and voc index readings, we'll need a secondary sensor such as the bme280 .. code-block:: python3 @@ -90,12 +64,20 @@ For humidity compensated raw gas readings, we'll need a secondary sensor such as while True: temperature = bme280.temperature humidity = bme280.relative_humidity - compensated_raw_gas = sgp.measure_raw(temperature = temperature, relative_humidity = humidity) + + # Compensated raw gas reading + compensated_raw_gas = sgp.measure_index(temperature = temperature, relative_humidity = humidity) + + # Compensated voc index reading + voc_index = sgp.measure_raw(temperature = temperature, relative_humidity = humidity) + print(compensated_raw_gas) + print(voc_index) print("") time.sleep(1) +It may take several minutes for the VOC index to start changing as it calibrates the baseline readings. Contributing ============ diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index a9ecf10..d2e9b2e 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -32,6 +32,7 @@ from time import sleep from struct import unpack_from import adafruit_bus_device.i2c_device as i2c_device +from voc_index_algorithm import DFRobot_VOCAlgorithm __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SGP40.git" @@ -118,6 +119,7 @@ def __init__(self, i2c, address=0x59): self.i2c_device = i2c_device.I2CDevice(i2c, address) self._command_buffer = bytearray(2) self._measure_command = _READ_CMD + self._voc_algorithm = DFRobot_VOCAlgorithm() self.initialize() @@ -138,9 +140,10 @@ def initialize(self): featureset = self._read_word_from_command() if featureset[0] != 0x3220: - raise RuntimeError("Feature set does not match: %s" % hex(featureset[0])) + raise RuntimeError("Feature set does not match: %s" % + hex(featureset[0])) - # VocAlgorithm_init(&voc_algorithm_params) + self._voc_algorithm.vocalgorithm_init() # Self Test self._command_buffer[0] = 0x28 @@ -232,6 +235,26 @@ def measure_raw(self, temperature=25, relative_humidity=50): self._measure_command = bytearray(_cmd) return self.raw + def measure_index(self, temperature=25, relative_humidity=50): + """ Measure VOC index after humidity compensation + :param float temperature: The temperature in degrees Celsius, defaults + to :const:`25` + :param float relative_humidity: The relative humidity in percentage, defaults + to :const:`50` + :note VOC index can indicate the quality of the air directly. The larger the value, the worse the air quality. + :note 0-100,no need to ventilate, purify + :note 100-200,no need to ventilate, purify + :note 200-400,ventilate, purify + :note 00-500,ventilate, purify intensely + :return int The VOC index measured, ranged from 0 to 500 + """ + raw = self.measure_raw(temperature, relative_humidity) + if raw < 0: + return -1 + else: + vocIndex = self._voc_algorithm.vocalgorithm_process(raw) + return vocIndex + def _read_word_from_command( self, delay_ms=10, @@ -264,9 +287,9 @@ def _read_word_from_command( i2c.readinto(replybuffer, end=replylen) for i in range(0, replylen, 3): - if not self._check_crc8(replybuffer[i : i + 2], replybuffer[i + 2]): + if not self._check_crc8(replybuffer[i: i + 2], replybuffer[i + 2]): raise RuntimeError("CRC check failed while reading data") - readdata_buffer.append(unpack_from(">H", replybuffer[i : i + 2])[0]) + readdata_buffer.append(unpack_from(">H", replybuffer[i: i + 2])[0]) return readdata_buffer @@ -294,7 +317,7 @@ def _generate_crc(crc_buffer): if crc & 0x80: crc = ( crc << 1 - ) ^ 0x31 # 0x31 is the Seed for SGP40's CRC polynomial + ) ^ 0x31 # 0x31 is the Seed for SGP40's CRC polynomial else: crc = crc << 1 return crc & 0xFF # Returns only bottom 8 bits diff --git a/examples/sgp40_indextest.py b/examples/sgp40_indextest.py new file mode 100644 index 0000000..28b5fc9 --- /dev/null +++ b/examples/sgp40_indextest.py @@ -0,0 +1,23 @@ +import time +import board +import adafruit_sgp40 +import adafruit_bme280 + +# Boards i2c bus +i2c = board.I2C() # uses board.SCL and board.SDA +sgp = adafruit_sgp40.SGP40(i2c) + +# Humidity sensor for compensated Readings +bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) + +while True: + temperature = bme280.temperature + humidity = bme280.relative_humidity + + # Compensated voc index reading + voc_index = sgp.measure_raw( + temperature=temperature, relative_humidity=humidity) + + print(voc_index) + print("") + time.sleep(1) diff --git a/voc_index_algorithm.py b/voc_index_algorithm.py new file mode 100644 index 0000000..f11a366 --- /dev/null +++ b/voc_index_algorithm.py @@ -0,0 +1,509 @@ +VOCALGORITHM_SAMPLING_INTERVAL = (1.) +VOCALGORITHM_INITIAL_BLACKOUT = (45.) +VOCALGORITHM_VOC_INDEX_GAIN = (230.) +VOCALGORITHM_SRAW_STD_INITIAL = (50.) +VOCALGORITHM_SRAW_STD_BONUS = (220.) +VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = (12.) +VOCALGORITHM_TAU_INITIAL_MEAN = (20.) +VOCALGORITHM_INITI_DURATION_MEAN = (3600. * 0.75) +VOCALGORITHM_INITI_TRANSITION_MEAN = (0.01) +VOCALGORITHM_TAU_INITIAL_VARIANCE = (2500.) +VOCALGORITHM_INITI_DURATION_VARIANCE = ((3600. * 1.45)) +VOCALGORITHM_INITI_TRANSITION_VARIANCE = (0.01) +VOCALGORITHM_GATING_THRESHOLD = (340.) +VOCALGORITHM_GATING_THRESHOLD_INITIAL = (510.) +VOCALGORITHM_GATING_THRESHOLD_TRANSITION = (0.09) +VOCALGORITHM_GATING_MAX_DURATION_MINUTES = ((60. * 3.)) +VOCALGORITHM_GATING_MAX_RATIO = (0.3) +VOCALGORITHM_SIGMOID_L = (500.) +VOCALGORITHM_SIGMOID_K = (-0.0065) +VOCALGORITHM_SIGMOID_X0 = (213.) +VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = (100.) +VOCALGORITHM_LP_TAU_FAST = (20.0) +VOCALGORITHM_LP_TAU_SLOW = (500.0) +VOCALGORITHM_LP_ALPHA = (-0.2) +VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = ((3. * 3600.)) +VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = (64.) +VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = (32767.) +FIX16_MAXIMUM = 0x7FFFFFFF +FIX16_MINIMUM = 0x80000000 +FIX16_OVERFLOW = 0x80000000 +FIX16_ONE = 0x00010000 + + +class DFRobot_vocalgorithmParams: + def __init__(self): + self.mvoc_index_offset = 0 + self.mtau_mean_variance_hours = 0 + self.mgating_max_duration_minutes = 0 + self.msraw_std_initial = 0 + self.muptime = 0 + self.msraw = 0 + self.mvoc_index = 0 + self.m_mean_variance_estimator_gating_max_duration_minutes = 0 + self.m_mean_variance_estimator_initialized = 0 + self.m_mean_variance_estimator_mean = 0 + self.m_mean_variance_estimator_sraw_offset = 0 + self.m_mean_variance_estimator_std = 0 + self.m_mean_variance_estimator_gamma = 0 + self.m_mean_variance_estimator_gamma_initial_mean = 0 + self.m_mean_variance_estimator_gamma_initial_variance = 0 + self.m_mean_variance_estimator_gamma_mean = 0 + self.m_mean_variance_estimator__gamma_variance = 0 + self.m_mean_variance_estimator_uptime_gamma = 0 + self.m_mean_variance_estimator_uptime_gating = 0 + self.m_mean_variance_estimator_gating_duration_minutes = 0 + self.m_mean_variance_estimator_sigmoid_l = 0 + self.m_mean_variance_estimator_sigmoid_k = 0 + self.m_mean_variance_estimator_sigmoid_x0 = 0 + self.m_mox_model_sraw_mean = 0 + self.m_sigmoid_scaled_offset = 0 + self.m_adaptive_lowpass_a1 = 0 + self.m_adaptive_lowpass_a2 = 0 + self.m_adaptive_lowpass_initialized = 0 + self.m_adaptive_lowpass_x1 = 0 + self.m_adaptive_lowpass_x2 = 0 + self.m_adaptive_lowpass_x3 = 0 + + +class DFRobot_VOCAlgorithm: + + def __init__(self): + self.params = DFRobot_vocalgorithmParams() + + def _f16(self, x): + if x >= 0: + return int((x)*65536.0 + 0.5) + else: + return int((x)*65536.0 - 0.5) + + def _fix16_from_int(self, a): + return int(a * FIX16_ONE) + + def _fix16_cast_to_int(self, a): + return int(a) >> 16 + + def _fix16_mul(self, inarg0, inarg1): + inarg0 = int(inarg0) + inarg1 = int(inarg1) + A = (inarg0 >> 16) + if inarg0 < 0: + B = (inarg0 & 0xFFFFFFFF) & 0xFFFF + else: + B = inarg0 & 0xFFFF + C = (inarg1 >> 16) + if inarg1 < 0: + D = (inarg1 & 0xFFFFFFFF) & 0xFFFF + else: + D = inarg1 & 0xFFFF + AC = (A * C) + AD_CB = (A * D + C * B) + BD = (B * D) + product_hi = (AC + (AD_CB >> 16)) + ad_cb_temp = ((AD_CB) << 16) & 0xFFFFFFFF + product_lo = ((BD + ad_cb_temp)) & 0xFFFFFFFF + if product_lo < BD: + product_hi = product_hi+1 + if ((product_hi >> 31) != (product_hi >> 15)): + return FIX16_OVERFLOW + product_lo_tmp = product_lo & 0xFFFFFFFF + product_lo = (product_lo - 0x8000) & 0xFFFFFFFF + product_lo = ( + product_lo-((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF + if product_lo > product_lo_tmp: + product_hi = product_hi-1 + result = (product_hi << 16) | (product_lo >> 16) + result += 1 + return result + + def _fix16_div(self, a, b): + a = int(a) + b = int(b) + if b == 0: + return FIX16_MINIMUM + if a >= 0: + remainder = a + else: + remainder = (a*(-1)) & 0xFFFFFFFF + if b >= 0: + divider = b + else: + divider = (b*(-1)) & 0xFFFFFFFF + quotient = 0 + bit = 0x10000 + while (divider < remainder): + divider = divider << 1 + bit <<= 1 + if not bit: + return FIX16_OVERFLOW + if (divider & 0x80000000): + if (remainder >= divider): + quotient |= bit + remainder -= divider + divider >>= 1 + bit >>= 1 + while bit and remainder: + if (remainder >= divider): + quotient |= bit + remainder -= divider + remainder <<= 1 + bit >>= 1 + if (remainder >= divider): + quotient += 1 + result = quotient + if ((a ^ b) & 0x80000000): + if (result == FIX16_MINIMUM): + return FIX16_OVERFLOW + result = -result + return result + + def _fix16_sqrt(self, x): + x = int(x) + num = x & 0xFFFFFFFF + result = 0 + bit = 1 << 30 + while (bit > num): + bit >>= 2 + for n in range(0, 2): + while (bit): + if (num >= result + bit): + num = num-(result + bit) & 0xFFFFFFFF + result = (result >> 1) + bit + else: + result = (result >> 1) + bit >>= 2 + if n == 0: + if num > 65535: + num = (num - result) & 0xFFFFFFFF + num = ((num << 16) - 0x8000) & 0xFFFFFFFF + result = ((result << 16) + 0x8000) & 0xFFFFFFFF + else: + num = ((num << 16) & 0xFFFFFFFF) + result = ((result << 16) & 0xFFFFFFFF) + bit = 1 << 14 + if (num > result): + result += 1 + return result + + def _fix16_exp(self, x): + x = int(x) + exp_pos_values = [self._f16(2.7182818), self._f16( + 1.1331485), self._f16(1.0157477), self._f16(1.0019550)] + exp_neg_values = [self._f16(0.3678794), self._f16( + 0.8824969), self._f16(0.9844964), self._f16(0.9980488)] + if (x >= self._f16(10.3972)): + return FIX16_MAXIMUM + if (x <= self._f16(-11.7835)): + return 0 + if (x < 0): + x = -x + exp_values = exp_neg_values + else: + exp_values = exp_pos_values + res = FIX16_ONE + arg = FIX16_ONE + for i in range(0, 4): + while (x >= arg): + res = self._fix16_mul(res, exp_values[i]) + x -= arg + arg >>= 3 + return res + + def vocalgorithm_init(self): + self.params.mvoc_index_offset = ( + self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT)) + self.params.mtau_mean_variance_hours = self._f16( + VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS) + self.params.mgating_max_duration_minutes = self._f16( + VOCALGORITHM_GATING_MAX_DURATION_MINUTES) + self.params.msraw_std_initial = self._f16( + VOCALGORITHM_SRAW_STD_INITIAL) + self.params.muptime = self._f16(0.) + self.params.msraw = self._f16(0.) + self.params.mvoc_index = 0 + self._vocalgorithm__init_instances() + + def _vocalgorithm__init_instances(self): + self._vocalgorithm__mean_variance_estimator__init() + self._vocalgorithm__mean_variance_estimator__set_parameters(self._f16( + VOCALGORITHM_SRAW_STD_INITIAL), self.params.mtau_mean_variance_hours, self.params.mgating_max_duration_minutes) + self._vocalgorithm__mox_model__init() + self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( + ), self._vocalgorithm__mean_variance_estimator__get_mean()) + self._vocalgorithm__sigmoid_scaled__init() + self._vocalgorithm__sigmoid_scaled__set_parameters( + self.params.mvoc_index_offset) + self._vocalgorithm__adaptive_lowpass__init() + self._vocalgorithm__adaptive_lowpass__set_parameters() + + def _vocalgorithm_get_states(self, state0, state1): + state0 = self._vocalgorithm__mean_variance_estimator__get_mean() + state1 = _vocalgorithm__mean_variance_estimator__get_std() + return state0, state1 + + def _vocalgorithm_set_states(self, state0, state1): + self._vocalgorithm__mean_variance_estimator__set_states( + params, state0, state1, self._f16(VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA)) + self.params.msraw = state0 + + def _vocalgorithm_set_tuning_parameters(self, voc_index_offset, learning_time_hours, gating_max_duration_minutes, std_initial): + self.params.mvoc_index_offset = self._fix16_from_int(voc_index_offset) + self.params.mtau_mean_variance_hours = self._fix16_from_int( + learning_time_hours) + self.params.mgating_max_duration_minutes = self._fix16_from_int( + gating_max_duration_minutes) + self.params.msraw_std_initial = self._fix16_from_int(std_initial) + self._vocalgorithm__init_instances() + + def vocalgorithm_process(self, sraw): + if ((self.params.muptime <= self._f16(VOCALGORITHM_INITIAL_BLACKOUT))): + self.params.muptime = self.params.muptime + \ + self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + else: + if (((sraw > 0) and (sraw < 65000))): + if ((sraw < 20001)): + sraw = 20001 + elif((sraw > 52767)): + sraw = 52767 + self.params.msraw = self._fix16_from_int((sraw - 20000)) + self.params.mvoc_index = self._vocalgorithm__mox_model__process( + self.params.msraw) + self.params.mvoc_index = self._vocalgorithm__sigmoid_scaled__process( + self.params.mvoc_index) + self.params.mvoc_index = self._vocalgorithm__adaptive_lowpass__process( + self.params.mvoc_index) + if ((self.params.mvoc_index < self._f16(0.5))): + self.params.mvoc_index = self._f16(0.5) + if self.params.msraw > self._f16(0.): + self._vocalgorithm__mean_variance_estimator__process( + self.params.msraw, self.params.mvoc_index) + self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( + ), self._vocalgorithm__mean_variance_estimator__get_mean()) + voc_index = self._fix16_cast_to_int( + (self.params.mvoc_index + self._f16(0.5))) + return voc_index + + def _vocalgorithm__mean_variance_estimator__init(self): + self._vocalgorithm__mean_variance_estimator__set_parameters( + self._f16(0.), self._f16(0.), self._f16(0.)) + self._vocalgorithm__mean_variance_estimator___init_instances() + + def _vocalgorithm__mean_variance_estimator___init_instances(self): + self._vocalgorithm__mean_variance_estimator___sigmoid__init() + + def _vocalgorithm__mean_variance_estimator__set_parameters(self, std_initial, tau_mean_variance_hours, gating_max_duration_minutes): + self.params.m_mean_variance_estimator_gating_max_duration_minutes = gating_max_duration_minutes + self.params.m_mean_variance_estimator_initialized = 0 + self.params.m_mean_variance_estimator_mean = self._f16(0.) + self.params.m_mean_variance_estimator_sraw_offset = self._f16(0.) + self.params.m_mean_variance_estimator_std = std_initial + self.params.m_mean_variance_estimator_gamma = self._fix16_div(self._f16((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * (VOCALGORITHM_SAMPLING_INTERVAL / 3600.)) + ), (tau_mean_variance_hours + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 3600.)))) + self.params.m_mean_variance_estimator_gamma_initial_mean = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) + / (VOCALGORITHM_TAU_INITIAL_MEAN + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_mean_variance_estimator_gamma_initial_variance = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) + / (VOCALGORITHM_TAU_INITIAL_VARIANCE + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_mean_variance_estimator_gamma_mean = self._f16(0.) + self.params.m_mean_variance_estimator__gamma_variance = self._f16(0.) + self.params.m_mean_variance_estimator_uptime_gamma = self._f16(0.) + self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) + self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( + 0.) + + def _vocalgorithm__mean_variance_estimator__set_states(self, mean, std, uptime_gamma): + self.params.m_mean_variance_estimator_mean = mean + self.params.m_mean_variance_estimator_std = std + self.params.m_mean_variance_estimator_uptime_gamma = uptime_gamma + self.params.m_mean_variance_estimator_initialized = true + + def _vocalgorithm__mean_variance_estimator__get_std(self): + return self.params.m_mean_variance_estimator_std + + def _vocalgorithm__mean_variance_estimator__get_mean(self): + return (self.params.m_mean_variance_estimator_mean + self.params.m_mean_variance_estimator_sraw_offset) + + def _vocalgorithm__mean_variance_estimator___calculate_gamma(self, voc_index_from_prior): + uptime_limit = self._f16( + (VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX - VOCALGORITHM_SAMPLING_INTERVAL)) + if self.params.m_mean_variance_estimator_uptime_gamma < uptime_limit: + self.params.m_mean_variance_estimator_uptime_gamma = ( + self.params.m_mean_variance_estimator_uptime_gamma + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) + + if self.params.m_mean_variance_estimator_uptime_gating < uptime_limit: + self.params.m_mean_variance_estimator_uptime_gating = ( + self.params.m_mean_variance_estimator_uptime_gating + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( + VOCALGORITHM_INITI_DURATION_MEAN), self._f16(VOCALGORITHM_INITI_TRANSITION_MEAN)) + sigmoid_gamma_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gamma) + gamma_mean = (self.params.m_mean_variance_estimator_gamma + (self._fix16_mul( + (self.params.m_mean_variance_estimator_gamma_initial_mean - self.params.m_mean_variance_estimator_gamma), sigmoid_gamma_mean))) + gating_threshold_mean = (self._f16(VOCALGORITHM_GATING_THRESHOLD) + + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), + self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( + 1.), gating_threshold_mean, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) + + sigmoid_gating_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + voc_index_from_prior) + self.params.m_mean_variance_estimator_gamma_mean = ( + self._fix16_mul(sigmoid_gating_mean, gamma_mean)) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( + VOCALGORITHM_INITI_DURATION_VARIANCE), self._f16(VOCALGORITHM_INITI_TRANSITION_VARIANCE)) + + sigmoid_gamma_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gamma) + + gamma_variance = (self.params.m_mean_variance_estimator_gamma + + (self._fix16_mul((self.params.m_mean_variance_estimator_gamma_initial_variance + - self.params.m_mean_variance_estimator_gamma), + (sigmoid_gamma_variance - sigmoid_gamma_mean)))) + + gating_threshold_variance = (self._f16(VOCALGORITHM_GATING_THRESHOLD) + + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), + self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( + 1.), gating_threshold_variance, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) + + sigmoid_gating_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + voc_index_from_prior) + + self.params.m_mean_variance_estimator__gamma_variance = ( + self._fix16_mul(sigmoid_gating_variance, gamma_variance)) + + self.params.m_mean_variance_estimator_gating_duration_minutes = (self.params.m_mean_variance_estimator_gating_duration_minutes + + (self._fix16_mul(self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 60.)), + ((self._fix16_mul((self._f16(1.) - sigmoid_gating_mean), + self._f16((1. + VOCALGORITHM_GATING_MAX_RATIO)))) + - self._f16(VOCALGORITHM_GATING_MAX_RATIO))))) + + if ((self.params.m_mean_variance_estimator_gating_duration_minutes < self._f16(0.))): + self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( + 0.) + + if ((self.params.m_mean_variance_estimator_gating_duration_minutes > self.params.m_mean_variance_estimator_gating_max_duration_minutes)): + self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) + + def _vocalgorithm__mean_variance_estimator__process(self, sraw, voc_index_from_prior): + if ((self.params.m_mean_variance_estimator_initialized == 0)): + self.params.m_mean_variance_estimator_initialized = 1 + self.params.m_mean_variance_estimator_sraw_offset = sraw + self.params.m_mean_variance_estimator_mean = self._f16(0.) + else: + if (((self.params.m_mean_variance_estimator_mean >= self._f16(100.)) or (self.params.m_mean_variance_estimator_mean <= self._f16(-100.)))): + self.params.m_mean_variance_estimator_sraw_offset = ( + self.params.m_mean_variance_estimator_sraw_offset + self.params.m_mean_variance_estimator_mean) + self.params.m_mean_variance_estimator_mean = self._f16(0.) + + sraw = (sraw - self.params.m_mean_variance_estimator_sraw_offset) + self._vocalgorithm__mean_variance_estimator___calculate_gamma( + voc_index_from_prior) + delta_sgp = (self._fix16_div((sraw - self.params.m_mean_variance_estimator_mean), + self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING))) + if ((delta_sgp < self._f16(0.))): + c = (self.params.m_mean_variance_estimator_std - delta_sgp) + else: + c = (self.params.m_mean_variance_estimator_std + delta_sgp) + additional_scaling = self._f16(1.) + if ((c > self._f16(1440.))): + additional_scaling = self._f16(4.) + self.params.m_mean_variance_estimator_std = self._fix16_mul(self._fix16_sqrt((self._fix16_mul(additional_scaling, + (self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) - self.params.m_mean_variance_estimator__gamma_variance)))), + self._fix16_sqrt(((self._fix16_mul(self.params.m_mean_variance_estimator_std, + (self._fix16_div(self.params.m_mean_variance_estimator_std, + (self._fix16_mul(self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), additional_scaling)))))) + + (self._fix16_mul((self._fix16_div((self._fix16_mul(self.params.m_mean_variance_estimator__gamma_variance, delta_sgp)), additional_scaling)), delta_sgp))))) + self.params.m_mean_variance_estimator_mean = (self.params.m_mean_variance_estimator_mean + ( + self._fix16_mul(self.params.m_mean_variance_estimator_gamma_mean, delta_sgp))) + + def _vocalgorithm__mean_variance_estimator___sigmoid__init(self): + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( + self._f16(0.), self._f16(0.), self._f16(0.)) + + def _vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self, L, X0, K): + self.params.m_mean_variance_estimator_sigmoid_l = L + self.params.m_mean_variance_estimator_sigmoid_k = K + self.params.m_mean_variance_estimator_sigmoid_x0 = X0 + + def _vocalgorithm__mean_variance_estimator___sigmoid__process(self, sample): + x = (self._fix16_mul(self.params.m_mean_variance_estimator_sigmoid_k, + (sample - self.params.m_mean_variance_estimator_sigmoid_x0))) + if ((x < self._f16(-50.))): + return self.params.m_mean_variance_estimator_sigmoid_l + elif ((x > self._f16(50.))): + return self._f16(0.) + else: + return (self._fix16_div(self.params.m_mean_variance_estimator_sigmoid_l, (self._f16(1.) + self._fix16_exp(x)))) + + def _vocalgorithm__mox_model__init(self): + self._vocalgorithm__mox_model__set_parameters( + self._f16(1.), self._f16(0.)) + + def _vocalgorithm__mox_model__set_parameters(self, SRAW_STD, SRAW_MEAN): + self.params.m_mox_model_sraw_std = SRAW_STD + self.params.m_mox_model_sraw_mean = SRAW_MEAN + + def _vocalgorithm__mox_model__process(self, sraw): + return (self._fix16_mul((self._fix16_div((sraw - self.params.m_mox_model_sraw_mean), (-(self.params.m_mox_model_sraw_std + self._f16(VOCALGORITHM_SRAW_STD_BONUS))))), self._f16(VOCALGORITHM_VOC_INDEX_GAIN))) + + def _vocalgorithm__sigmoid_scaled__init(self): + self._vocalgorithm__sigmoid_scaled__set_parameters(self._f16(0.)) + + def _vocalgorithm__sigmoid_scaled__set_parameters(self, offset): + self.params.m_sigmoid_scaled_offset = offset + + def _vocalgorithm__sigmoid_scaled__process(self, sample): + x = (self._fix16_mul(self._f16(VOCALGORITHM_SIGMOID_K), + (sample - self._f16(VOCALGORITHM_SIGMOID_X0)))) + if ((x < self._f16(-50.))): + return self._f16(VOCALGORITHM_SIGMOID_L) + elif ((x > self._f16(50.))): + return self._f16(0.) + else: + if ((sample >= self._f16(0.))): + shift = (self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) - (self._fix16_mul( + self._f16(5.), self.params.m_sigmoid_scaled_offset))), self._f16(4.))) + return ((self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) + shift), (self._f16(1.) + self._fix16_exp(x)))) - shift) + else: + return (self._fix16_mul((self._fix16_div(self.params.m_sigmoid_scaled_offset, self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT))), + (self._fix16_div(self._f16(VOCALGORITHM_SIGMOID_L), (self._f16(1.) + self._fix16_exp(x)))))) + + def _vocalgorithm__adaptive_lowpass__init(self): + self._vocalgorithm__adaptive_lowpass__set_parameters() + + def _vocalgorithm__adaptive_lowpass__set_parameters(self): + self.params.m_adaptive_lowpass_a1 = self._f16( + (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_FAST + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_adaptive_lowpass_a2 = self._f16( + (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_SLOW + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_adaptive_lowpass_initialized = 0 + + def _vocalgorithm__adaptive_lowpass__process(self, sample): + if ((self.params.m_adaptive_lowpass_initialized == 0)): + self.params.m_adaptive_lowpass_x1 = sample + self.params.m_adaptive_lowpass_x2 = sample + self.params.m_adaptive_lowpass_x3 = sample + self.params.m_adaptive_lowpass_initialized = 1 + self.params.m_adaptive_lowpass_x1 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a1), + self.params.m_adaptive_lowpass_x1)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a1, sample))) + + self.params.m_adaptive_lowpass_x2 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a2), + self.params.m_adaptive_lowpass_x2)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a2, sample))) + + abs_delta = (self.params.m_adaptive_lowpass_x1 - + self.params.m_adaptive_lowpass_x2) + + if ((abs_delta < self._f16(0.))): + abs_delta = (-abs_delta) + F1 = self._fix16_exp( + (self._fix16_mul(self._f16(VOCALGORITHM_LP_ALPHA), abs_delta))) + tau_a = ((self._fix16_mul(self._f16((VOCALGORITHM_LP_TAU_SLOW - + VOCALGORITHM_LP_TAU_FAST)), F1)) + self._f16(VOCALGORITHM_LP_TAU_FAST)) + a3 = (self._fix16_div(self._f16(VOCALGORITHM_SAMPLING_INTERVAL), + (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a))) + self.params.m_adaptive_lowpass_x3 = ((self._fix16_mul((self._f16( + 1.) - a3), self.params.m_adaptive_lowpass_x3)) + (self._fix16_mul(a3, sample))) + return self.params.m_adaptive_lowpass_x3 From 7cca5857f198534a500d837b4bca6bc07cab2250 Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Mon, 26 Jul 2021 02:48:57 -0700 Subject: [PATCH 2/9] combined algorithm and lib --- README.rst | 8 +- adafruit_sgp40.py | 512 ++++++++++++++++++++++++++++++++++++++++- voc_index_algorithm.py | 509 ---------------------------------------- 3 files changed, 516 insertions(+), 513 deletions(-) delete mode 100644 voc_index_algorithm.py diff --git a/README.rst b/README.rst index 0909fbe..cae2fd2 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,12 @@ Introduction ============ +This fork adds DFRobots VOC Index algorithm to adafruits CircuitPython SGP40 library. + CircuitPython library for the Adafruit SGP40 Air Quality Sensor / VOC Index Sensor Breakouts. -This fork adds DFRobots VOC Index algorithm to adafruits CircuitPython library. -`Original Library `_ -`DFRobot Algorithm `_ +* `Original Library `_ +* `DFRobot Algorithm `_ Dependencies @@ -16,6 +17,7 @@ This driver depends on: * `Bus Device `_ * `Register `_ + Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading `the Adafruit library and driver bundle `_. diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index d2e9b2e..f0aa485 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -32,7 +32,517 @@ from time import sleep from struct import unpack_from import adafruit_bus_device.i2c_device as i2c_device -from voc_index_algorithm import DFRobot_VOCAlgorithm + +VOCALGORITHM_SAMPLING_INTERVAL = (1.) +VOCALGORITHM_INITIAL_BLACKOUT = (45.) +VOCALGORITHM_VOC_INDEX_GAIN = (230.) +VOCALGORITHM_SRAW_STD_INITIAL = (50.) +VOCALGORITHM_SRAW_STD_BONUS = (220.) +VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = (12.) +VOCALGORITHM_TAU_INITIAL_MEAN = (20.) +VOCALGORITHM_INITI_DURATION_MEAN = (3600. * 0.75) +VOCALGORITHM_INITI_TRANSITION_MEAN = (0.01) +VOCALGORITHM_TAU_INITIAL_VARIANCE = (2500.) +VOCALGORITHM_INITI_DURATION_VARIANCE = ((3600. * 1.45)) +VOCALGORITHM_INITI_TRANSITION_VARIANCE = (0.01) +VOCALGORITHM_GATING_THRESHOLD = (340.) +VOCALGORITHM_GATING_THRESHOLD_INITIAL = (510.) +VOCALGORITHM_GATING_THRESHOLD_TRANSITION = (0.09) +VOCALGORITHM_GATING_MAX_DURATION_MINUTES = ((60. * 3.)) +VOCALGORITHM_GATING_MAX_RATIO = (0.3) +VOCALGORITHM_SIGMOID_L = (500.) +VOCALGORITHM_SIGMOID_K = (-0.0065) +VOCALGORITHM_SIGMOID_X0 = (213.) +VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = (100.) +VOCALGORITHM_LP_TAU_FAST = (20.0) +VOCALGORITHM_LP_TAU_SLOW = (500.0) +VOCALGORITHM_LP_ALPHA = (-0.2) +VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = ((3. * 3600.)) +VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = (64.) +VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = (32767.) +FIX16_MAXIMUM = 0x7FFFFFFF +FIX16_MINIMUM = 0x80000000 +FIX16_OVERFLOW = 0x80000000 +FIX16_ONE = 0x00010000 + + +class DFRobot_vocalgorithmParams: + def __init__(self): + self.mvoc_index_offset = 0 + self.mtau_mean_variance_hours = 0 + self.mgating_max_duration_minutes = 0 + self.msraw_std_initial = 0 + self.muptime = 0 + self.msraw = 0 + self.mvoc_index = 0 + self.m_mean_variance_estimator_gating_max_duration_minutes = 0 + self.m_mean_variance_estimator_initialized = 0 + self.m_mean_variance_estimator_mean = 0 + self.m_mean_variance_estimator_sraw_offset = 0 + self.m_mean_variance_estimator_std = 0 + self.m_mean_variance_estimator_gamma = 0 + self.m_mean_variance_estimator_gamma_initial_mean = 0 + self.m_mean_variance_estimator_gamma_initial_variance = 0 + self.m_mean_variance_estimator_gamma_mean = 0 + self.m_mean_variance_estimator__gamma_variance = 0 + self.m_mean_variance_estimator_uptime_gamma = 0 + self.m_mean_variance_estimator_uptime_gating = 0 + self.m_mean_variance_estimator_gating_duration_minutes = 0 + self.m_mean_variance_estimator_sigmoid_l = 0 + self.m_mean_variance_estimator_sigmoid_k = 0 + self.m_mean_variance_estimator_sigmoid_x0 = 0 + self.m_mox_model_sraw_mean = 0 + self.m_sigmoid_scaled_offset = 0 + self.m_adaptive_lowpass_a1 = 0 + self.m_adaptive_lowpass_a2 = 0 + self.m_adaptive_lowpass_initialized = 0 + self.m_adaptive_lowpass_x1 = 0 + self.m_adaptive_lowpass_x2 = 0 + self.m_adaptive_lowpass_x3 = 0 + + +class DFRobot_VOCAlgorithm: + + def __init__(self): + self.params = DFRobot_vocalgorithmParams() + + def _f16(self, x): + if x >= 0: + return int((x)*65536.0 + 0.5) + else: + return int((x)*65536.0 - 0.5) + + def _fix16_from_int(self, a): + return int(a * FIX16_ONE) + + def _fix16_cast_to_int(self, a): + return int(a) >> 16 + + def _fix16_mul(self, inarg0, inarg1): + inarg0 = int(inarg0) + inarg1 = int(inarg1) + A = (inarg0 >> 16) + if inarg0 < 0: + B = (inarg0 & 0xFFFFFFFF) & 0xFFFF + else: + B = inarg0 & 0xFFFF + C = (inarg1 >> 16) + if inarg1 < 0: + D = (inarg1 & 0xFFFFFFFF) & 0xFFFF + else: + D = inarg1 & 0xFFFF + AC = (A * C) + AD_CB = (A * D + C * B) + BD = (B * D) + product_hi = (AC + (AD_CB >> 16)) + ad_cb_temp = ((AD_CB) << 16) & 0xFFFFFFFF + product_lo = ((BD + ad_cb_temp)) & 0xFFFFFFFF + if product_lo < BD: + product_hi = product_hi+1 + if ((product_hi >> 31) != (product_hi >> 15)): + return FIX16_OVERFLOW + product_lo_tmp = product_lo & 0xFFFFFFFF + product_lo = (product_lo - 0x8000) & 0xFFFFFFFF + product_lo = ( + product_lo-((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF + if product_lo > product_lo_tmp: + product_hi = product_hi-1 + result = (product_hi << 16) | (product_lo >> 16) + result += 1 + return result + + def _fix16_div(self, a, b): + a = int(a) + b = int(b) + if b == 0: + return FIX16_MINIMUM + if a >= 0: + remainder = a + else: + remainder = (a*(-1)) & 0xFFFFFFFF + if b >= 0: + divider = b + else: + divider = (b*(-1)) & 0xFFFFFFFF + quotient = 0 + bit = 0x10000 + while (divider < remainder): + divider = divider << 1 + bit <<= 1 + if not bit: + return FIX16_OVERFLOW + if (divider & 0x80000000): + if (remainder >= divider): + quotient |= bit + remainder -= divider + divider >>= 1 + bit >>= 1 + while bit and remainder: + if (remainder >= divider): + quotient |= bit + remainder -= divider + remainder <<= 1 + bit >>= 1 + if (remainder >= divider): + quotient += 1 + result = quotient + if ((a ^ b) & 0x80000000): + if (result == FIX16_MINIMUM): + return FIX16_OVERFLOW + result = -result + return result + + def _fix16_sqrt(self, x): + x = int(x) + num = x & 0xFFFFFFFF + result = 0 + bit = 1 << 30 + while (bit > num): + bit >>= 2 + for n in range(0, 2): + while (bit): + if (num >= result + bit): + num = num-(result + bit) & 0xFFFFFFFF + result = (result >> 1) + bit + else: + result = (result >> 1) + bit >>= 2 + if n == 0: + if num > 65535: + num = (num - result) & 0xFFFFFFFF + num = ((num << 16) - 0x8000) & 0xFFFFFFFF + result = ((result << 16) + 0x8000) & 0xFFFFFFFF + else: + num = ((num << 16) & 0xFFFFFFFF) + result = ((result << 16) & 0xFFFFFFFF) + bit = 1 << 14 + if (num > result): + result += 1 + return result + + def _fix16_exp(self, x): + x = int(x) + exp_pos_values = [self._f16(2.7182818), self._f16( + 1.1331485), self._f16(1.0157477), self._f16(1.0019550)] + exp_neg_values = [self._f16(0.3678794), self._f16( + 0.8824969), self._f16(0.9844964), self._f16(0.9980488)] + if (x >= self._f16(10.3972)): + return FIX16_MAXIMUM + if (x <= self._f16(-11.7835)): + return 0 + if (x < 0): + x = -x + exp_values = exp_neg_values + else: + exp_values = exp_pos_values + res = FIX16_ONE + arg = FIX16_ONE + for i in range(0, 4): + while (x >= arg): + res = self._fix16_mul(res, exp_values[i]) + x -= arg + arg >>= 3 + return res + + def vocalgorithm_init(self): + self.params.mvoc_index_offset = ( + self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT)) + self.params.mtau_mean_variance_hours = self._f16( + VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS) + self.params.mgating_max_duration_minutes = self._f16( + VOCALGORITHM_GATING_MAX_DURATION_MINUTES) + self.params.msraw_std_initial = self._f16( + VOCALGORITHM_SRAW_STD_INITIAL) + self.params.muptime = self._f16(0.) + self.params.msraw = self._f16(0.) + self.params.mvoc_index = 0 + self._vocalgorithm__init_instances() + + def _vocalgorithm__init_instances(self): + self._vocalgorithm__mean_variance_estimator__init() + self._vocalgorithm__mean_variance_estimator__set_parameters(self._f16( + VOCALGORITHM_SRAW_STD_INITIAL), self.params.mtau_mean_variance_hours, self.params.mgating_max_duration_minutes) + self._vocalgorithm__mox_model__init() + self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( + ), self._vocalgorithm__mean_variance_estimator__get_mean()) + self._vocalgorithm__sigmoid_scaled__init() + self._vocalgorithm__sigmoid_scaled__set_parameters( + self.params.mvoc_index_offset) + self._vocalgorithm__adaptive_lowpass__init() + self._vocalgorithm__adaptive_lowpass__set_parameters() + + def _vocalgorithm_get_states(self, state0, state1): + state0 = self._vocalgorithm__mean_variance_estimator__get_mean() + state1 = _vocalgorithm__mean_variance_estimator__get_std() + return state0, state1 + + def _vocalgorithm_set_states(self, state0, state1): + self._vocalgorithm__mean_variance_estimator__set_states( + params, state0, state1, self._f16(VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA)) + self.params.msraw = state0 + + def _vocalgorithm_set_tuning_parameters(self, voc_index_offset, learning_time_hours, gating_max_duration_minutes, std_initial): + self.params.mvoc_index_offset = self._fix16_from_int(voc_index_offset) + self.params.mtau_mean_variance_hours = self._fix16_from_int( + learning_time_hours) + self.params.mgating_max_duration_minutes = self._fix16_from_int( + gating_max_duration_minutes) + self.params.msraw_std_initial = self._fix16_from_int(std_initial) + self._vocalgorithm__init_instances() + + def vocalgorithm_process(self, sraw): + if ((self.params.muptime <= self._f16(VOCALGORITHM_INITIAL_BLACKOUT))): + self.params.muptime = self.params.muptime + \ + self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + else: + if (((sraw > 0) and (sraw < 65000))): + if ((sraw < 20001)): + sraw = 20001 + elif((sraw > 52767)): + sraw = 52767 + self.params.msraw = self._fix16_from_int((sraw - 20000)) + self.params.mvoc_index = self._vocalgorithm__mox_model__process( + self.params.msraw) + self.params.mvoc_index = self._vocalgorithm__sigmoid_scaled__process( + self.params.mvoc_index) + self.params.mvoc_index = self._vocalgorithm__adaptive_lowpass__process( + self.params.mvoc_index) + if ((self.params.mvoc_index < self._f16(0.5))): + self.params.mvoc_index = self._f16(0.5) + if self.params.msraw > self._f16(0.): + self._vocalgorithm__mean_variance_estimator__process( + self.params.msraw, self.params.mvoc_index) + self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( + ), self._vocalgorithm__mean_variance_estimator__get_mean()) + voc_index = self._fix16_cast_to_int( + (self.params.mvoc_index + self._f16(0.5))) + return voc_index + + def _vocalgorithm__mean_variance_estimator__init(self): + self._vocalgorithm__mean_variance_estimator__set_parameters( + self._f16(0.), self._f16(0.), self._f16(0.)) + self._vocalgorithm__mean_variance_estimator___init_instances() + + def _vocalgorithm__mean_variance_estimator___init_instances(self): + self._vocalgorithm__mean_variance_estimator___sigmoid__init() + + def _vocalgorithm__mean_variance_estimator__set_parameters(self, std_initial, tau_mean_variance_hours, gating_max_duration_minutes): + self.params.m_mean_variance_estimator_gating_max_duration_minutes = gating_max_duration_minutes + self.params.m_mean_variance_estimator_initialized = 0 + self.params.m_mean_variance_estimator_mean = self._f16(0.) + self.params.m_mean_variance_estimator_sraw_offset = self._f16(0.) + self.params.m_mean_variance_estimator_std = std_initial + self.params.m_mean_variance_estimator_gamma = self._fix16_div(self._f16((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * (VOCALGORITHM_SAMPLING_INTERVAL / 3600.)) + ), (tau_mean_variance_hours + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 3600.)))) + self.params.m_mean_variance_estimator_gamma_initial_mean = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) + / (VOCALGORITHM_TAU_INITIAL_MEAN + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_mean_variance_estimator_gamma_initial_variance = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) + / (VOCALGORITHM_TAU_INITIAL_VARIANCE + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_mean_variance_estimator_gamma_mean = self._f16(0.) + self.params.m_mean_variance_estimator__gamma_variance = self._f16(0.) + self.params.m_mean_variance_estimator_uptime_gamma = self._f16(0.) + self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) + self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( + 0.) + + def _vocalgorithm__mean_variance_estimator__set_states(self, mean, std, uptime_gamma): + self.params.m_mean_variance_estimator_mean = mean + self.params.m_mean_variance_estimator_std = std + self.params.m_mean_variance_estimator_uptime_gamma = uptime_gamma + self.params.m_mean_variance_estimator_initialized = true + + def _vocalgorithm__mean_variance_estimator__get_std(self): + return self.params.m_mean_variance_estimator_std + + def _vocalgorithm__mean_variance_estimator__get_mean(self): + return (self.params.m_mean_variance_estimator_mean + self.params.m_mean_variance_estimator_sraw_offset) + + def _vocalgorithm__mean_variance_estimator___calculate_gamma(self, voc_index_from_prior): + uptime_limit = self._f16( + (VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX - VOCALGORITHM_SAMPLING_INTERVAL)) + if self.params.m_mean_variance_estimator_uptime_gamma < uptime_limit: + self.params.m_mean_variance_estimator_uptime_gamma = ( + self.params.m_mean_variance_estimator_uptime_gamma + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) + + if self.params.m_mean_variance_estimator_uptime_gating < uptime_limit: + self.params.m_mean_variance_estimator_uptime_gating = ( + self.params.m_mean_variance_estimator_uptime_gating + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( + VOCALGORITHM_INITI_DURATION_MEAN), self._f16(VOCALGORITHM_INITI_TRANSITION_MEAN)) + sigmoid_gamma_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gamma) + gamma_mean = (self.params.m_mean_variance_estimator_gamma + (self._fix16_mul( + (self.params.m_mean_variance_estimator_gamma_initial_mean - self.params.m_mean_variance_estimator_gamma), sigmoid_gamma_mean))) + gating_threshold_mean = (self._f16(VOCALGORITHM_GATING_THRESHOLD) + + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), + self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( + 1.), gating_threshold_mean, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) + + sigmoid_gating_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + voc_index_from_prior) + self.params.m_mean_variance_estimator_gamma_mean = ( + self._fix16_mul(sigmoid_gating_mean, gamma_mean)) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( + VOCALGORITHM_INITI_DURATION_VARIANCE), self._f16(VOCALGORITHM_INITI_TRANSITION_VARIANCE)) + + sigmoid_gamma_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gamma) + + gamma_variance = (self.params.m_mean_variance_estimator_gamma + + (self._fix16_mul((self.params.m_mean_variance_estimator_gamma_initial_variance + - self.params.m_mean_variance_estimator_gamma), + (sigmoid_gamma_variance - sigmoid_gamma_mean)))) + + gating_threshold_variance = (self._f16(VOCALGORITHM_GATING_THRESHOLD) + + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), + self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( + 1.), gating_threshold_variance, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) + + sigmoid_gating_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + voc_index_from_prior) + + self.params.m_mean_variance_estimator__gamma_variance = ( + self._fix16_mul(sigmoid_gating_variance, gamma_variance)) + + self.params.m_mean_variance_estimator_gating_duration_minutes = (self.params.m_mean_variance_estimator_gating_duration_minutes + + (self._fix16_mul(self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 60.)), + ((self._fix16_mul((self._f16(1.) - sigmoid_gating_mean), + self._f16((1. + VOCALGORITHM_GATING_MAX_RATIO)))) + - self._f16(VOCALGORITHM_GATING_MAX_RATIO))))) + + if ((self.params.m_mean_variance_estimator_gating_duration_minutes < self._f16(0.))): + self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( + 0.) + + if ((self.params.m_mean_variance_estimator_gating_duration_minutes > self.params.m_mean_variance_estimator_gating_max_duration_minutes)): + self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) + + def _vocalgorithm__mean_variance_estimator__process(self, sraw, voc_index_from_prior): + if ((self.params.m_mean_variance_estimator_initialized == 0)): + self.params.m_mean_variance_estimator_initialized = 1 + self.params.m_mean_variance_estimator_sraw_offset = sraw + self.params.m_mean_variance_estimator_mean = self._f16(0.) + else: + if (((self.params.m_mean_variance_estimator_mean >= self._f16(100.)) or (self.params.m_mean_variance_estimator_mean <= self._f16(-100.)))): + self.params.m_mean_variance_estimator_sraw_offset = ( + self.params.m_mean_variance_estimator_sraw_offset + self.params.m_mean_variance_estimator_mean) + self.params.m_mean_variance_estimator_mean = self._f16(0.) + + sraw = (sraw - self.params.m_mean_variance_estimator_sraw_offset) + self._vocalgorithm__mean_variance_estimator___calculate_gamma( + voc_index_from_prior) + delta_sgp = (self._fix16_div((sraw - self.params.m_mean_variance_estimator_mean), + self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING))) + if ((delta_sgp < self._f16(0.))): + c = (self.params.m_mean_variance_estimator_std - delta_sgp) + else: + c = (self.params.m_mean_variance_estimator_std + delta_sgp) + additional_scaling = self._f16(1.) + if ((c > self._f16(1440.))): + additional_scaling = self._f16(4.) + self.params.m_mean_variance_estimator_std = self._fix16_mul(self._fix16_sqrt((self._fix16_mul(additional_scaling, + (self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) - self.params.m_mean_variance_estimator__gamma_variance)))), + self._fix16_sqrt(((self._fix16_mul(self.params.m_mean_variance_estimator_std, + (self._fix16_div(self.params.m_mean_variance_estimator_std, + (self._fix16_mul(self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), additional_scaling)))))) + + (self._fix16_mul((self._fix16_div((self._fix16_mul(self.params.m_mean_variance_estimator__gamma_variance, delta_sgp)), additional_scaling)), delta_sgp))))) + self.params.m_mean_variance_estimator_mean = (self.params.m_mean_variance_estimator_mean + ( + self._fix16_mul(self.params.m_mean_variance_estimator_gamma_mean, delta_sgp))) + + def _vocalgorithm__mean_variance_estimator___sigmoid__init(self): + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( + self._f16(0.), self._f16(0.), self._f16(0.)) + + def _vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self, L, X0, K): + self.params.m_mean_variance_estimator_sigmoid_l = L + self.params.m_mean_variance_estimator_sigmoid_k = K + self.params.m_mean_variance_estimator_sigmoid_x0 = X0 + + def _vocalgorithm__mean_variance_estimator___sigmoid__process(self, sample): + x = (self._fix16_mul(self.params.m_mean_variance_estimator_sigmoid_k, + (sample - self.params.m_mean_variance_estimator_sigmoid_x0))) + if ((x < self._f16(-50.))): + return self.params.m_mean_variance_estimator_sigmoid_l + elif ((x > self._f16(50.))): + return self._f16(0.) + else: + return (self._fix16_div(self.params.m_mean_variance_estimator_sigmoid_l, (self._f16(1.) + self._fix16_exp(x)))) + + def _vocalgorithm__mox_model__init(self): + self._vocalgorithm__mox_model__set_parameters( + self._f16(1.), self._f16(0.)) + + def _vocalgorithm__mox_model__set_parameters(self, SRAW_STD, SRAW_MEAN): + self.params.m_mox_model_sraw_std = SRAW_STD + self.params.m_mox_model_sraw_mean = SRAW_MEAN + + def _vocalgorithm__mox_model__process(self, sraw): + return (self._fix16_mul((self._fix16_div((sraw - self.params.m_mox_model_sraw_mean), (-(self.params.m_mox_model_sraw_std + self._f16(VOCALGORITHM_SRAW_STD_BONUS))))), self._f16(VOCALGORITHM_VOC_INDEX_GAIN))) + + def _vocalgorithm__sigmoid_scaled__init(self): + self._vocalgorithm__sigmoid_scaled__set_parameters(self._f16(0.)) + + def _vocalgorithm__sigmoid_scaled__set_parameters(self, offset): + self.params.m_sigmoid_scaled_offset = offset + + def _vocalgorithm__sigmoid_scaled__process(self, sample): + x = (self._fix16_mul(self._f16(VOCALGORITHM_SIGMOID_K), + (sample - self._f16(VOCALGORITHM_SIGMOID_X0)))) + if ((x < self._f16(-50.))): + return self._f16(VOCALGORITHM_SIGMOID_L) + elif ((x > self._f16(50.))): + return self._f16(0.) + else: + if ((sample >= self._f16(0.))): + shift = (self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) - (self._fix16_mul( + self._f16(5.), self.params.m_sigmoid_scaled_offset))), self._f16(4.))) + return ((self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) + shift), (self._f16(1.) + self._fix16_exp(x)))) - shift) + else: + return (self._fix16_mul((self._fix16_div(self.params.m_sigmoid_scaled_offset, self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT))), + (self._fix16_div(self._f16(VOCALGORITHM_SIGMOID_L), (self._f16(1.) + self._fix16_exp(x)))))) + + def _vocalgorithm__adaptive_lowpass__init(self): + self._vocalgorithm__adaptive_lowpass__set_parameters() + + def _vocalgorithm__adaptive_lowpass__set_parameters(self): + self.params.m_adaptive_lowpass_a1 = self._f16( + (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_FAST + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_adaptive_lowpass_a2 = self._f16( + (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_SLOW + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_adaptive_lowpass_initialized = 0 + + def _vocalgorithm__adaptive_lowpass__process(self, sample): + if ((self.params.m_adaptive_lowpass_initialized == 0)): + self.params.m_adaptive_lowpass_x1 = sample + self.params.m_adaptive_lowpass_x2 = sample + self.params.m_adaptive_lowpass_x3 = sample + self.params.m_adaptive_lowpass_initialized = 1 + self.params.m_adaptive_lowpass_x1 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a1), + self.params.m_adaptive_lowpass_x1)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a1, sample))) + + self.params.m_adaptive_lowpass_x2 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a2), + self.params.m_adaptive_lowpass_x2)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a2, sample))) + + abs_delta = (self.params.m_adaptive_lowpass_x1 - + self.params.m_adaptive_lowpass_x2) + + if ((abs_delta < self._f16(0.))): + abs_delta = (-abs_delta) + F1 = self._fix16_exp( + (self._fix16_mul(self._f16(VOCALGORITHM_LP_ALPHA), abs_delta))) + tau_a = ((self._fix16_mul(self._f16((VOCALGORITHM_LP_TAU_SLOW - + VOCALGORITHM_LP_TAU_FAST)), F1)) + self._f16(VOCALGORITHM_LP_TAU_FAST)) + a3 = (self._fix16_div(self._f16(VOCALGORITHM_SAMPLING_INTERVAL), + (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a))) + self.params.m_adaptive_lowpass_x3 = ((self._fix16_mul((self._f16( + 1.) - a3), self.params.m_adaptive_lowpass_x3)) + (self._fix16_mul(a3, sample))) + return self.params.m_adaptive_lowpass_x3 + __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SGP40.git" diff --git a/voc_index_algorithm.py b/voc_index_algorithm.py deleted file mode 100644 index f11a366..0000000 --- a/voc_index_algorithm.py +++ /dev/null @@ -1,509 +0,0 @@ -VOCALGORITHM_SAMPLING_INTERVAL = (1.) -VOCALGORITHM_INITIAL_BLACKOUT = (45.) -VOCALGORITHM_VOC_INDEX_GAIN = (230.) -VOCALGORITHM_SRAW_STD_INITIAL = (50.) -VOCALGORITHM_SRAW_STD_BONUS = (220.) -VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = (12.) -VOCALGORITHM_TAU_INITIAL_MEAN = (20.) -VOCALGORITHM_INITI_DURATION_MEAN = (3600. * 0.75) -VOCALGORITHM_INITI_TRANSITION_MEAN = (0.01) -VOCALGORITHM_TAU_INITIAL_VARIANCE = (2500.) -VOCALGORITHM_INITI_DURATION_VARIANCE = ((3600. * 1.45)) -VOCALGORITHM_INITI_TRANSITION_VARIANCE = (0.01) -VOCALGORITHM_GATING_THRESHOLD = (340.) -VOCALGORITHM_GATING_THRESHOLD_INITIAL = (510.) -VOCALGORITHM_GATING_THRESHOLD_TRANSITION = (0.09) -VOCALGORITHM_GATING_MAX_DURATION_MINUTES = ((60. * 3.)) -VOCALGORITHM_GATING_MAX_RATIO = (0.3) -VOCALGORITHM_SIGMOID_L = (500.) -VOCALGORITHM_SIGMOID_K = (-0.0065) -VOCALGORITHM_SIGMOID_X0 = (213.) -VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = (100.) -VOCALGORITHM_LP_TAU_FAST = (20.0) -VOCALGORITHM_LP_TAU_SLOW = (500.0) -VOCALGORITHM_LP_ALPHA = (-0.2) -VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = ((3. * 3600.)) -VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = (64.) -VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = (32767.) -FIX16_MAXIMUM = 0x7FFFFFFF -FIX16_MINIMUM = 0x80000000 -FIX16_OVERFLOW = 0x80000000 -FIX16_ONE = 0x00010000 - - -class DFRobot_vocalgorithmParams: - def __init__(self): - self.mvoc_index_offset = 0 - self.mtau_mean_variance_hours = 0 - self.mgating_max_duration_minutes = 0 - self.msraw_std_initial = 0 - self.muptime = 0 - self.msraw = 0 - self.mvoc_index = 0 - self.m_mean_variance_estimator_gating_max_duration_minutes = 0 - self.m_mean_variance_estimator_initialized = 0 - self.m_mean_variance_estimator_mean = 0 - self.m_mean_variance_estimator_sraw_offset = 0 - self.m_mean_variance_estimator_std = 0 - self.m_mean_variance_estimator_gamma = 0 - self.m_mean_variance_estimator_gamma_initial_mean = 0 - self.m_mean_variance_estimator_gamma_initial_variance = 0 - self.m_mean_variance_estimator_gamma_mean = 0 - self.m_mean_variance_estimator__gamma_variance = 0 - self.m_mean_variance_estimator_uptime_gamma = 0 - self.m_mean_variance_estimator_uptime_gating = 0 - self.m_mean_variance_estimator_gating_duration_minutes = 0 - self.m_mean_variance_estimator_sigmoid_l = 0 - self.m_mean_variance_estimator_sigmoid_k = 0 - self.m_mean_variance_estimator_sigmoid_x0 = 0 - self.m_mox_model_sraw_mean = 0 - self.m_sigmoid_scaled_offset = 0 - self.m_adaptive_lowpass_a1 = 0 - self.m_adaptive_lowpass_a2 = 0 - self.m_adaptive_lowpass_initialized = 0 - self.m_adaptive_lowpass_x1 = 0 - self.m_adaptive_lowpass_x2 = 0 - self.m_adaptive_lowpass_x3 = 0 - - -class DFRobot_VOCAlgorithm: - - def __init__(self): - self.params = DFRobot_vocalgorithmParams() - - def _f16(self, x): - if x >= 0: - return int((x)*65536.0 + 0.5) - else: - return int((x)*65536.0 - 0.5) - - def _fix16_from_int(self, a): - return int(a * FIX16_ONE) - - def _fix16_cast_to_int(self, a): - return int(a) >> 16 - - def _fix16_mul(self, inarg0, inarg1): - inarg0 = int(inarg0) - inarg1 = int(inarg1) - A = (inarg0 >> 16) - if inarg0 < 0: - B = (inarg0 & 0xFFFFFFFF) & 0xFFFF - else: - B = inarg0 & 0xFFFF - C = (inarg1 >> 16) - if inarg1 < 0: - D = (inarg1 & 0xFFFFFFFF) & 0xFFFF - else: - D = inarg1 & 0xFFFF - AC = (A * C) - AD_CB = (A * D + C * B) - BD = (B * D) - product_hi = (AC + (AD_CB >> 16)) - ad_cb_temp = ((AD_CB) << 16) & 0xFFFFFFFF - product_lo = ((BD + ad_cb_temp)) & 0xFFFFFFFF - if product_lo < BD: - product_hi = product_hi+1 - if ((product_hi >> 31) != (product_hi >> 15)): - return FIX16_OVERFLOW - product_lo_tmp = product_lo & 0xFFFFFFFF - product_lo = (product_lo - 0x8000) & 0xFFFFFFFF - product_lo = ( - product_lo-((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF - if product_lo > product_lo_tmp: - product_hi = product_hi-1 - result = (product_hi << 16) | (product_lo >> 16) - result += 1 - return result - - def _fix16_div(self, a, b): - a = int(a) - b = int(b) - if b == 0: - return FIX16_MINIMUM - if a >= 0: - remainder = a - else: - remainder = (a*(-1)) & 0xFFFFFFFF - if b >= 0: - divider = b - else: - divider = (b*(-1)) & 0xFFFFFFFF - quotient = 0 - bit = 0x10000 - while (divider < remainder): - divider = divider << 1 - bit <<= 1 - if not bit: - return FIX16_OVERFLOW - if (divider & 0x80000000): - if (remainder >= divider): - quotient |= bit - remainder -= divider - divider >>= 1 - bit >>= 1 - while bit and remainder: - if (remainder >= divider): - quotient |= bit - remainder -= divider - remainder <<= 1 - bit >>= 1 - if (remainder >= divider): - quotient += 1 - result = quotient - if ((a ^ b) & 0x80000000): - if (result == FIX16_MINIMUM): - return FIX16_OVERFLOW - result = -result - return result - - def _fix16_sqrt(self, x): - x = int(x) - num = x & 0xFFFFFFFF - result = 0 - bit = 1 << 30 - while (bit > num): - bit >>= 2 - for n in range(0, 2): - while (bit): - if (num >= result + bit): - num = num-(result + bit) & 0xFFFFFFFF - result = (result >> 1) + bit - else: - result = (result >> 1) - bit >>= 2 - if n == 0: - if num > 65535: - num = (num - result) & 0xFFFFFFFF - num = ((num << 16) - 0x8000) & 0xFFFFFFFF - result = ((result << 16) + 0x8000) & 0xFFFFFFFF - else: - num = ((num << 16) & 0xFFFFFFFF) - result = ((result << 16) & 0xFFFFFFFF) - bit = 1 << 14 - if (num > result): - result += 1 - return result - - def _fix16_exp(self, x): - x = int(x) - exp_pos_values = [self._f16(2.7182818), self._f16( - 1.1331485), self._f16(1.0157477), self._f16(1.0019550)] - exp_neg_values = [self._f16(0.3678794), self._f16( - 0.8824969), self._f16(0.9844964), self._f16(0.9980488)] - if (x >= self._f16(10.3972)): - return FIX16_MAXIMUM - if (x <= self._f16(-11.7835)): - return 0 - if (x < 0): - x = -x - exp_values = exp_neg_values - else: - exp_values = exp_pos_values - res = FIX16_ONE - arg = FIX16_ONE - for i in range(0, 4): - while (x >= arg): - res = self._fix16_mul(res, exp_values[i]) - x -= arg - arg >>= 3 - return res - - def vocalgorithm_init(self): - self.params.mvoc_index_offset = ( - self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT)) - self.params.mtau_mean_variance_hours = self._f16( - VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS) - self.params.mgating_max_duration_minutes = self._f16( - VOCALGORITHM_GATING_MAX_DURATION_MINUTES) - self.params.msraw_std_initial = self._f16( - VOCALGORITHM_SRAW_STD_INITIAL) - self.params.muptime = self._f16(0.) - self.params.msraw = self._f16(0.) - self.params.mvoc_index = 0 - self._vocalgorithm__init_instances() - - def _vocalgorithm__init_instances(self): - self._vocalgorithm__mean_variance_estimator__init() - self._vocalgorithm__mean_variance_estimator__set_parameters(self._f16( - VOCALGORITHM_SRAW_STD_INITIAL), self.params.mtau_mean_variance_hours, self.params.mgating_max_duration_minutes) - self._vocalgorithm__mox_model__init() - self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( - ), self._vocalgorithm__mean_variance_estimator__get_mean()) - self._vocalgorithm__sigmoid_scaled__init() - self._vocalgorithm__sigmoid_scaled__set_parameters( - self.params.mvoc_index_offset) - self._vocalgorithm__adaptive_lowpass__init() - self._vocalgorithm__adaptive_lowpass__set_parameters() - - def _vocalgorithm_get_states(self, state0, state1): - state0 = self._vocalgorithm__mean_variance_estimator__get_mean() - state1 = _vocalgorithm__mean_variance_estimator__get_std() - return state0, state1 - - def _vocalgorithm_set_states(self, state0, state1): - self._vocalgorithm__mean_variance_estimator__set_states( - params, state0, state1, self._f16(VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA)) - self.params.msraw = state0 - - def _vocalgorithm_set_tuning_parameters(self, voc_index_offset, learning_time_hours, gating_max_duration_minutes, std_initial): - self.params.mvoc_index_offset = self._fix16_from_int(voc_index_offset) - self.params.mtau_mean_variance_hours = self._fix16_from_int( - learning_time_hours) - self.params.mgating_max_duration_minutes = self._fix16_from_int( - gating_max_duration_minutes) - self.params.msraw_std_initial = self._fix16_from_int(std_initial) - self._vocalgorithm__init_instances() - - def vocalgorithm_process(self, sraw): - if ((self.params.muptime <= self._f16(VOCALGORITHM_INITIAL_BLACKOUT))): - self.params.muptime = self.params.muptime + \ - self._f16(VOCALGORITHM_SAMPLING_INTERVAL) - else: - if (((sraw > 0) and (sraw < 65000))): - if ((sraw < 20001)): - sraw = 20001 - elif((sraw > 52767)): - sraw = 52767 - self.params.msraw = self._fix16_from_int((sraw - 20000)) - self.params.mvoc_index = self._vocalgorithm__mox_model__process( - self.params.msraw) - self.params.mvoc_index = self._vocalgorithm__sigmoid_scaled__process( - self.params.mvoc_index) - self.params.mvoc_index = self._vocalgorithm__adaptive_lowpass__process( - self.params.mvoc_index) - if ((self.params.mvoc_index < self._f16(0.5))): - self.params.mvoc_index = self._f16(0.5) - if self.params.msraw > self._f16(0.): - self._vocalgorithm__mean_variance_estimator__process( - self.params.msraw, self.params.mvoc_index) - self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( - ), self._vocalgorithm__mean_variance_estimator__get_mean()) - voc_index = self._fix16_cast_to_int( - (self.params.mvoc_index + self._f16(0.5))) - return voc_index - - def _vocalgorithm__mean_variance_estimator__init(self): - self._vocalgorithm__mean_variance_estimator__set_parameters( - self._f16(0.), self._f16(0.), self._f16(0.)) - self._vocalgorithm__mean_variance_estimator___init_instances() - - def _vocalgorithm__mean_variance_estimator___init_instances(self): - self._vocalgorithm__mean_variance_estimator___sigmoid__init() - - def _vocalgorithm__mean_variance_estimator__set_parameters(self, std_initial, tau_mean_variance_hours, gating_max_duration_minutes): - self.params.m_mean_variance_estimator_gating_max_duration_minutes = gating_max_duration_minutes - self.params.m_mean_variance_estimator_initialized = 0 - self.params.m_mean_variance_estimator_mean = self._f16(0.) - self.params.m_mean_variance_estimator_sraw_offset = self._f16(0.) - self.params.m_mean_variance_estimator_std = std_initial - self.params.m_mean_variance_estimator_gamma = self._fix16_div(self._f16((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * (VOCALGORITHM_SAMPLING_INTERVAL / 3600.)) - ), (tau_mean_variance_hours + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 3600.)))) - self.params.m_mean_variance_estimator_gamma_initial_mean = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) - / (VOCALGORITHM_TAU_INITIAL_MEAN + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_mean_variance_estimator_gamma_initial_variance = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) - / (VOCALGORITHM_TAU_INITIAL_VARIANCE + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_mean_variance_estimator_gamma_mean = self._f16(0.) - self.params.m_mean_variance_estimator__gamma_variance = self._f16(0.) - self.params.m_mean_variance_estimator_uptime_gamma = self._f16(0.) - self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) - self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( - 0.) - - def _vocalgorithm__mean_variance_estimator__set_states(self, mean, std, uptime_gamma): - self.params.m_mean_variance_estimator_mean = mean - self.params.m_mean_variance_estimator_std = std - self.params.m_mean_variance_estimator_uptime_gamma = uptime_gamma - self.params.m_mean_variance_estimator_initialized = true - - def _vocalgorithm__mean_variance_estimator__get_std(self): - return self.params.m_mean_variance_estimator_std - - def _vocalgorithm__mean_variance_estimator__get_mean(self): - return (self.params.m_mean_variance_estimator_mean + self.params.m_mean_variance_estimator_sraw_offset) - - def _vocalgorithm__mean_variance_estimator___calculate_gamma(self, voc_index_from_prior): - uptime_limit = self._f16( - (VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX - VOCALGORITHM_SAMPLING_INTERVAL)) - if self.params.m_mean_variance_estimator_uptime_gamma < uptime_limit: - self.params.m_mean_variance_estimator_uptime_gamma = ( - self.params.m_mean_variance_estimator_uptime_gamma + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) - - if self.params.m_mean_variance_estimator_uptime_gating < uptime_limit: - self.params.m_mean_variance_estimator_uptime_gating = ( - self.params.m_mean_variance_estimator_uptime_gating + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( - VOCALGORITHM_INITI_DURATION_MEAN), self._f16(VOCALGORITHM_INITI_TRANSITION_MEAN)) - sigmoid_gamma_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - self.params.m_mean_variance_estimator_uptime_gamma) - gamma_mean = (self.params.m_mean_variance_estimator_gamma + (self._fix16_mul( - (self.params.m_mean_variance_estimator_gamma_initial_mean - self.params.m_mean_variance_estimator_gamma), sigmoid_gamma_mean))) - gating_threshold_mean = (self._f16(VOCALGORITHM_GATING_THRESHOLD) - + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), - self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( - 1.), gating_threshold_mean, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) - - sigmoid_gating_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - voc_index_from_prior) - self.params.m_mean_variance_estimator_gamma_mean = ( - self._fix16_mul(sigmoid_gating_mean, gamma_mean)) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( - VOCALGORITHM_INITI_DURATION_VARIANCE), self._f16(VOCALGORITHM_INITI_TRANSITION_VARIANCE)) - - sigmoid_gamma_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - self.params.m_mean_variance_estimator_uptime_gamma) - - gamma_variance = (self.params.m_mean_variance_estimator_gamma + - (self._fix16_mul((self.params.m_mean_variance_estimator_gamma_initial_variance - - self.params.m_mean_variance_estimator_gamma), - (sigmoid_gamma_variance - sigmoid_gamma_mean)))) - - gating_threshold_variance = (self._f16(VOCALGORITHM_GATING_THRESHOLD) - + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), - self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( - 1.), gating_threshold_variance, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) - - sigmoid_gating_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - voc_index_from_prior) - - self.params.m_mean_variance_estimator__gamma_variance = ( - self._fix16_mul(sigmoid_gating_variance, gamma_variance)) - - self.params.m_mean_variance_estimator_gating_duration_minutes = (self.params.m_mean_variance_estimator_gating_duration_minutes - + (self._fix16_mul(self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 60.)), - ((self._fix16_mul((self._f16(1.) - sigmoid_gating_mean), - self._f16((1. + VOCALGORITHM_GATING_MAX_RATIO)))) - - self._f16(VOCALGORITHM_GATING_MAX_RATIO))))) - - if ((self.params.m_mean_variance_estimator_gating_duration_minutes < self._f16(0.))): - self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( - 0.) - - if ((self.params.m_mean_variance_estimator_gating_duration_minutes > self.params.m_mean_variance_estimator_gating_max_duration_minutes)): - self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) - - def _vocalgorithm__mean_variance_estimator__process(self, sraw, voc_index_from_prior): - if ((self.params.m_mean_variance_estimator_initialized == 0)): - self.params.m_mean_variance_estimator_initialized = 1 - self.params.m_mean_variance_estimator_sraw_offset = sraw - self.params.m_mean_variance_estimator_mean = self._f16(0.) - else: - if (((self.params.m_mean_variance_estimator_mean >= self._f16(100.)) or (self.params.m_mean_variance_estimator_mean <= self._f16(-100.)))): - self.params.m_mean_variance_estimator_sraw_offset = ( - self.params.m_mean_variance_estimator_sraw_offset + self.params.m_mean_variance_estimator_mean) - self.params.m_mean_variance_estimator_mean = self._f16(0.) - - sraw = (sraw - self.params.m_mean_variance_estimator_sraw_offset) - self._vocalgorithm__mean_variance_estimator___calculate_gamma( - voc_index_from_prior) - delta_sgp = (self._fix16_div((sraw - self.params.m_mean_variance_estimator_mean), - self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING))) - if ((delta_sgp < self._f16(0.))): - c = (self.params.m_mean_variance_estimator_std - delta_sgp) - else: - c = (self.params.m_mean_variance_estimator_std + delta_sgp) - additional_scaling = self._f16(1.) - if ((c > self._f16(1440.))): - additional_scaling = self._f16(4.) - self.params.m_mean_variance_estimator_std = self._fix16_mul(self._fix16_sqrt((self._fix16_mul(additional_scaling, - (self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) - self.params.m_mean_variance_estimator__gamma_variance)))), - self._fix16_sqrt(((self._fix16_mul(self.params.m_mean_variance_estimator_std, - (self._fix16_div(self.params.m_mean_variance_estimator_std, - (self._fix16_mul(self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), additional_scaling)))))) - + (self._fix16_mul((self._fix16_div((self._fix16_mul(self.params.m_mean_variance_estimator__gamma_variance, delta_sgp)), additional_scaling)), delta_sgp))))) - self.params.m_mean_variance_estimator_mean = (self.params.m_mean_variance_estimator_mean + ( - self._fix16_mul(self.params.m_mean_variance_estimator_gamma_mean, delta_sgp))) - - def _vocalgorithm__mean_variance_estimator___sigmoid__init(self): - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( - self._f16(0.), self._f16(0.), self._f16(0.)) - - def _vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self, L, X0, K): - self.params.m_mean_variance_estimator_sigmoid_l = L - self.params.m_mean_variance_estimator_sigmoid_k = K - self.params.m_mean_variance_estimator_sigmoid_x0 = X0 - - def _vocalgorithm__mean_variance_estimator___sigmoid__process(self, sample): - x = (self._fix16_mul(self.params.m_mean_variance_estimator_sigmoid_k, - (sample - self.params.m_mean_variance_estimator_sigmoid_x0))) - if ((x < self._f16(-50.))): - return self.params.m_mean_variance_estimator_sigmoid_l - elif ((x > self._f16(50.))): - return self._f16(0.) - else: - return (self._fix16_div(self.params.m_mean_variance_estimator_sigmoid_l, (self._f16(1.) + self._fix16_exp(x)))) - - def _vocalgorithm__mox_model__init(self): - self._vocalgorithm__mox_model__set_parameters( - self._f16(1.), self._f16(0.)) - - def _vocalgorithm__mox_model__set_parameters(self, SRAW_STD, SRAW_MEAN): - self.params.m_mox_model_sraw_std = SRAW_STD - self.params.m_mox_model_sraw_mean = SRAW_MEAN - - def _vocalgorithm__mox_model__process(self, sraw): - return (self._fix16_mul((self._fix16_div((sraw - self.params.m_mox_model_sraw_mean), (-(self.params.m_mox_model_sraw_std + self._f16(VOCALGORITHM_SRAW_STD_BONUS))))), self._f16(VOCALGORITHM_VOC_INDEX_GAIN))) - - def _vocalgorithm__sigmoid_scaled__init(self): - self._vocalgorithm__sigmoid_scaled__set_parameters(self._f16(0.)) - - def _vocalgorithm__sigmoid_scaled__set_parameters(self, offset): - self.params.m_sigmoid_scaled_offset = offset - - def _vocalgorithm__sigmoid_scaled__process(self, sample): - x = (self._fix16_mul(self._f16(VOCALGORITHM_SIGMOID_K), - (sample - self._f16(VOCALGORITHM_SIGMOID_X0)))) - if ((x < self._f16(-50.))): - return self._f16(VOCALGORITHM_SIGMOID_L) - elif ((x > self._f16(50.))): - return self._f16(0.) - else: - if ((sample >= self._f16(0.))): - shift = (self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) - (self._fix16_mul( - self._f16(5.), self.params.m_sigmoid_scaled_offset))), self._f16(4.))) - return ((self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) + shift), (self._f16(1.) + self._fix16_exp(x)))) - shift) - else: - return (self._fix16_mul((self._fix16_div(self.params.m_sigmoid_scaled_offset, self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT))), - (self._fix16_div(self._f16(VOCALGORITHM_SIGMOID_L), (self._f16(1.) + self._fix16_exp(x)))))) - - def _vocalgorithm__adaptive_lowpass__init(self): - self._vocalgorithm__adaptive_lowpass__set_parameters() - - def _vocalgorithm__adaptive_lowpass__set_parameters(self): - self.params.m_adaptive_lowpass_a1 = self._f16( - (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_FAST + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_adaptive_lowpass_a2 = self._f16( - (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_SLOW + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_adaptive_lowpass_initialized = 0 - - def _vocalgorithm__adaptive_lowpass__process(self, sample): - if ((self.params.m_adaptive_lowpass_initialized == 0)): - self.params.m_adaptive_lowpass_x1 = sample - self.params.m_adaptive_lowpass_x2 = sample - self.params.m_adaptive_lowpass_x3 = sample - self.params.m_adaptive_lowpass_initialized = 1 - self.params.m_adaptive_lowpass_x1 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a1), - self.params.m_adaptive_lowpass_x1)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a1, sample))) - - self.params.m_adaptive_lowpass_x2 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a2), - self.params.m_adaptive_lowpass_x2)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a2, sample))) - - abs_delta = (self.params.m_adaptive_lowpass_x1 - - self.params.m_adaptive_lowpass_x2) - - if ((abs_delta < self._f16(0.))): - abs_delta = (-abs_delta) - F1 = self._fix16_exp( - (self._fix16_mul(self._f16(VOCALGORITHM_LP_ALPHA), abs_delta))) - tau_a = ((self._fix16_mul(self._f16((VOCALGORITHM_LP_TAU_SLOW - - VOCALGORITHM_LP_TAU_FAST)), F1)) + self._f16(VOCALGORITHM_LP_TAU_FAST)) - a3 = (self._fix16_div(self._f16(VOCALGORITHM_SAMPLING_INTERVAL), - (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a))) - self.params.m_adaptive_lowpass_x3 = ((self._fix16_mul((self._f16( - 1.) - a3), self.params.m_adaptive_lowpass_x3)) + (self._fix16_mul(a3, sample))) - return self.params.m_adaptive_lowpass_x3 From 791e169871142f7527fa833c6251084fab10f1f8 Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Sat, 28 Aug 2021 06:13:21 -0700 Subject: [PATCH 3/9] mv main to __init__ --- .../__init__.py | 0 adafruit_sgp40/voc_algorithm.py | 523 ++++++++++++++++++ 2 files changed, 523 insertions(+) rename adafruit_sgp40.py => adafruit_sgp40/__init__.py (100%) create mode 100644 adafruit_sgp40/voc_algorithm.py diff --git a/adafruit_sgp40.py b/adafruit_sgp40/__init__.py similarity index 100% rename from adafruit_sgp40.py rename to adafruit_sgp40/__init__.py diff --git a/adafruit_sgp40/voc_algorithm.py b/adafruit_sgp40/voc_algorithm.py new file mode 100644 index 0000000..0242e6c --- /dev/null +++ b/adafruit_sgp40/voc_algorithm.py @@ -0,0 +1,523 @@ +# SPDX-FileCopyrightText: Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com) +# +# SPDX-License-Identifier: MIT +""" +`voc_algorithm` +================================================================================ + +Class and algorithm to convert Sensirion sgp40 raw reading to indexed voc readings. + + +* Author(s): yangfeng + +""" + +VOCALGORITHM_SAMPLING_INTERVAL = (1.) +VOCALGORITHM_INITIAL_BLACKOUT = (45.) +VOCALGORITHM_VOC_INDEX_GAIN = (230.) +VOCALGORITHM_SRAW_STD_INITIAL = (50.) +VOCALGORITHM_SRAW_STD_BONUS = (220.) +VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = (12.) +VOCALGORITHM_TAU_INITIAL_MEAN = (20.) +VOCALGORITHM_INITI_DURATION_MEAN = (3600. * 0.75) +VOCALGORITHM_INITI_TRANSITION_MEAN = (0.01) +VOCALGORITHM_TAU_INITIAL_VARIANCE = (2500.) +VOCALGORITHM_INITI_DURATION_VARIANCE = ((3600. * 1.45)) +VOCALGORITHM_INITI_TRANSITION_VARIANCE = (0.01) +VOCALGORITHM_GATING_THRESHOLD = (340.) +VOCALGORITHM_GATING_THRESHOLD_INITIAL = (510.) +VOCALGORITHM_GATING_THRESHOLD_TRANSITION = (0.09) +VOCALGORITHM_GATING_MAX_DURATION_MINUTES = ((60. * 3.)) +VOCALGORITHM_GATING_MAX_RATIO = (0.3) +VOCALGORITHM_SIGMOID_L = (500.) +VOCALGORITHM_SIGMOID_K = (-0.0065) +VOCALGORITHM_SIGMOID_X0 = (213.) +VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = (100.) +VOCALGORITHM_LP_TAU_FAST = (20.0) +VOCALGORITHM_LP_TAU_SLOW = (500.0) +VOCALGORITHM_LP_ALPHA = (-0.2) +VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = ((3. * 3600.)) +VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = (64.) +VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = (32767.) +FIX16_MAXIMUM = 0x7FFFFFFF +FIX16_MINIMUM = 0x80000000 +FIX16_OVERFLOW = 0x80000000 +FIX16_ONE = 0x00010000 + + +class DFRobot_vocalgorithmParams: + def __init__(self): + self.mvoc_index_offset = 0 + self.mtau_mean_variance_hours = 0 + self.mgating_max_duration_minutes = 0 + self.msraw_std_initial = 0 + self.muptime = 0 + self.msraw = 0 + self.mvoc_index = 0 + self.m_mean_variance_estimator_gating_max_duration_minutes = 0 + self.m_mean_variance_estimator_initialized = 0 + self.m_mean_variance_estimator_mean = 0 + self.m_mean_variance_estimator_sraw_offset = 0 + self.m_mean_variance_estimator_std = 0 + self.m_mean_variance_estimator_gamma = 0 + self.m_mean_variance_estimator_gamma_initial_mean = 0 + self.m_mean_variance_estimator_gamma_initial_variance = 0 + self.m_mean_variance_estimator_gamma_mean = 0 + self.m_mean_variance_estimator__gamma_variance = 0 + self.m_mean_variance_estimator_uptime_gamma = 0 + self.m_mean_variance_estimator_uptime_gating = 0 + self.m_mean_variance_estimator_gating_duration_minutes = 0 + self.m_mean_variance_estimator_sigmoid_l = 0 + self.m_mean_variance_estimator_sigmoid_k = 0 + self.m_mean_variance_estimator_sigmoid_x0 = 0 + self.m_mox_model_sraw_mean = 0 + self.m_sigmoid_scaled_offset = 0 + self.m_adaptive_lowpass_a1 = 0 + self.m_adaptive_lowpass_a2 = 0 + self.m_adaptive_lowpass_initialized = 0 + self.m_adaptive_lowpass_x1 = 0 + self.m_adaptive_lowpass_x2 = 0 + self.m_adaptive_lowpass_x3 = 0 + + +class VOCAlgorithm: + + def __init__(self): + self.params = DFRobot_vocalgorithmParams() + + def _f16(self, x): + if x >= 0: + return int((x)*65536.0 + 0.5) + else: + return int((x)*65536.0 - 0.5) + + def _fix16_from_int(self, a): + return int(a * FIX16_ONE) + + def _fix16_cast_to_int(self, a): + return int(a) >> 16 + + def _fix16_mul(self, inarg0, inarg1): + inarg0 = int(inarg0) + inarg1 = int(inarg1) + A = (inarg0 >> 16) + if inarg0 < 0: + B = (inarg0 & 0xFFFFFFFF) & 0xFFFF + else: + B = inarg0 & 0xFFFF + C = (inarg1 >> 16) + if inarg1 < 0: + D = (inarg1 & 0xFFFFFFFF) & 0xFFFF + else: + D = inarg1 & 0xFFFF + AC = (A * C) + AD_CB = (A * D + C * B) + BD = (B * D) + product_hi = (AC + (AD_CB >> 16)) + ad_cb_temp = ((AD_CB) << 16) & 0xFFFFFFFF + product_lo = ((BD + ad_cb_temp)) & 0xFFFFFFFF + if product_lo < BD: + product_hi = product_hi+1 + if ((product_hi >> 31) != (product_hi >> 15)): + return FIX16_OVERFLOW + product_lo_tmp = product_lo & 0xFFFFFFFF + product_lo = (product_lo - 0x8000) & 0xFFFFFFFF + product_lo = ( + product_lo-((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF + if product_lo > product_lo_tmp: + product_hi = product_hi-1 + result = (product_hi << 16) | (product_lo >> 16) + result += 1 + return result + + def _fix16_div(self, a, b): + a = int(a) + b = int(b) + if b == 0: + return FIX16_MINIMUM + if a >= 0: + remainder = a + else: + remainder = (a*(-1)) & 0xFFFFFFFF + if b >= 0: + divider = b + else: + divider = (b*(-1)) & 0xFFFFFFFF + quotient = 0 + bit = 0x10000 + while (divider < remainder): + divider = divider << 1 + bit <<= 1 + if not bit: + return FIX16_OVERFLOW + if (divider & 0x80000000): + if (remainder >= divider): + quotient |= bit + remainder -= divider + divider >>= 1 + bit >>= 1 + while bit and remainder: + if (remainder >= divider): + quotient |= bit + remainder -= divider + remainder <<= 1 + bit >>= 1 + if (remainder >= divider): + quotient += 1 + result = quotient + if ((a ^ b) & 0x80000000): + if (result == FIX16_MINIMUM): + return FIX16_OVERFLOW + result = -result + return result + + def _fix16_sqrt(self, x): + x = int(x) + num = x & 0xFFFFFFFF + result = 0 + bit = 1 << 30 + while (bit > num): + bit >>= 2 + for n in range(0, 2): + while (bit): + if (num >= result + bit): + num = num-(result + bit) & 0xFFFFFFFF + result = (result >> 1) + bit + else: + result = (result >> 1) + bit >>= 2 + if n == 0: + if num > 65535: + num = (num - result) & 0xFFFFFFFF + num = ((num << 16) - 0x8000) & 0xFFFFFFFF + result = ((result << 16) + 0x8000) & 0xFFFFFFFF + else: + num = ((num << 16) & 0xFFFFFFFF) + result = ((result << 16) & 0xFFFFFFFF) + bit = 1 << 14 + if (num > result): + result += 1 + return result + + def _fix16_exp(self, x): + x = int(x) + exp_pos_values = [self._f16(2.7182818), self._f16( + 1.1331485), self._f16(1.0157477), self._f16(1.0019550)] + exp_neg_values = [self._f16(0.3678794), self._f16( + 0.8824969), self._f16(0.9844964), self._f16(0.9980488)] + if (x >= self._f16(10.3972)): + return FIX16_MAXIMUM + if (x <= self._f16(-11.7835)): + return 0 + if (x < 0): + x = -x + exp_values = exp_neg_values + else: + exp_values = exp_pos_values + res = FIX16_ONE + arg = FIX16_ONE + for i in range(0, 4): + while (x >= arg): + res = self._fix16_mul(res, exp_values[i]) + x -= arg + arg >>= 3 + return res + + def vocalgorithm_init(self): + self.params.mvoc_index_offset = ( + self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT)) + self.params.mtau_mean_variance_hours = self._f16( + VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS) + self.params.mgating_max_duration_minutes = self._f16( + VOCALGORITHM_GATING_MAX_DURATION_MINUTES) + self.params.msraw_std_initial = self._f16( + VOCALGORITHM_SRAW_STD_INITIAL) + self.params.muptime = self._f16(0.) + self.params.msraw = self._f16(0.) + self.params.mvoc_index = 0 + self._vocalgorithm__init_instances() + + def _vocalgorithm__init_instances(self): + self._vocalgorithm__mean_variance_estimator__init() + self._vocalgorithm__mean_variance_estimator__set_parameters(self._f16( + VOCALGORITHM_SRAW_STD_INITIAL), self.params.mtau_mean_variance_hours, self.params.mgating_max_duration_minutes) + self._vocalgorithm__mox_model__init() + self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( + ), self._vocalgorithm__mean_variance_estimator__get_mean()) + self._vocalgorithm__sigmoid_scaled__init() + self._vocalgorithm__sigmoid_scaled__set_parameters( + self.params.mvoc_index_offset) + self._vocalgorithm__adaptive_lowpass__init() + self._vocalgorithm__adaptive_lowpass__set_parameters() + + def _vocalgorithm_get_states(self, state0, state1): + state0 = self._vocalgorithm__mean_variance_estimator__get_mean() + state1 = _vocalgorithm__mean_variance_estimator__get_std() + return state0, state1 + + def _vocalgorithm_set_states(self, state0, state1): + self._vocalgorithm__mean_variance_estimator__set_states( + params, state0, state1, self._f16(VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA)) + self.params.msraw = state0 + + def _vocalgorithm_set_tuning_parameters(self, voc_index_offset, learning_time_hours, gating_max_duration_minutes, std_initial): + self.params.mvoc_index_offset = self._fix16_from_int(voc_index_offset) + self.params.mtau_mean_variance_hours = self._fix16_from_int( + learning_time_hours) + self.params.mgating_max_duration_minutes = self._fix16_from_int( + gating_max_duration_minutes) + self.params.msraw_std_initial = self._fix16_from_int(std_initial) + self._vocalgorithm__init_instances() + + def vocalgorithm_process(self, sraw): + if ((self.params.muptime <= self._f16(VOCALGORITHM_INITIAL_BLACKOUT))): + self.params.muptime = self.params.muptime + \ + self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + else: + if (((sraw > 0) and (sraw < 65000))): + if ((sraw < 20001)): + sraw = 20001 + elif((sraw > 52767)): + sraw = 52767 + self.params.msraw = self._fix16_from_int((sraw - 20000)) + self.params.mvoc_index = self._vocalgorithm__mox_model__process( + self.params.msraw) + self.params.mvoc_index = self._vocalgorithm__sigmoid_scaled__process( + self.params.mvoc_index) + self.params.mvoc_index = self._vocalgorithm__adaptive_lowpass__process( + self.params.mvoc_index) + if ((self.params.mvoc_index < self._f16(0.5))): + self.params.mvoc_index = self._f16(0.5) + if self.params.msraw > self._f16(0.): + self._vocalgorithm__mean_variance_estimator__process( + self.params.msraw, self.params.mvoc_index) + self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( + ), self._vocalgorithm__mean_variance_estimator__get_mean()) + voc_index = self._fix16_cast_to_int( + (self.params.mvoc_index + self._f16(0.5))) + return voc_index + + def _vocalgorithm__mean_variance_estimator__init(self): + self._vocalgorithm__mean_variance_estimator__set_parameters( + self._f16(0.), self._f16(0.), self._f16(0.)) + self._vocalgorithm__mean_variance_estimator___init_instances() + + def _vocalgorithm__mean_variance_estimator___init_instances(self): + self._vocalgorithm__mean_variance_estimator___sigmoid__init() + + def _vocalgorithm__mean_variance_estimator__set_parameters(self, std_initial, tau_mean_variance_hours, gating_max_duration_minutes): + self.params.m_mean_variance_estimator_gating_max_duration_minutes = gating_max_duration_minutes + self.params.m_mean_variance_estimator_initialized = 0 + self.params.m_mean_variance_estimator_mean = self._f16(0.) + self.params.m_mean_variance_estimator_sraw_offset = self._f16(0.) + self.params.m_mean_variance_estimator_std = std_initial + self.params.m_mean_variance_estimator_gamma = self._fix16_div(self._f16((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * (VOCALGORITHM_SAMPLING_INTERVAL / 3600.)) + ), (tau_mean_variance_hours + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 3600.)))) + self.params.m_mean_variance_estimator_gamma_initial_mean = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) + / (VOCALGORITHM_TAU_INITIAL_MEAN + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_mean_variance_estimator_gamma_initial_variance = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) + / (VOCALGORITHM_TAU_INITIAL_VARIANCE + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_mean_variance_estimator_gamma_mean = self._f16(0.) + self.params.m_mean_variance_estimator__gamma_variance = self._f16(0.) + self.params.m_mean_variance_estimator_uptime_gamma = self._f16(0.) + self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) + self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( + 0.) + + def _vocalgorithm__mean_variance_estimator__set_states(self, mean, std, uptime_gamma): + self.params.m_mean_variance_estimator_mean = mean + self.params.m_mean_variance_estimator_std = std + self.params.m_mean_variance_estimator_uptime_gamma = uptime_gamma + self.params.m_mean_variance_estimator_initialized = true + + def _vocalgorithm__mean_variance_estimator__get_std(self): + return self.params.m_mean_variance_estimator_std + + def _vocalgorithm__mean_variance_estimator__get_mean(self): + return (self.params.m_mean_variance_estimator_mean + self.params.m_mean_variance_estimator_sraw_offset) + + def _vocalgorithm__mean_variance_estimator___calculate_gamma(self, voc_index_from_prior): + uptime_limit = self._f16( + (VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX - VOCALGORITHM_SAMPLING_INTERVAL)) + if self.params.m_mean_variance_estimator_uptime_gamma < uptime_limit: + self.params.m_mean_variance_estimator_uptime_gamma = ( + self.params.m_mean_variance_estimator_uptime_gamma + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) + + if self.params.m_mean_variance_estimator_uptime_gating < uptime_limit: + self.params.m_mean_variance_estimator_uptime_gating = ( + self.params.m_mean_variance_estimator_uptime_gating + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( + VOCALGORITHM_INITI_DURATION_MEAN), self._f16(VOCALGORITHM_INITI_TRANSITION_MEAN)) + sigmoid_gamma_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gamma) + gamma_mean = (self.params.m_mean_variance_estimator_gamma + (self._fix16_mul( + (self.params.m_mean_variance_estimator_gamma_initial_mean - self.params.m_mean_variance_estimator_gamma), sigmoid_gamma_mean))) + gating_threshold_mean = (self._f16(VOCALGORITHM_GATING_THRESHOLD) + + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), + self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( + 1.), gating_threshold_mean, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) + + sigmoid_gating_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + voc_index_from_prior) + self.params.m_mean_variance_estimator_gamma_mean = ( + self._fix16_mul(sigmoid_gating_mean, gamma_mean)) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( + VOCALGORITHM_INITI_DURATION_VARIANCE), self._f16(VOCALGORITHM_INITI_TRANSITION_VARIANCE)) + + sigmoid_gamma_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gamma) + + gamma_variance = (self.params.m_mean_variance_estimator_gamma + + (self._fix16_mul((self.params.m_mean_variance_estimator_gamma_initial_variance + - self.params.m_mean_variance_estimator_gamma), + (sigmoid_gamma_variance - sigmoid_gamma_mean)))) + + gating_threshold_variance = (self._f16(VOCALGORITHM_GATING_THRESHOLD) + + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), + self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( + 1.), gating_threshold_variance, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) + + sigmoid_gating_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( + voc_index_from_prior) + + self.params.m_mean_variance_estimator__gamma_variance = ( + self._fix16_mul(sigmoid_gating_variance, gamma_variance)) + + self.params.m_mean_variance_estimator_gating_duration_minutes = (self.params.m_mean_variance_estimator_gating_duration_minutes + + (self._fix16_mul(self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 60.)), + ((self._fix16_mul((self._f16(1.) - sigmoid_gating_mean), + self._f16((1. + VOCALGORITHM_GATING_MAX_RATIO)))) + - self._f16(VOCALGORITHM_GATING_MAX_RATIO))))) + + if ((self.params.m_mean_variance_estimator_gating_duration_minutes < self._f16(0.))): + self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( + 0.) + + if ((self.params.m_mean_variance_estimator_gating_duration_minutes > self.params.m_mean_variance_estimator_gating_max_duration_minutes)): + self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) + + def _vocalgorithm__mean_variance_estimator__process(self, sraw, voc_index_from_prior): + if ((self.params.m_mean_variance_estimator_initialized == 0)): + self.params.m_mean_variance_estimator_initialized = 1 + self.params.m_mean_variance_estimator_sraw_offset = sraw + self.params.m_mean_variance_estimator_mean = self._f16(0.) + else: + if (((self.params.m_mean_variance_estimator_mean >= self._f16(100.)) or (self.params.m_mean_variance_estimator_mean <= self._f16(-100.)))): + self.params.m_mean_variance_estimator_sraw_offset = ( + self.params.m_mean_variance_estimator_sraw_offset + self.params.m_mean_variance_estimator_mean) + self.params.m_mean_variance_estimator_mean = self._f16(0.) + + sraw = (sraw - self.params.m_mean_variance_estimator_sraw_offset) + self._vocalgorithm__mean_variance_estimator___calculate_gamma( + voc_index_from_prior) + delta_sgp = (self._fix16_div((sraw - self.params.m_mean_variance_estimator_mean), + self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING))) + if ((delta_sgp < self._f16(0.))): + c = (self.params.m_mean_variance_estimator_std - delta_sgp) + else: + c = (self.params.m_mean_variance_estimator_std + delta_sgp) + additional_scaling = self._f16(1.) + if ((c > self._f16(1440.))): + additional_scaling = self._f16(4.) + self.params.m_mean_variance_estimator_std = self._fix16_mul(self._fix16_sqrt((self._fix16_mul(additional_scaling, + (self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) - self.params.m_mean_variance_estimator__gamma_variance)))), + self._fix16_sqrt(((self._fix16_mul(self.params.m_mean_variance_estimator_std, + (self._fix16_div(self.params.m_mean_variance_estimator_std, + (self._fix16_mul(self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), additional_scaling)))))) + + (self._fix16_mul((self._fix16_div((self._fix16_mul(self.params.m_mean_variance_estimator__gamma_variance, delta_sgp)), additional_scaling)), delta_sgp))))) + self.params.m_mean_variance_estimator_mean = (self.params.m_mean_variance_estimator_mean + ( + self._fix16_mul(self.params.m_mean_variance_estimator_gamma_mean, delta_sgp))) + + def _vocalgorithm__mean_variance_estimator___sigmoid__init(self): + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( + self._f16(0.), self._f16(0.), self._f16(0.)) + + def _vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self, L, X0, K): + self.params.m_mean_variance_estimator_sigmoid_l = L + self.params.m_mean_variance_estimator_sigmoid_k = K + self.params.m_mean_variance_estimator_sigmoid_x0 = X0 + + def _vocalgorithm__mean_variance_estimator___sigmoid__process(self, sample): + x = (self._fix16_mul(self.params.m_mean_variance_estimator_sigmoid_k, + (sample - self.params.m_mean_variance_estimator_sigmoid_x0))) + if ((x < self._f16(-50.))): + return self.params.m_mean_variance_estimator_sigmoid_l + elif ((x > self._f16(50.))): + return self._f16(0.) + else: + return (self._fix16_div(self.params.m_mean_variance_estimator_sigmoid_l, (self._f16(1.) + self._fix16_exp(x)))) + + def _vocalgorithm__mox_model__init(self): + self._vocalgorithm__mox_model__set_parameters( + self._f16(1.), self._f16(0.)) + + def _vocalgorithm__mox_model__set_parameters(self, SRAW_STD, SRAW_MEAN): + self.params.m_mox_model_sraw_std = SRAW_STD + self.params.m_mox_model_sraw_mean = SRAW_MEAN + + def _vocalgorithm__mox_model__process(self, sraw): + return (self._fix16_mul((self._fix16_div((sraw - self.params.m_mox_model_sraw_mean), (-(self.params.m_mox_model_sraw_std + self._f16(VOCALGORITHM_SRAW_STD_BONUS))))), self._f16(VOCALGORITHM_VOC_INDEX_GAIN))) + + def _vocalgorithm__sigmoid_scaled__init(self): + self._vocalgorithm__sigmoid_scaled__set_parameters(self._f16(0.)) + + def _vocalgorithm__sigmoid_scaled__set_parameters(self, offset): + self.params.m_sigmoid_scaled_offset = offset + + def _vocalgorithm__sigmoid_scaled__process(self, sample): + x = (self._fix16_mul(self._f16(VOCALGORITHM_SIGMOID_K), + (sample - self._f16(VOCALGORITHM_SIGMOID_X0)))) + if ((x < self._f16(-50.))): + return self._f16(VOCALGORITHM_SIGMOID_L) + elif ((x > self._f16(50.))): + return self._f16(0.) + else: + if ((sample >= self._f16(0.))): + shift = (self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) - (self._fix16_mul( + self._f16(5.), self.params.m_sigmoid_scaled_offset))), self._f16(4.))) + return ((self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) + shift), (self._f16(1.) + self._fix16_exp(x)))) - shift) + else: + return (self._fix16_mul((self._fix16_div(self.params.m_sigmoid_scaled_offset, self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT))), + (self._fix16_div(self._f16(VOCALGORITHM_SIGMOID_L), (self._f16(1.) + self._fix16_exp(x)))))) + + def _vocalgorithm__adaptive_lowpass__init(self): + self._vocalgorithm__adaptive_lowpass__set_parameters() + + def _vocalgorithm__adaptive_lowpass__set_parameters(self): + self.params.m_adaptive_lowpass_a1 = self._f16( + (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_FAST + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_adaptive_lowpass_a2 = self._f16( + (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_SLOW + VOCALGORITHM_SAMPLING_INTERVAL))) + self.params.m_adaptive_lowpass_initialized = 0 + + def _vocalgorithm__adaptive_lowpass__process(self, sample): + if ((self.params.m_adaptive_lowpass_initialized == 0)): + self.params.m_adaptive_lowpass_x1 = sample + self.params.m_adaptive_lowpass_x2 = sample + self.params.m_adaptive_lowpass_x3 = sample + self.params.m_adaptive_lowpass_initialized = 1 + self.params.m_adaptive_lowpass_x1 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a1), + self.params.m_adaptive_lowpass_x1)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a1, sample))) + + self.params.m_adaptive_lowpass_x2 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a2), + self.params.m_adaptive_lowpass_x2)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a2, sample))) + + abs_delta = (self.params.m_adaptive_lowpass_x1 - + self.params.m_adaptive_lowpass_x2) + + if ((abs_delta < self._f16(0.))): + abs_delta = (-abs_delta) + F1 = self._fix16_exp( + (self._fix16_mul(self._f16(VOCALGORITHM_LP_ALPHA), abs_delta))) + tau_a = ((self._fix16_mul(self._f16((VOCALGORITHM_LP_TAU_SLOW - + VOCALGORITHM_LP_TAU_FAST)), F1)) + self._f16(VOCALGORITHM_LP_TAU_FAST)) + a3 = (self._fix16_div(self._f16(VOCALGORITHM_SAMPLING_INTERVAL), + (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a))) + self.params.m_adaptive_lowpass_x3 = ((self._fix16_mul((self._f16( + 1.) - a3), self.params.m_adaptive_lowpass_x3)) + (self._fix16_mul(a3, sample))) + return self.params.m_adaptive_lowpass_x3 From e5af7e2771c3d25c75c3a73c6ee203a1f646d7ed Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Sat, 28 Aug 2021 06:14:37 -0700 Subject: [PATCH 4/9] added index --- README.rst | 38 ++- adafruit_sgp40/__init__.py | 514 +----------------------------------- examples/sgp40_indextest.py | 8 +- 3 files changed, 40 insertions(+), 520 deletions(-) diff --git a/README.rst b/README.rst index cae2fd2..cc76fd3 100644 --- a/README.rst +++ b/README.rst @@ -1,12 +1,23 @@ Introduction ============ -This fork adds DFRobots VOC Index algorithm to adafruits CircuitPython SGP40 library. +.. image:: https://readthedocs.org/projects/adafruit-circuitpython-sgp40/badge/?version=latest + :target: https://circuitpython.readthedocs.io/projects/sgp40/en/latest/ + :alt: Documentation Status -CircuitPython library for the Adafruit SGP40 Air Quality Sensor / VOC Index Sensor Breakouts. +.. image:: https://img.shields.io/discord/327254708534116352.svg + :target: https://adafru.it/discord + :alt: Discord -* `Original Library `_ -* `DFRobot Algorithm `_ +.. image:: https://github.com/adafruit/Adafruit_CircuitPython_SGP40/workflows/Build%20CI/badge.svg + :target: https://github.com/adafruit/Adafruit_CircuitPython_SGP40/actions + :alt: Build Status + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code Style: Black + +CircuitPython library for the Adafruit SGP40 Air Quality Sensor / VOC Index Sensor Breakouts Dependencies @@ -17,7 +28,6 @@ This driver depends on: * `Bus Device `_ * `Register `_ - Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading `the Adafruit library and driver bundle `_. @@ -50,6 +60,20 @@ To install in a virtual environment in your current project: Usage Example ============= +.. code-block:: python3 + + import time + import board + import adafruit_sgp40 + + i2c = board.I2C() # uses board.SCL and board.SDA + sgp = adafruit_sgp40(i2c) + + while True: + print("Measurement: ", sgp.raw) + print("") + sleep(1) + For humidity compensated raw gas and voc index readings, we'll need a secondary sensor such as the bme280 .. code-block:: python3 @@ -67,10 +91,10 @@ For humidity compensated raw gas and voc index readings, we'll need a secondary temperature = bme280.temperature humidity = bme280.relative_humidity - # Compensated raw gas reading + # For compensated raw gas readings compensated_raw_gas = sgp.measure_index(temperature = temperature, relative_humidity = humidity) - # Compensated voc index reading + # For Compensated voc index readings voc_index = sgp.measure_raw(temperature = temperature, relative_humidity = humidity) print(compensated_raw_gas) diff --git a/adafruit_sgp40/__init__.py b/adafruit_sgp40/__init__.py index d8df2cb..2ff3920 100644 --- a/adafruit_sgp40/__init__.py +++ b/adafruit_sgp40/__init__.py @@ -32,517 +32,7 @@ from time import sleep from struct import unpack_from import adafruit_bus_device.i2c_device as i2c_device - -VOCALGORITHM_SAMPLING_INTERVAL = (1.) -VOCALGORITHM_INITIAL_BLACKOUT = (45.) -VOCALGORITHM_VOC_INDEX_GAIN = (230.) -VOCALGORITHM_SRAW_STD_INITIAL = (50.) -VOCALGORITHM_SRAW_STD_BONUS = (220.) -VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = (12.) -VOCALGORITHM_TAU_INITIAL_MEAN = (20.) -VOCALGORITHM_INITI_DURATION_MEAN = (3600. * 0.75) -VOCALGORITHM_INITI_TRANSITION_MEAN = (0.01) -VOCALGORITHM_TAU_INITIAL_VARIANCE = (2500.) -VOCALGORITHM_INITI_DURATION_VARIANCE = ((3600. * 1.45)) -VOCALGORITHM_INITI_TRANSITION_VARIANCE = (0.01) -VOCALGORITHM_GATING_THRESHOLD = (340.) -VOCALGORITHM_GATING_THRESHOLD_INITIAL = (510.) -VOCALGORITHM_GATING_THRESHOLD_TRANSITION = (0.09) -VOCALGORITHM_GATING_MAX_DURATION_MINUTES = ((60. * 3.)) -VOCALGORITHM_GATING_MAX_RATIO = (0.3) -VOCALGORITHM_SIGMOID_L = (500.) -VOCALGORITHM_SIGMOID_K = (-0.0065) -VOCALGORITHM_SIGMOID_X0 = (213.) -VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = (100.) -VOCALGORITHM_LP_TAU_FAST = (20.0) -VOCALGORITHM_LP_TAU_SLOW = (500.0) -VOCALGORITHM_LP_ALPHA = (-0.2) -VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = ((3. * 3600.)) -VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = (64.) -VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = (32767.) -FIX16_MAXIMUM = 0x7FFFFFFF -FIX16_MINIMUM = 0x80000000 -FIX16_OVERFLOW = 0x80000000 -FIX16_ONE = 0x00010000 - - -class DFRobot_vocalgorithmParams: - def __init__(self): - self.mvoc_index_offset = 0 - self.mtau_mean_variance_hours = 0 - self.mgating_max_duration_minutes = 0 - self.msraw_std_initial = 0 - self.muptime = 0 - self.msraw = 0 - self.mvoc_index = 0 - self.m_mean_variance_estimator_gating_max_duration_minutes = 0 - self.m_mean_variance_estimator_initialized = 0 - self.m_mean_variance_estimator_mean = 0 - self.m_mean_variance_estimator_sraw_offset = 0 - self.m_mean_variance_estimator_std = 0 - self.m_mean_variance_estimator_gamma = 0 - self.m_mean_variance_estimator_gamma_initial_mean = 0 - self.m_mean_variance_estimator_gamma_initial_variance = 0 - self.m_mean_variance_estimator_gamma_mean = 0 - self.m_mean_variance_estimator__gamma_variance = 0 - self.m_mean_variance_estimator_uptime_gamma = 0 - self.m_mean_variance_estimator_uptime_gating = 0 - self.m_mean_variance_estimator_gating_duration_minutes = 0 - self.m_mean_variance_estimator_sigmoid_l = 0 - self.m_mean_variance_estimator_sigmoid_k = 0 - self.m_mean_variance_estimator_sigmoid_x0 = 0 - self.m_mox_model_sraw_mean = 0 - self.m_sigmoid_scaled_offset = 0 - self.m_adaptive_lowpass_a1 = 0 - self.m_adaptive_lowpass_a2 = 0 - self.m_adaptive_lowpass_initialized = 0 - self.m_adaptive_lowpass_x1 = 0 - self.m_adaptive_lowpass_x2 = 0 - self.m_adaptive_lowpass_x3 = 0 - - -class DFRobot_VOCAlgorithm: - - def __init__(self): - self.params = DFRobot_vocalgorithmParams() - - def _f16(self, x): - if x >= 0: - return int((x)*65536.0 + 0.5) - else: - return int((x)*65536.0 - 0.5) - - def _fix16_from_int(self, a): - return int(a * FIX16_ONE) - - def _fix16_cast_to_int(self, a): - return int(a) >> 16 - - def _fix16_mul(self, inarg0, inarg1): - inarg0 = int(inarg0) - inarg1 = int(inarg1) - A = (inarg0 >> 16) - if inarg0 < 0: - B = (inarg0 & 0xFFFFFFFF) & 0xFFFF - else: - B = inarg0 & 0xFFFF - C = (inarg1 >> 16) - if inarg1 < 0: - D = (inarg1 & 0xFFFFFFFF) & 0xFFFF - else: - D = inarg1 & 0xFFFF - AC = (A * C) - AD_CB = (A * D + C * B) - BD = (B * D) - product_hi = (AC + (AD_CB >> 16)) - ad_cb_temp = ((AD_CB) << 16) & 0xFFFFFFFF - product_lo = ((BD + ad_cb_temp)) & 0xFFFFFFFF - if product_lo < BD: - product_hi = product_hi+1 - if ((product_hi >> 31) != (product_hi >> 15)): - return FIX16_OVERFLOW - product_lo_tmp = product_lo & 0xFFFFFFFF - product_lo = (product_lo - 0x8000) & 0xFFFFFFFF - product_lo = ( - product_lo-((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF - if product_lo > product_lo_tmp: - product_hi = product_hi-1 - result = (product_hi << 16) | (product_lo >> 16) - result += 1 - return result - - def _fix16_div(self, a, b): - a = int(a) - b = int(b) - if b == 0: - return FIX16_MINIMUM - if a >= 0: - remainder = a - else: - remainder = (a*(-1)) & 0xFFFFFFFF - if b >= 0: - divider = b - else: - divider = (b*(-1)) & 0xFFFFFFFF - quotient = 0 - bit = 0x10000 - while (divider < remainder): - divider = divider << 1 - bit <<= 1 - if not bit: - return FIX16_OVERFLOW - if (divider & 0x80000000): - if (remainder >= divider): - quotient |= bit - remainder -= divider - divider >>= 1 - bit >>= 1 - while bit and remainder: - if (remainder >= divider): - quotient |= bit - remainder -= divider - remainder <<= 1 - bit >>= 1 - if (remainder >= divider): - quotient += 1 - result = quotient - if ((a ^ b) & 0x80000000): - if (result == FIX16_MINIMUM): - return FIX16_OVERFLOW - result = -result - return result - - def _fix16_sqrt(self, x): - x = int(x) - num = x & 0xFFFFFFFF - result = 0 - bit = 1 << 30 - while (bit > num): - bit >>= 2 - for n in range(0, 2): - while (bit): - if (num >= result + bit): - num = num-(result + bit) & 0xFFFFFFFF - result = (result >> 1) + bit - else: - result = (result >> 1) - bit >>= 2 - if n == 0: - if num > 65535: - num = (num - result) & 0xFFFFFFFF - num = ((num << 16) - 0x8000) & 0xFFFFFFFF - result = ((result << 16) + 0x8000) & 0xFFFFFFFF - else: - num = ((num << 16) & 0xFFFFFFFF) - result = ((result << 16) & 0xFFFFFFFF) - bit = 1 << 14 - if (num > result): - result += 1 - return result - - def _fix16_exp(self, x): - x = int(x) - exp_pos_values = [self._f16(2.7182818), self._f16( - 1.1331485), self._f16(1.0157477), self._f16(1.0019550)] - exp_neg_values = [self._f16(0.3678794), self._f16( - 0.8824969), self._f16(0.9844964), self._f16(0.9980488)] - if (x >= self._f16(10.3972)): - return FIX16_MAXIMUM - if (x <= self._f16(-11.7835)): - return 0 - if (x < 0): - x = -x - exp_values = exp_neg_values - else: - exp_values = exp_pos_values - res = FIX16_ONE - arg = FIX16_ONE - for i in range(0, 4): - while (x >= arg): - res = self._fix16_mul(res, exp_values[i]) - x -= arg - arg >>= 3 - return res - - def vocalgorithm_init(self): - self.params.mvoc_index_offset = ( - self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT)) - self.params.mtau_mean_variance_hours = self._f16( - VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS) - self.params.mgating_max_duration_minutes = self._f16( - VOCALGORITHM_GATING_MAX_DURATION_MINUTES) - self.params.msraw_std_initial = self._f16( - VOCALGORITHM_SRAW_STD_INITIAL) - self.params.muptime = self._f16(0.) - self.params.msraw = self._f16(0.) - self.params.mvoc_index = 0 - self._vocalgorithm__init_instances() - - def _vocalgorithm__init_instances(self): - self._vocalgorithm__mean_variance_estimator__init() - self._vocalgorithm__mean_variance_estimator__set_parameters(self._f16( - VOCALGORITHM_SRAW_STD_INITIAL), self.params.mtau_mean_variance_hours, self.params.mgating_max_duration_minutes) - self._vocalgorithm__mox_model__init() - self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( - ), self._vocalgorithm__mean_variance_estimator__get_mean()) - self._vocalgorithm__sigmoid_scaled__init() - self._vocalgorithm__sigmoid_scaled__set_parameters( - self.params.mvoc_index_offset) - self._vocalgorithm__adaptive_lowpass__init() - self._vocalgorithm__adaptive_lowpass__set_parameters() - - def _vocalgorithm_get_states(self, state0, state1): - state0 = self._vocalgorithm__mean_variance_estimator__get_mean() - state1 = _vocalgorithm__mean_variance_estimator__get_std() - return state0, state1 - - def _vocalgorithm_set_states(self, state0, state1): - self._vocalgorithm__mean_variance_estimator__set_states( - params, state0, state1, self._f16(VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA)) - self.params.msraw = state0 - - def _vocalgorithm_set_tuning_parameters(self, voc_index_offset, learning_time_hours, gating_max_duration_minutes, std_initial): - self.params.mvoc_index_offset = self._fix16_from_int(voc_index_offset) - self.params.mtau_mean_variance_hours = self._fix16_from_int( - learning_time_hours) - self.params.mgating_max_duration_minutes = self._fix16_from_int( - gating_max_duration_minutes) - self.params.msraw_std_initial = self._fix16_from_int(std_initial) - self._vocalgorithm__init_instances() - - def vocalgorithm_process(self, sraw): - if ((self.params.muptime <= self._f16(VOCALGORITHM_INITIAL_BLACKOUT))): - self.params.muptime = self.params.muptime + \ - self._f16(VOCALGORITHM_SAMPLING_INTERVAL) - else: - if (((sraw > 0) and (sraw < 65000))): - if ((sraw < 20001)): - sraw = 20001 - elif((sraw > 52767)): - sraw = 52767 - self.params.msraw = self._fix16_from_int((sraw - 20000)) - self.params.mvoc_index = self._vocalgorithm__mox_model__process( - self.params.msraw) - self.params.mvoc_index = self._vocalgorithm__sigmoid_scaled__process( - self.params.mvoc_index) - self.params.mvoc_index = self._vocalgorithm__adaptive_lowpass__process( - self.params.mvoc_index) - if ((self.params.mvoc_index < self._f16(0.5))): - self.params.mvoc_index = self._f16(0.5) - if self.params.msraw > self._f16(0.): - self._vocalgorithm__mean_variance_estimator__process( - self.params.msraw, self.params.mvoc_index) - self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( - ), self._vocalgorithm__mean_variance_estimator__get_mean()) - voc_index = self._fix16_cast_to_int( - (self.params.mvoc_index + self._f16(0.5))) - return voc_index - - def _vocalgorithm__mean_variance_estimator__init(self): - self._vocalgorithm__mean_variance_estimator__set_parameters( - self._f16(0.), self._f16(0.), self._f16(0.)) - self._vocalgorithm__mean_variance_estimator___init_instances() - - def _vocalgorithm__mean_variance_estimator___init_instances(self): - self._vocalgorithm__mean_variance_estimator___sigmoid__init() - - def _vocalgorithm__mean_variance_estimator__set_parameters(self, std_initial, tau_mean_variance_hours, gating_max_duration_minutes): - self.params.m_mean_variance_estimator_gating_max_duration_minutes = gating_max_duration_minutes - self.params.m_mean_variance_estimator_initialized = 0 - self.params.m_mean_variance_estimator_mean = self._f16(0.) - self.params.m_mean_variance_estimator_sraw_offset = self._f16(0.) - self.params.m_mean_variance_estimator_std = std_initial - self.params.m_mean_variance_estimator_gamma = self._fix16_div(self._f16((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * (VOCALGORITHM_SAMPLING_INTERVAL / 3600.)) - ), (tau_mean_variance_hours + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 3600.)))) - self.params.m_mean_variance_estimator_gamma_initial_mean = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) - / (VOCALGORITHM_TAU_INITIAL_MEAN + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_mean_variance_estimator_gamma_initial_variance = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) - / (VOCALGORITHM_TAU_INITIAL_VARIANCE + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_mean_variance_estimator_gamma_mean = self._f16(0.) - self.params.m_mean_variance_estimator__gamma_variance = self._f16(0.) - self.params.m_mean_variance_estimator_uptime_gamma = self._f16(0.) - self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) - self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( - 0.) - - def _vocalgorithm__mean_variance_estimator__set_states(self, mean, std, uptime_gamma): - self.params.m_mean_variance_estimator_mean = mean - self.params.m_mean_variance_estimator_std = std - self.params.m_mean_variance_estimator_uptime_gamma = uptime_gamma - self.params.m_mean_variance_estimator_initialized = true - - def _vocalgorithm__mean_variance_estimator__get_std(self): - return self.params.m_mean_variance_estimator_std - - def _vocalgorithm__mean_variance_estimator__get_mean(self): - return (self.params.m_mean_variance_estimator_mean + self.params.m_mean_variance_estimator_sraw_offset) - - def _vocalgorithm__mean_variance_estimator___calculate_gamma(self, voc_index_from_prior): - uptime_limit = self._f16( - (VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX - VOCALGORITHM_SAMPLING_INTERVAL)) - if self.params.m_mean_variance_estimator_uptime_gamma < uptime_limit: - self.params.m_mean_variance_estimator_uptime_gamma = ( - self.params.m_mean_variance_estimator_uptime_gamma + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) - - if self.params.m_mean_variance_estimator_uptime_gating < uptime_limit: - self.params.m_mean_variance_estimator_uptime_gating = ( - self.params.m_mean_variance_estimator_uptime_gating + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( - VOCALGORITHM_INITI_DURATION_MEAN), self._f16(VOCALGORITHM_INITI_TRANSITION_MEAN)) - sigmoid_gamma_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - self.params.m_mean_variance_estimator_uptime_gamma) - gamma_mean = (self.params.m_mean_variance_estimator_gamma + (self._fix16_mul( - (self.params.m_mean_variance_estimator_gamma_initial_mean - self.params.m_mean_variance_estimator_gamma), sigmoid_gamma_mean))) - gating_threshold_mean = (self._f16(VOCALGORITHM_GATING_THRESHOLD) - + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), - self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( - 1.), gating_threshold_mean, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) - - sigmoid_gating_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - voc_index_from_prior) - self.params.m_mean_variance_estimator_gamma_mean = ( - self._fix16_mul(sigmoid_gating_mean, gamma_mean)) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( - VOCALGORITHM_INITI_DURATION_VARIANCE), self._f16(VOCALGORITHM_INITI_TRANSITION_VARIANCE)) - - sigmoid_gamma_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - self.params.m_mean_variance_estimator_uptime_gamma) - - gamma_variance = (self.params.m_mean_variance_estimator_gamma + - (self._fix16_mul((self.params.m_mean_variance_estimator_gamma_initial_variance - - self.params.m_mean_variance_estimator_gamma), - (sigmoid_gamma_variance - sigmoid_gamma_mean)))) - - gating_threshold_variance = (self._f16(VOCALGORITHM_GATING_THRESHOLD) - + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), - self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( - 1.), gating_threshold_variance, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) - - sigmoid_gating_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - voc_index_from_prior) - - self.params.m_mean_variance_estimator__gamma_variance = ( - self._fix16_mul(sigmoid_gating_variance, gamma_variance)) - - self.params.m_mean_variance_estimator_gating_duration_minutes = (self.params.m_mean_variance_estimator_gating_duration_minutes - + (self._fix16_mul(self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 60.)), - ((self._fix16_mul((self._f16(1.) - sigmoid_gating_mean), - self._f16((1. + VOCALGORITHM_GATING_MAX_RATIO)))) - - self._f16(VOCALGORITHM_GATING_MAX_RATIO))))) - - if ((self.params.m_mean_variance_estimator_gating_duration_minutes < self._f16(0.))): - self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( - 0.) - - if ((self.params.m_mean_variance_estimator_gating_duration_minutes > self.params.m_mean_variance_estimator_gating_max_duration_minutes)): - self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) - - def _vocalgorithm__mean_variance_estimator__process(self, sraw, voc_index_from_prior): - if ((self.params.m_mean_variance_estimator_initialized == 0)): - self.params.m_mean_variance_estimator_initialized = 1 - self.params.m_mean_variance_estimator_sraw_offset = sraw - self.params.m_mean_variance_estimator_mean = self._f16(0.) - else: - if (((self.params.m_mean_variance_estimator_mean >= self._f16(100.)) or (self.params.m_mean_variance_estimator_mean <= self._f16(-100.)))): - self.params.m_mean_variance_estimator_sraw_offset = ( - self.params.m_mean_variance_estimator_sraw_offset + self.params.m_mean_variance_estimator_mean) - self.params.m_mean_variance_estimator_mean = self._f16(0.) - - sraw = (sraw - self.params.m_mean_variance_estimator_sraw_offset) - self._vocalgorithm__mean_variance_estimator___calculate_gamma( - voc_index_from_prior) - delta_sgp = (self._fix16_div((sraw - self.params.m_mean_variance_estimator_mean), - self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING))) - if ((delta_sgp < self._f16(0.))): - c = (self.params.m_mean_variance_estimator_std - delta_sgp) - else: - c = (self.params.m_mean_variance_estimator_std + delta_sgp) - additional_scaling = self._f16(1.) - if ((c > self._f16(1440.))): - additional_scaling = self._f16(4.) - self.params.m_mean_variance_estimator_std = self._fix16_mul(self._fix16_sqrt((self._fix16_mul(additional_scaling, - (self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) - self.params.m_mean_variance_estimator__gamma_variance)))), - self._fix16_sqrt(((self._fix16_mul(self.params.m_mean_variance_estimator_std, - (self._fix16_div(self.params.m_mean_variance_estimator_std, - (self._fix16_mul(self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), additional_scaling)))))) - + (self._fix16_mul((self._fix16_div((self._fix16_mul(self.params.m_mean_variance_estimator__gamma_variance, delta_sgp)), additional_scaling)), delta_sgp))))) - self.params.m_mean_variance_estimator_mean = (self.params.m_mean_variance_estimator_mean + ( - self._fix16_mul(self.params.m_mean_variance_estimator_gamma_mean, delta_sgp))) - - def _vocalgorithm__mean_variance_estimator___sigmoid__init(self): - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( - self._f16(0.), self._f16(0.), self._f16(0.)) - - def _vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self, L, X0, K): - self.params.m_mean_variance_estimator_sigmoid_l = L - self.params.m_mean_variance_estimator_sigmoid_k = K - self.params.m_mean_variance_estimator_sigmoid_x0 = X0 - - def _vocalgorithm__mean_variance_estimator___sigmoid__process(self, sample): - x = (self._fix16_mul(self.params.m_mean_variance_estimator_sigmoid_k, - (sample - self.params.m_mean_variance_estimator_sigmoid_x0))) - if ((x < self._f16(-50.))): - return self.params.m_mean_variance_estimator_sigmoid_l - elif ((x > self._f16(50.))): - return self._f16(0.) - else: - return (self._fix16_div(self.params.m_mean_variance_estimator_sigmoid_l, (self._f16(1.) + self._fix16_exp(x)))) - - def _vocalgorithm__mox_model__init(self): - self._vocalgorithm__mox_model__set_parameters( - self._f16(1.), self._f16(0.)) - - def _vocalgorithm__mox_model__set_parameters(self, SRAW_STD, SRAW_MEAN): - self.params.m_mox_model_sraw_std = SRAW_STD - self.params.m_mox_model_sraw_mean = SRAW_MEAN - - def _vocalgorithm__mox_model__process(self, sraw): - return (self._fix16_mul((self._fix16_div((sraw - self.params.m_mox_model_sraw_mean), (-(self.params.m_mox_model_sraw_std + self._f16(VOCALGORITHM_SRAW_STD_BONUS))))), self._f16(VOCALGORITHM_VOC_INDEX_GAIN))) - - def _vocalgorithm__sigmoid_scaled__init(self): - self._vocalgorithm__sigmoid_scaled__set_parameters(self._f16(0.)) - - def _vocalgorithm__sigmoid_scaled__set_parameters(self, offset): - self.params.m_sigmoid_scaled_offset = offset - - def _vocalgorithm__sigmoid_scaled__process(self, sample): - x = (self._fix16_mul(self._f16(VOCALGORITHM_SIGMOID_K), - (sample - self._f16(VOCALGORITHM_SIGMOID_X0)))) - if ((x < self._f16(-50.))): - return self._f16(VOCALGORITHM_SIGMOID_L) - elif ((x > self._f16(50.))): - return self._f16(0.) - else: - if ((sample >= self._f16(0.))): - shift = (self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) - (self._fix16_mul( - self._f16(5.), self.params.m_sigmoid_scaled_offset))), self._f16(4.))) - return ((self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) + shift), (self._f16(1.) + self._fix16_exp(x)))) - shift) - else: - return (self._fix16_mul((self._fix16_div(self.params.m_sigmoid_scaled_offset, self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT))), - (self._fix16_div(self._f16(VOCALGORITHM_SIGMOID_L), (self._f16(1.) + self._fix16_exp(x)))))) - - def _vocalgorithm__adaptive_lowpass__init(self): - self._vocalgorithm__adaptive_lowpass__set_parameters() - - def _vocalgorithm__adaptive_lowpass__set_parameters(self): - self.params.m_adaptive_lowpass_a1 = self._f16( - (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_FAST + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_adaptive_lowpass_a2 = self._f16( - (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_SLOW + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_adaptive_lowpass_initialized = 0 - - def _vocalgorithm__adaptive_lowpass__process(self, sample): - if ((self.params.m_adaptive_lowpass_initialized == 0)): - self.params.m_adaptive_lowpass_x1 = sample - self.params.m_adaptive_lowpass_x2 = sample - self.params.m_adaptive_lowpass_x3 = sample - self.params.m_adaptive_lowpass_initialized = 1 - self.params.m_adaptive_lowpass_x1 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a1), - self.params.m_adaptive_lowpass_x1)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a1, sample))) - - self.params.m_adaptive_lowpass_x2 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a2), - self.params.m_adaptive_lowpass_x2)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a2, sample))) - - abs_delta = (self.params.m_adaptive_lowpass_x1 - - self.params.m_adaptive_lowpass_x2) - - if ((abs_delta < self._f16(0.))): - abs_delta = (-abs_delta) - F1 = self._fix16_exp( - (self._fix16_mul(self._f16(VOCALGORITHM_LP_ALPHA), abs_delta))) - tau_a = ((self._fix16_mul(self._f16((VOCALGORITHM_LP_TAU_SLOW - - VOCALGORITHM_LP_TAU_FAST)), F1)) + self._f16(VOCALGORITHM_LP_TAU_FAST)) - a3 = (self._fix16_div(self._f16(VOCALGORITHM_SAMPLING_INTERVAL), - (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a))) - self.params.m_adaptive_lowpass_x3 = ((self._fix16_mul((self._f16( - 1.) - a3), self.params.m_adaptive_lowpass_x3)) + (self._fix16_mul(a3, sample))) - return self.params.m_adaptive_lowpass_x3 - +from adafruit_sgp40.voc_algorithm import VOCAlgorithm __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SGP40.git" @@ -622,7 +112,7 @@ def __init__(self, i2c, address=0x59): self.i2c_device = i2c_device.I2CDevice(i2c, address) self._command_buffer = bytearray(2) self._measure_command = _READ_CMD - self._voc_algorithm = DFRobot_VOCAlgorithm() + self._voc_algorithm = VOCAlgorithm() self.initialize() diff --git a/examples/sgp40_indextest.py b/examples/sgp40_indextest.py index 28b5fc9..97cbc09 100644 --- a/examples/sgp40_indextest.py +++ b/examples/sgp40_indextest.py @@ -14,10 +14,16 @@ temperature = bme280.temperature humidity = bme280.relative_humidity - # Compensated voc index reading + # For compensated raw gas readings + compensated_raw_gas = sgp.measure_index( + temperature=temperature, relative_humidity=humidity) + + # For Compensated voc index readings + # It may take several minutes for the VOC index to start changing as it calibrates the baseline readings. voc_index = sgp.measure_raw( temperature=temperature, relative_humidity=humidity) + print(compensated_raw_gas) print(voc_index) print("") time.sleep(1) From 69b093cfe1d065fa186d16ee603191a927672f8f Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Sat, 28 Aug 2021 07:40:53 -0700 Subject: [PATCH 5/9] fixed built tests --- adafruit_sgp40/__init__.py | 12 +- adafruit_sgp40/voc_algorithm.py | 775 ++++++++++++++++++++++---------- examples/sgp40_indextest.py | 11 +- 3 files changed, 550 insertions(+), 248 deletions(-) diff --git a/adafruit_sgp40/__init__.py b/adafruit_sgp40/__init__.py index 2ff3920..615b8ba 100644 --- a/adafruit_sgp40/__init__.py +++ b/adafruit_sgp40/__init__.py @@ -31,7 +31,7 @@ """ from time import sleep from struct import unpack_from -import adafruit_bus_device.i2c_device as i2c_device +from adafruit_bus_device import i2c_device from adafruit_sgp40.voc_algorithm import VOCAlgorithm __version__ = "0.0.0-auto.0" @@ -230,12 +230,13 @@ def measure_raw(self, temperature=25, relative_humidity=50): return self.raw def measure_index(self, temperature=25, relative_humidity=50): - """ Measure VOC index after humidity compensation + """Measure VOC index after humidity compensation :param float temperature: The temperature in degrees Celsius, defaults to :const:`25` :param float relative_humidity: The relative humidity in percentage, defaults to :const:`50` - :note VOC index can indicate the quality of the air directly. The larger the value, the worse the air quality. + :note VOC index can indicate the quality of the air directly. + The larger the value, the worse the air quality. :note 0-100,no need to ventilate, purify :note 100-200,no need to ventilate, purify :note 200-400,ventilate, purify @@ -245,9 +246,8 @@ def measure_index(self, temperature=25, relative_humidity=50): raw = self.measure_raw(temperature, relative_humidity) if raw < 0: return -1 - else: - vocIndex = self._voc_algorithm.vocalgorithm_process(raw) - return vocIndex + voc_index = self._voc_algorithm.vocalgorithm_process(raw) + return voc_index def _read_word_from_command( self, diff --git a/adafruit_sgp40/voc_algorithm.py b/adafruit_sgp40/voc_algorithm.py index 0242e6c..11d8068 100644 --- a/adafruit_sgp40/voc_algorithm.py +++ b/adafruit_sgp40/voc_algorithm.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com) # # SPDX-License-Identifier: MIT -""" +""" `voc_algorithm` ================================================================================ @@ -12,33 +12,33 @@ """ -VOCALGORITHM_SAMPLING_INTERVAL = (1.) -VOCALGORITHM_INITIAL_BLACKOUT = (45.) -VOCALGORITHM_VOC_INDEX_GAIN = (230.) -VOCALGORITHM_SRAW_STD_INITIAL = (50.) -VOCALGORITHM_SRAW_STD_BONUS = (220.) -VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = (12.) -VOCALGORITHM_TAU_INITIAL_MEAN = (20.) -VOCALGORITHM_INITI_DURATION_MEAN = (3600. * 0.75) -VOCALGORITHM_INITI_TRANSITION_MEAN = (0.01) -VOCALGORITHM_TAU_INITIAL_VARIANCE = (2500.) -VOCALGORITHM_INITI_DURATION_VARIANCE = ((3600. * 1.45)) -VOCALGORITHM_INITI_TRANSITION_VARIANCE = (0.01) -VOCALGORITHM_GATING_THRESHOLD = (340.) -VOCALGORITHM_GATING_THRESHOLD_INITIAL = (510.) -VOCALGORITHM_GATING_THRESHOLD_TRANSITION = (0.09) -VOCALGORITHM_GATING_MAX_DURATION_MINUTES = ((60. * 3.)) -VOCALGORITHM_GATING_MAX_RATIO = (0.3) -VOCALGORITHM_SIGMOID_L = (500.) -VOCALGORITHM_SIGMOID_K = (-0.0065) -VOCALGORITHM_SIGMOID_X0 = (213.) -VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = (100.) -VOCALGORITHM_LP_TAU_FAST = (20.0) -VOCALGORITHM_LP_TAU_SLOW = (500.0) -VOCALGORITHM_LP_ALPHA = (-0.2) -VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = ((3. * 3600.)) -VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = (64.) -VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = (32767.) +VOCALGORITHM_SAMPLING_INTERVAL = 1.0 +VOCALGORITHM_INITIAL_BLACKOUT = 45.0 +VOCALGORITHM_VOC_INDEX_GAIN = 230.0 +VOCALGORITHM_SRAW_STD_INITIAL = 50.0 +VOCALGORITHM_SRAW_STD_BONUS = 220.0 +VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = 12.0 +VOCALGORITHM_TAU_INITIAL_MEAN = 20.0 +VOCALGORITHM_INITI_DURATION_MEAN = 3600.0 * 0.75 +VOCALGORITHM_INITI_TRANSITION_MEAN = 0.01 +VOCALGORITHM_TAU_INITIAL_VARIANCE = 2500.0 +VOCALGORITHM_INITI_DURATION_VARIANCE = 3600.0 * 1.45 +VOCALGORITHM_INITI_TRANSITION_VARIANCE = 0.01 +VOCALGORITHM_GATING_THRESHOLD = 340.0 +VOCALGORITHM_GATING_THRESHOLD_INITIAL = 510.0 +VOCALGORITHM_GATING_THRESHOLD_TRANSITION = 0.09 +VOCALGORITHM_GATING_MAX_DURATION_MINUTES = 60.0 * 3.0 +VOCALGORITHM_GATING_MAX_RATIO = 0.3 +VOCALGORITHM_SIGMOID_L = 500.0 +VOCALGORITHM_SIGMOID_K = -0.0065 +VOCALGORITHM_SIGMOID_X0 = 213.0 +VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = 100.0 +VOCALGORITHM_LP_TAU_FAST = 20.0 +VOCALGORITHM_LP_TAU_SLOW = 500.0 +VOCALGORITHM_LP_ALPHA = -0.2 +VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = 3.0 * 3600.0 +VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = 64.0 +VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = 32767.0 FIX16_MAXIMUM = 0x7FFFFFFF FIX16_MINIMUM = 0x80000000 FIX16_OVERFLOW = 0x80000000 @@ -46,6 +46,12 @@ class DFRobot_vocalgorithmParams: + """Class for voc index algorithm + """ + + # pylint: disable=all + # Complex math conversion from C + def __init__(self): self.mvoc_index_offset = 0 self.mtau_mean_variance_hours = 0 @@ -81,15 +87,14 @@ def __init__(self): class VOCAlgorithm: - def __init__(self): self.params = DFRobot_vocalgorithmParams() def _f16(self, x): if x >= 0: - return int((x)*65536.0 + 0.5) + return int((x) * 65536.0 + 0.5) else: - return int((x)*65536.0 - 0.5) + return int((x) * 65536.0 - 0.5) def _fix16_from_int(self, a): return int(a * FIX16_ONE) @@ -100,32 +105,32 @@ def _fix16_cast_to_int(self, a): def _fix16_mul(self, inarg0, inarg1): inarg0 = int(inarg0) inarg1 = int(inarg1) - A = (inarg0 >> 16) + A = inarg0 >> 16 if inarg0 < 0: B = (inarg0 & 0xFFFFFFFF) & 0xFFFF else: B = inarg0 & 0xFFFF - C = (inarg1 >> 16) + C = inarg1 >> 16 if inarg1 < 0: D = (inarg1 & 0xFFFFFFFF) & 0xFFFF else: D = inarg1 & 0xFFFF - AC = (A * C) - AD_CB = (A * D + C * B) - BD = (B * D) - product_hi = (AC + (AD_CB >> 16)) + AC = A * C + AD_CB = A * D + C * B + BD = B * D + product_hi = AC + (AD_CB >> 16) ad_cb_temp = ((AD_CB) << 16) & 0xFFFFFFFF product_lo = ((BD + ad_cb_temp)) & 0xFFFFFFFF if product_lo < BD: - product_hi = product_hi+1 - if ((product_hi >> 31) != (product_hi >> 15)): + product_hi = product_hi + 1 + if (product_hi >> 31) != (product_hi >> 15): return FIX16_OVERFLOW product_lo_tmp = product_lo & 0xFFFFFFFF product_lo = (product_lo - 0x8000) & 0xFFFFFFFF product_lo = ( - product_lo-((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF + product_lo - ((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF if product_lo > product_lo_tmp: - product_hi = product_hi-1 + product_hi = product_hi - 1 result = (product_hi << 16) | (product_lo >> 16) result += 1 return result @@ -138,35 +143,35 @@ def _fix16_div(self, a, b): if a >= 0: remainder = a else: - remainder = (a*(-1)) & 0xFFFFFFFF + remainder = (a * (-1)) & 0xFFFFFFFF if b >= 0: divider = b else: - divider = (b*(-1)) & 0xFFFFFFFF + divider = (b * (-1)) & 0xFFFFFFFF quotient = 0 bit = 0x10000 - while (divider < remainder): + while divider < remainder: divider = divider << 1 bit <<= 1 if not bit: return FIX16_OVERFLOW - if (divider & 0x80000000): - if (remainder >= divider): + if divider & 0x80000000: + if remainder >= divider: quotient |= bit remainder -= divider divider >>= 1 bit >>= 1 while bit and remainder: - if (remainder >= divider): + if remainder >= divider: quotient |= bit remainder -= divider remainder <<= 1 bit >>= 1 - if (remainder >= divider): + if remainder >= divider: quotient += 1 result = quotient - if ((a ^ b) & 0x80000000): - if (result == FIX16_MINIMUM): + if (a ^ b) & 0x80000000: + if result == FIX16_MINIMUM: return FIX16_OVERFLOW result = -result return result @@ -176,15 +181,15 @@ def _fix16_sqrt(self, x): num = x & 0xFFFFFFFF result = 0 bit = 1 << 30 - while (bit > num): + while bit > num: bit >>= 2 for n in range(0, 2): - while (bit): - if (num >= result + bit): - num = num-(result + bit) & 0xFFFFFFFF + while bit: + if num >= result + bit: + num = num - (result + bit) & 0xFFFFFFFF result = (result >> 1) + bit else: - result = (result >> 1) + result = result >> 1 bit >>= 2 if n == 0: if num > 65535: @@ -192,24 +197,32 @@ def _fix16_sqrt(self, x): num = ((num << 16) - 0x8000) & 0xFFFFFFFF result = ((result << 16) + 0x8000) & 0xFFFFFFFF else: - num = ((num << 16) & 0xFFFFFFFF) - result = ((result << 16) & 0xFFFFFFFF) + num = (num << 16) & 0xFFFFFFFF + result = (result << 16) & 0xFFFFFFFF bit = 1 << 14 - if (num > result): + if num > result: result += 1 return result def _fix16_exp(self, x): x = int(x) - exp_pos_values = [self._f16(2.7182818), self._f16( - 1.1331485), self._f16(1.0157477), self._f16(1.0019550)] - exp_neg_values = [self._f16(0.3678794), self._f16( - 0.8824969), self._f16(0.9844964), self._f16(0.9980488)] - if (x >= self._f16(10.3972)): + exp_pos_values = [ + self._f16(2.7182818), + self._f16(1.1331485), + self._f16(1.0157477), + self._f16(1.0019550), + ] + exp_neg_values = [ + self._f16(0.3678794), + self._f16(0.8824969), + self._f16(0.9844964), + self._f16(0.9980488), + ] + if x >= self._f16(10.3972): return FIX16_MAXIMUM - if (x <= self._f16(-11.7835)): + if x <= self._f16(-11.7835): return 0 - if (x < 0): + if x < 0: x = -x exp_values = exp_neg_values else: @@ -217,307 +230,591 @@ def _fix16_exp(self, x): res = FIX16_ONE arg = FIX16_ONE for i in range(0, 4): - while (x >= arg): + while x >= arg: res = self._fix16_mul(res, exp_values[i]) x -= arg arg >>= 3 return res def vocalgorithm_init(self): - self.params.mvoc_index_offset = ( - self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT)) + self.params.mvoc_index_offset = self._f16( + VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT) self.params.mtau_mean_variance_hours = self._f16( - VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS) + VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS + ) self.params.mgating_max_duration_minutes = self._f16( - VOCALGORITHM_GATING_MAX_DURATION_MINUTES) + VOCALGORITHM_GATING_MAX_DURATION_MINUTES + ) self.params.msraw_std_initial = self._f16( VOCALGORITHM_SRAW_STD_INITIAL) - self.params.muptime = self._f16(0.) - self.params.msraw = self._f16(0.) + self.params.muptime = self._f16(0.0) + self.params.msraw = self._f16(0.0) self.params.mvoc_index = 0 self._vocalgorithm__init_instances() def _vocalgorithm__init_instances(self): self._vocalgorithm__mean_variance_estimator__init() - self._vocalgorithm__mean_variance_estimator__set_parameters(self._f16( - VOCALGORITHM_SRAW_STD_INITIAL), self.params.mtau_mean_variance_hours, self.params.mgating_max_duration_minutes) + self._vocalgorithm__mean_variance_estimator__set_parameters( + self._f16(VOCALGORITHM_SRAW_STD_INITIAL), + self.params.mtau_mean_variance_hours, + self.params.mgating_max_duration_minutes, + ) self._vocalgorithm__mox_model__init() - self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( - ), self._vocalgorithm__mean_variance_estimator__get_mean()) + self._vocalgorithm__mox_model__set_parameters( + self._vocalgorithm__mean_variance_estimator__get_std(), + self._vocalgorithm__mean_variance_estimator__get_mean(), + ) self._vocalgorithm__sigmoid_scaled__init() self._vocalgorithm__sigmoid_scaled__set_parameters( - self.params.mvoc_index_offset) + self.params.mvoc_index_offset + ) self._vocalgorithm__adaptive_lowpass__init() self._vocalgorithm__adaptive_lowpass__set_parameters() def _vocalgorithm_get_states(self, state0, state1): state0 = self._vocalgorithm__mean_variance_estimator__get_mean() - state1 = _vocalgorithm__mean_variance_estimator__get_std() + state1 = self._vocalgorithm__mean_variance_estimator__get_std() return state0, state1 def _vocalgorithm_set_states(self, state0, state1): self._vocalgorithm__mean_variance_estimator__set_states( - params, state0, state1, self._f16(VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA)) + self.params, state0, state1, self._f16( + VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA) + ) self.params.msraw = state0 - def _vocalgorithm_set_tuning_parameters(self, voc_index_offset, learning_time_hours, gating_max_duration_minutes, std_initial): + def _vocalgorithm_set_tuning_parameters( + self, + voc_index_offset, + learning_time_hours, + gating_max_duration_minutes, + std_initial, + ): self.params.mvoc_index_offset = self._fix16_from_int(voc_index_offset) self.params.mtau_mean_variance_hours = self._fix16_from_int( learning_time_hours) self.params.mgating_max_duration_minutes = self._fix16_from_int( - gating_max_duration_minutes) + gating_max_duration_minutes + ) self.params.msraw_std_initial = self._fix16_from_int(std_initial) self._vocalgorithm__init_instances() def vocalgorithm_process(self, sraw): - if ((self.params.muptime <= self._f16(VOCALGORITHM_INITIAL_BLACKOUT))): - self.params.muptime = self.params.muptime + \ - self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + if self.params.muptime <= self._f16(VOCALGORITHM_INITIAL_BLACKOUT): + self.params.muptime = self.params.muptime + self._f16( + VOCALGORITHM_SAMPLING_INTERVAL + ) else: - if (((sraw > 0) and (sraw < 65000))): - if ((sraw < 20001)): + if (sraw > 0) and (sraw < 65000): + if sraw < 20001: sraw = 20001 - elif((sraw > 52767)): + elif sraw > 52767: sraw = 52767 self.params.msraw = self._fix16_from_int((sraw - 20000)) self.params.mvoc_index = self._vocalgorithm__mox_model__process( - self.params.msraw) + self.params.msraw + ) self.params.mvoc_index = self._vocalgorithm__sigmoid_scaled__process( - self.params.mvoc_index) + self.params.mvoc_index + ) self.params.mvoc_index = self._vocalgorithm__adaptive_lowpass__process( - self.params.mvoc_index) - if ((self.params.mvoc_index < self._f16(0.5))): + self.params.mvoc_index + ) + if self.params.mvoc_index < self._f16(0.5): self.params.mvoc_index = self._f16(0.5) - if self.params.msraw > self._f16(0.): + if self.params.msraw > self._f16(0.0): self._vocalgorithm__mean_variance_estimator__process( - self.params.msraw, self.params.mvoc_index) - self._vocalgorithm__mox_model__set_parameters(self._vocalgorithm__mean_variance_estimator__get_std( - ), self._vocalgorithm__mean_variance_estimator__get_mean()) + self.params.msraw, self.params.mvoc_index + ) + self._vocalgorithm__mox_model__set_parameters( + self._vocalgorithm__mean_variance_estimator__get_std(), + self._vocalgorithm__mean_variance_estimator__get_mean(), + ) voc_index = self._fix16_cast_to_int( (self.params.mvoc_index + self._f16(0.5))) return voc_index def _vocalgorithm__mean_variance_estimator__init(self): self._vocalgorithm__mean_variance_estimator__set_parameters( - self._f16(0.), self._f16(0.), self._f16(0.)) + self._f16(0.0), self._f16(0.0), self._f16(0.0) + ) self._vocalgorithm__mean_variance_estimator___init_instances() def _vocalgorithm__mean_variance_estimator___init_instances(self): self._vocalgorithm__mean_variance_estimator___sigmoid__init() - def _vocalgorithm__mean_variance_estimator__set_parameters(self, std_initial, tau_mean_variance_hours, gating_max_duration_minutes): - self.params.m_mean_variance_estimator_gating_max_duration_minutes = gating_max_duration_minutes + def _vocalgorithm__mean_variance_estimator__set_parameters( + self, std_initial, tau_mean_variance_hours, gating_max_duration_minutes + ): + self.params.m_mean_variance_estimator_gating_max_duration_minutes = ( + gating_max_duration_minutes + ) self.params.m_mean_variance_estimator_initialized = 0 - self.params.m_mean_variance_estimator_mean = self._f16(0.) - self.params.m_mean_variance_estimator_sraw_offset = self._f16(0.) + self.params.m_mean_variance_estimator_mean = self._f16(0.0) + self.params.m_mean_variance_estimator_sraw_offset = self._f16(0.0) self.params.m_mean_variance_estimator_std = std_initial - self.params.m_mean_variance_estimator_gamma = self._fix16_div(self._f16((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * (VOCALGORITHM_SAMPLING_INTERVAL / 3600.)) - ), (tau_mean_variance_hours + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 3600.)))) - self.params.m_mean_variance_estimator_gamma_initial_mean = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) - / (VOCALGORITHM_TAU_INITIAL_MEAN + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_mean_variance_estimator_gamma_initial_variance = self._f16(((VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING * VOCALGORITHM_SAMPLING_INTERVAL) - / (VOCALGORITHM_TAU_INITIAL_VARIANCE + VOCALGORITHM_SAMPLING_INTERVAL))) - self.params.m_mean_variance_estimator_gamma_mean = self._f16(0.) - self.params.m_mean_variance_estimator__gamma_variance = self._f16(0.) - self.params.m_mean_variance_estimator_uptime_gamma = self._f16(0.) - self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) + self.params.m_mean_variance_estimator_gamma = self._fix16_div( + self._f16( + ( + VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + * (VOCALGORITHM_SAMPLING_INTERVAL / 3600.0) + ) + ), + ( + tau_mean_variance_hours + + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 3600.0)) + ), + ) + self.params.m_mean_variance_estimator_gamma_initial_mean = self._f16( + ( + ( + VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + * VOCALGORITHM_SAMPLING_INTERVAL + ) + / (VOCALGORITHM_TAU_INITIAL_MEAN + VOCALGORITHM_SAMPLING_INTERVAL) + ) + ) + self.params.m_mean_variance_estimator_gamma_initial_variance = self._f16( + ( + ( + VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + * VOCALGORITHM_SAMPLING_INTERVAL + ) + / (VOCALGORITHM_TAU_INITIAL_VARIANCE + VOCALGORITHM_SAMPLING_INTERVAL) + ) + ) + self.params.m_mean_variance_estimator_gamma_mean = self._f16(0.0) + self.params.m_mean_variance_estimator__gamma_variance = self._f16(0.0) + self.params.m_mean_variance_estimator_uptime_gamma = self._f16(0.0) + self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.0) self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( - 0.) + 0.0) - def _vocalgorithm__mean_variance_estimator__set_states(self, mean, std, uptime_gamma): + def _vocalgorithm__mean_variance_estimator__set_states( + self, mean, std, uptime_gamma + ): self.params.m_mean_variance_estimator_mean = mean self.params.m_mean_variance_estimator_std = std self.params.m_mean_variance_estimator_uptime_gamma = uptime_gamma - self.params.m_mean_variance_estimator_initialized = true + self.params.m_mean_variance_estimator_initialized = True def _vocalgorithm__mean_variance_estimator__get_std(self): return self.params.m_mean_variance_estimator_std def _vocalgorithm__mean_variance_estimator__get_mean(self): - return (self.params.m_mean_variance_estimator_mean + self.params.m_mean_variance_estimator_sraw_offset) - - def _vocalgorithm__mean_variance_estimator___calculate_gamma(self, voc_index_from_prior): + return ( + self.params.m_mean_variance_estimator_mean + + self.params.m_mean_variance_estimator_sraw_offset + ) + + def _vocalgorithm__mean_variance_estimator___calculate_gamma( + self, voc_index_from_prior + ): uptime_limit = self._f16( - (VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX - VOCALGORITHM_SAMPLING_INTERVAL)) + ( + VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX + - VOCALGORITHM_SAMPLING_INTERVAL + ) + ) if self.params.m_mean_variance_estimator_uptime_gamma < uptime_limit: self.params.m_mean_variance_estimator_uptime_gamma = ( - self.params.m_mean_variance_estimator_uptime_gamma + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) + self.params.m_mean_variance_estimator_uptime_gamma + + self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + ) if self.params.m_mean_variance_estimator_uptime_gating < uptime_limit: self.params.m_mean_variance_estimator_uptime_gating = ( - self.params.m_mean_variance_estimator_uptime_gating + self._f16(VOCALGORITHM_SAMPLING_INTERVAL)) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( - VOCALGORITHM_INITI_DURATION_MEAN), self._f16(VOCALGORITHM_INITI_TRANSITION_MEAN)) - sigmoid_gamma_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - self.params.m_mean_variance_estimator_uptime_gamma) - gamma_mean = (self.params.m_mean_variance_estimator_gamma + (self._fix16_mul( - (self.params.m_mean_variance_estimator_gamma_initial_mean - self.params.m_mean_variance_estimator_gamma), sigmoid_gamma_mean))) - gating_threshold_mean = (self._f16(VOCALGORITHM_GATING_THRESHOLD) - + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), - self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( - 1.), gating_threshold_mean, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) - - sigmoid_gating_mean = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - voc_index_from_prior) - self.params.m_mean_variance_estimator_gamma_mean = ( - self._fix16_mul(sigmoid_gating_mean, gamma_mean)) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16(1.), self._f16( - VOCALGORITHM_INITI_DURATION_VARIANCE), self._f16(VOCALGORITHM_INITI_TRANSITION_VARIANCE)) - - sigmoid_gamma_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - self.params.m_mean_variance_estimator_uptime_gamma) - - gamma_variance = (self.params.m_mean_variance_estimator_gamma + - (self._fix16_mul((self.params.m_mean_variance_estimator_gamma_initial_variance - - self.params.m_mean_variance_estimator_gamma), - (sigmoid_gamma_variance - sigmoid_gamma_mean)))) - - gating_threshold_variance = (self._f16(VOCALGORITHM_GATING_THRESHOLD) - + (self._fix16_mul(self._f16((VOCALGORITHM_GATING_THRESHOLD_INITIAL - VOCALGORITHM_GATING_THRESHOLD)), - self._vocalgorithm__mean_variance_estimator___sigmoid__process(self.params.m_mean_variance_estimator_uptime_gating)))) - - self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self._f16( - 1.), gating_threshold_variance, self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION)) - - sigmoid_gating_variance = self._vocalgorithm__mean_variance_estimator___sigmoid__process( - voc_index_from_prior) - - self.params.m_mean_variance_estimator__gamma_variance = ( - self._fix16_mul(sigmoid_gating_variance, gamma_variance)) - - self.params.m_mean_variance_estimator_gating_duration_minutes = (self.params.m_mean_variance_estimator_gating_duration_minutes - + (self._fix16_mul(self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 60.)), - ((self._fix16_mul((self._f16(1.) - sigmoid_gating_mean), - self._f16((1. + VOCALGORITHM_GATING_MAX_RATIO)))) - - self._f16(VOCALGORITHM_GATING_MAX_RATIO))))) - - if ((self.params.m_mean_variance_estimator_gating_duration_minutes < self._f16(0.))): - self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( - 0.) + self.params.m_mean_variance_estimator_uptime_gating + + self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + ) - if ((self.params.m_mean_variance_estimator_gating_duration_minutes > self.params.m_mean_variance_estimator_gating_max_duration_minutes)): - self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.) + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( + self._f16(1.0), + self._f16(VOCALGORITHM_INITI_DURATION_MEAN), + self._f16(VOCALGORITHM_INITI_TRANSITION_MEAN), + ) + sigmoid_gamma_mean = ( + self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gamma + ) + ) + gamma_mean = self.params.m_mean_variance_estimator_gamma + ( + self._fix16_mul( + ( + self.params.m_mean_variance_estimator_gamma_initial_mean + - self.params.m_mean_variance_estimator_gamma + ), + sigmoid_gamma_mean, + ) + ) + gating_threshold_mean = self._f16(VOCALGORITHM_GATING_THRESHOLD) + ( + self._fix16_mul( + self._f16( + ( + VOCALGORITHM_GATING_THRESHOLD_INITIAL + - VOCALGORITHM_GATING_THRESHOLD + ) + ), + self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gating + ), + ) + ) + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( + self._f16(1.0), + gating_threshold_mean, + self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION), + ) + + sigmoid_gating_mean = ( + self._vocalgorithm__mean_variance_estimator___sigmoid__process( + voc_index_from_prior + ) + ) + self.params.m_mean_variance_estimator_gamma_mean = self._fix16_mul( + sigmoid_gating_mean, gamma_mean + ) + + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( + self._f16(1.0), + self._f16(VOCALGORITHM_INITI_DURATION_VARIANCE), + self._f16(VOCALGORITHM_INITI_TRANSITION_VARIANCE), + ) + + sigmoid_gamma_variance = ( + self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gamma + ) + ) + + gamma_variance = self.params.m_mean_variance_estimator_gamma + ( + self._fix16_mul( + ( + self.params.m_mean_variance_estimator_gamma_initial_variance + - self.params.m_mean_variance_estimator_gamma + ), + (sigmoid_gamma_variance - sigmoid_gamma_mean), + ) + ) + + gating_threshold_variance = self._f16(VOCALGORITHM_GATING_THRESHOLD) + ( + self._fix16_mul( + self._f16( + ( + VOCALGORITHM_GATING_THRESHOLD_INITIAL + - VOCALGORITHM_GATING_THRESHOLD + ) + ), + self._vocalgorithm__mean_variance_estimator___sigmoid__process( + self.params.m_mean_variance_estimator_uptime_gating + ), + ) + ) - def _vocalgorithm__mean_variance_estimator__process(self, sraw, voc_index_from_prior): - if ((self.params.m_mean_variance_estimator_initialized == 0)): + self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( + self._f16(1.0), + gating_threshold_variance, + self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION), + ) + + sigmoid_gating_variance = ( + self._vocalgorithm__mean_variance_estimator___sigmoid__process( + voc_index_from_prior + ) + ) + + self.params.m_mean_variance_estimator__gamma_variance = self._fix16_mul( + sigmoid_gating_variance, gamma_variance + ) + + self.params.m_mean_variance_estimator_gating_duration_minutes = ( + self.params.m_mean_variance_estimator_gating_duration_minutes + + ( + self._fix16_mul( + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 60.0)), + ( + ( + self._fix16_mul( + (self._f16(1.0) - sigmoid_gating_mean), + self._f16( + (1.0 + VOCALGORITHM_GATING_MAX_RATIO)), + ) + ) + - self._f16(VOCALGORITHM_GATING_MAX_RATIO) + ), + ) + ) + ) + + if self.params.m_mean_variance_estimator_gating_duration_minutes < self._f16( + 0.0 + ): + self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( + 0.0 + ) + + if ( + self.params.m_mean_variance_estimator_gating_duration_minutes + > self.params.m_mean_variance_estimator_gating_max_duration_minutes + ): + self.params.m_mean_variance_estimator_uptime_gating = self._f16( + 0.0) + + def _vocalgorithm__mean_variance_estimator__process( + self, sraw, voc_index_from_prior + ): + if self.params.m_mean_variance_estimator_initialized == 0: self.params.m_mean_variance_estimator_initialized = 1 self.params.m_mean_variance_estimator_sraw_offset = sraw - self.params.m_mean_variance_estimator_mean = self._f16(0.) + self.params.m_mean_variance_estimator_mean = self._f16(0.0) else: - if (((self.params.m_mean_variance_estimator_mean >= self._f16(100.)) or (self.params.m_mean_variance_estimator_mean <= self._f16(-100.)))): + if (self.params.m_mean_variance_estimator_mean >= self._f16(100.0)) or ( + self.params.m_mean_variance_estimator_mean <= self._f16(-100.0) + ): self.params.m_mean_variance_estimator_sraw_offset = ( - self.params.m_mean_variance_estimator_sraw_offset + self.params.m_mean_variance_estimator_mean) - self.params.m_mean_variance_estimator_mean = self._f16(0.) + self.params.m_mean_variance_estimator_sraw_offset + + self.params.m_mean_variance_estimator_mean + ) + self.params.m_mean_variance_estimator_mean = self._f16(0.0) - sraw = (sraw - self.params.m_mean_variance_estimator_sraw_offset) + sraw = sraw - self.params.m_mean_variance_estimator_sraw_offset self._vocalgorithm__mean_variance_estimator___calculate_gamma( - voc_index_from_prior) - delta_sgp = (self._fix16_div((sraw - self.params.m_mean_variance_estimator_mean), - self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING))) - if ((delta_sgp < self._f16(0.))): - c = (self.params.m_mean_variance_estimator_std - delta_sgp) + voc_index_from_prior + ) + delta_sgp = self._fix16_div( + (sraw - self.params.m_mean_variance_estimator_mean), + self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), + ) + if delta_sgp < self._f16(0.0): + c = self.params.m_mean_variance_estimator_std - delta_sgp else: - c = (self.params.m_mean_variance_estimator_std + delta_sgp) - additional_scaling = self._f16(1.) - if ((c > self._f16(1440.))): - additional_scaling = self._f16(4.) - self.params.m_mean_variance_estimator_std = self._fix16_mul(self._fix16_sqrt((self._fix16_mul(additional_scaling, - (self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) - self.params.m_mean_variance_estimator__gamma_variance)))), - self._fix16_sqrt(((self._fix16_mul(self.params.m_mean_variance_estimator_std, - (self._fix16_div(self.params.m_mean_variance_estimator_std, - (self._fix16_mul(self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), additional_scaling)))))) - + (self._fix16_mul((self._fix16_div((self._fix16_mul(self.params.m_mean_variance_estimator__gamma_variance, delta_sgp)), additional_scaling)), delta_sgp))))) - self.params.m_mean_variance_estimator_mean = (self.params.m_mean_variance_estimator_mean + ( - self._fix16_mul(self.params.m_mean_variance_estimator_gamma_mean, delta_sgp))) + c = self.params.m_mean_variance_estimator_std + delta_sgp + additional_scaling = self._f16(1.0) + if c > self._f16(1440.0): + additional_scaling = self._f16(4.0) + self.params.m_mean_variance_estimator_std = self._fix16_mul( + self._fix16_sqrt( + ( + self._fix16_mul( + additional_scaling, + ( + self._f16( + VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + ) + - self.params.m_mean_variance_estimator__gamma_variance + ), + ) + ) + ), + self._fix16_sqrt( + ( + ( + self._fix16_mul( + self.params.m_mean_variance_estimator_std, + ( + self._fix16_div( + self.params.m_mean_variance_estimator_std, + ( + self._fix16_mul( + self._f16( + VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + ), + additional_scaling, + ) + ), + ) + ), + ) + ) + + ( + self._fix16_mul( + ( + self._fix16_div( + ( + self._fix16_mul( + self.params.m_mean_variance_estimator__gamma_variance, + delta_sgp, + ) + ), + additional_scaling, + ) + ), + delta_sgp, + ) + ) + ) + ), + ) + self.params.m_mean_variance_estimator_mean = ( + self.params.m_mean_variance_estimator_mean + + ( + self._fix16_mul( + self.params.m_mean_variance_estimator_gamma_mean, delta_sgp + ) + ) + ) def _vocalgorithm__mean_variance_estimator___sigmoid__init(self): self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( - self._f16(0.), self._f16(0.), self._f16(0.)) + self._f16(0.0), self._f16(0.0), self._f16(0.0) + ) - def _vocalgorithm__mean_variance_estimator___sigmoid__set_parameters(self, L, X0, K): + def _vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( + self, L, X0, K + ): self.params.m_mean_variance_estimator_sigmoid_l = L self.params.m_mean_variance_estimator_sigmoid_k = K self.params.m_mean_variance_estimator_sigmoid_x0 = X0 def _vocalgorithm__mean_variance_estimator___sigmoid__process(self, sample): - x = (self._fix16_mul(self.params.m_mean_variance_estimator_sigmoid_k, - (sample - self.params.m_mean_variance_estimator_sigmoid_x0))) - if ((x < self._f16(-50.))): + x = self._fix16_mul( + self.params.m_mean_variance_estimator_sigmoid_k, + (sample - self.params.m_mean_variance_estimator_sigmoid_x0), + ) + if x < self._f16(-50.0): return self.params.m_mean_variance_estimator_sigmoid_l - elif ((x > self._f16(50.))): - return self._f16(0.) + elif x > self._f16(50.0): + return self._f16(0.0) else: - return (self._fix16_div(self.params.m_mean_variance_estimator_sigmoid_l, (self._f16(1.) + self._fix16_exp(x)))) + return self._fix16_div( + self.params.m_mean_variance_estimator_sigmoid_l, + (self._f16(1.0) + self._fix16_exp(x)), + ) def _vocalgorithm__mox_model__init(self): self._vocalgorithm__mox_model__set_parameters( - self._f16(1.), self._f16(0.)) + self._f16(1.0), self._f16(0.0)) def _vocalgorithm__mox_model__set_parameters(self, SRAW_STD, SRAW_MEAN): self.params.m_mox_model_sraw_std = SRAW_STD self.params.m_mox_model_sraw_mean = SRAW_MEAN def _vocalgorithm__mox_model__process(self, sraw): - return (self._fix16_mul((self._fix16_div((sraw - self.params.m_mox_model_sraw_mean), (-(self.params.m_mox_model_sraw_std + self._f16(VOCALGORITHM_SRAW_STD_BONUS))))), self._f16(VOCALGORITHM_VOC_INDEX_GAIN))) + return self._fix16_mul( + ( + self._fix16_div( + (sraw - self.params.m_mox_model_sraw_mean), + ( + -( + self.params.m_mox_model_sraw_std + + self._f16(VOCALGORITHM_SRAW_STD_BONUS) + ) + ), + ) + ), + self._f16(VOCALGORITHM_VOC_INDEX_GAIN), + ) def _vocalgorithm__sigmoid_scaled__init(self): - self._vocalgorithm__sigmoid_scaled__set_parameters(self._f16(0.)) + self._vocalgorithm__sigmoid_scaled__set_parameters(self._f16(0.0)) def _vocalgorithm__sigmoid_scaled__set_parameters(self, offset): self.params.m_sigmoid_scaled_offset = offset def _vocalgorithm__sigmoid_scaled__process(self, sample): - x = (self._fix16_mul(self._f16(VOCALGORITHM_SIGMOID_K), - (sample - self._f16(VOCALGORITHM_SIGMOID_X0)))) - if ((x < self._f16(-50.))): + x = self._fix16_mul( + self._f16(VOCALGORITHM_SIGMOID_K), + (sample - self._f16(VOCALGORITHM_SIGMOID_X0)), + ) + if x < self._f16(-50.0): return self._f16(VOCALGORITHM_SIGMOID_L) - elif ((x > self._f16(50.))): - return self._f16(0.) + elif x > self._f16(50.0): + return self._f16(0.0) else: - if ((sample >= self._f16(0.))): - shift = (self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) - (self._fix16_mul( - self._f16(5.), self.params.m_sigmoid_scaled_offset))), self._f16(4.))) - return ((self._fix16_div((self._f16(VOCALGORITHM_SIGMOID_L) + shift), (self._f16(1.) + self._fix16_exp(x)))) - shift) + if sample >= self._f16(0.0): + shift = self._fix16_div( + ( + self._f16(VOCALGORITHM_SIGMOID_L) + - ( + self._fix16_mul( + self._f16( + 5.0), self.params.m_sigmoid_scaled_offset + ) + ) + ), + self._f16(4.0), + ) + return ( + self._fix16_div( + (self._f16(VOCALGORITHM_SIGMOID_L) + shift), + (self._f16(1.0) + self._fix16_exp(x)), + ) + ) - shift else: - return (self._fix16_mul((self._fix16_div(self.params.m_sigmoid_scaled_offset, self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT))), - (self._fix16_div(self._f16(VOCALGORITHM_SIGMOID_L), (self._f16(1.) + self._fix16_exp(x)))))) + return self._fix16_mul( + ( + self._fix16_div( + self.params.m_sigmoid_scaled_offset, + self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT), + ) + ), + ( + self._fix16_div( + self._f16(VOCALGORITHM_SIGMOID_L), + (self._f16(1.0) + self._fix16_exp(x)), + ) + ), + ) def _vocalgorithm__adaptive_lowpass__init(self): self._vocalgorithm__adaptive_lowpass__set_parameters() def _vocalgorithm__adaptive_lowpass__set_parameters(self): self.params.m_adaptive_lowpass_a1 = self._f16( - (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_FAST + VOCALGORITHM_SAMPLING_INTERVAL))) + ( + VOCALGORITHM_SAMPLING_INTERVAL + / (VOCALGORITHM_LP_TAU_FAST + VOCALGORITHM_SAMPLING_INTERVAL) + ) + ) self.params.m_adaptive_lowpass_a2 = self._f16( - (VOCALGORITHM_SAMPLING_INTERVAL / (VOCALGORITHM_LP_TAU_SLOW + VOCALGORITHM_SAMPLING_INTERVAL))) + ( + VOCALGORITHM_SAMPLING_INTERVAL + / (VOCALGORITHM_LP_TAU_SLOW + VOCALGORITHM_SAMPLING_INTERVAL) + ) + ) self.params.m_adaptive_lowpass_initialized = 0 def _vocalgorithm__adaptive_lowpass__process(self, sample): - if ((self.params.m_adaptive_lowpass_initialized == 0)): + if self.params.m_adaptive_lowpass_initialized == 0: self.params.m_adaptive_lowpass_x1 = sample self.params.m_adaptive_lowpass_x2 = sample self.params.m_adaptive_lowpass_x3 = sample self.params.m_adaptive_lowpass_initialized = 1 - self.params.m_adaptive_lowpass_x1 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a1), - self.params.m_adaptive_lowpass_x1)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a1, sample))) - - self.params.m_adaptive_lowpass_x2 = ((self._fix16_mul((self._f16(1.) - self.params.m_adaptive_lowpass_a2), - self.params.m_adaptive_lowpass_x2)) + (self._fix16_mul(self.params.m_adaptive_lowpass_a2, sample))) - - abs_delta = (self.params.m_adaptive_lowpass_x1 - - self.params.m_adaptive_lowpass_x2) - - if ((abs_delta < self._f16(0.))): - abs_delta = (-abs_delta) + self.params.m_adaptive_lowpass_x1 = ( + self._fix16_mul( + (self._f16(1.0) - self.params.m_adaptive_lowpass_a1), + self.params.m_adaptive_lowpass_x1, + ) + ) + (self._fix16_mul(self.params.m_adaptive_lowpass_a1, sample)) + + self.params.m_adaptive_lowpass_x2 = ( + self._fix16_mul( + (self._f16(1.0) - self.params.m_adaptive_lowpass_a2), + self.params.m_adaptive_lowpass_x2, + ) + ) + (self._fix16_mul(self.params.m_adaptive_lowpass_a2, sample)) + + abs_delta = ( + self.params.m_adaptive_lowpass_x1 - self.params.m_adaptive_lowpass_x2 + ) + + if abs_delta < self._f16(0.0): + abs_delta = -abs_delta F1 = self._fix16_exp( - (self._fix16_mul(self._f16(VOCALGORITHM_LP_ALPHA), abs_delta))) - tau_a = ((self._fix16_mul(self._f16((VOCALGORITHM_LP_TAU_SLOW - - VOCALGORITHM_LP_TAU_FAST)), F1)) + self._f16(VOCALGORITHM_LP_TAU_FAST)) - a3 = (self._fix16_div(self._f16(VOCALGORITHM_SAMPLING_INTERVAL), - (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a))) - self.params.m_adaptive_lowpass_x3 = ((self._fix16_mul((self._f16( - 1.) - a3), self.params.m_adaptive_lowpass_x3)) + (self._fix16_mul(a3, sample))) + (self._fix16_mul(self._f16(VOCALGORITHM_LP_ALPHA), abs_delta)) + ) + tau_a = ( + self._fix16_mul( + self._f16((VOCALGORITHM_LP_TAU_SLOW - + VOCALGORITHM_LP_TAU_FAST)), F1 + ) + ) + self._f16(VOCALGORITHM_LP_TAU_FAST) + a3 = self._fix16_div( + self._f16(VOCALGORITHM_SAMPLING_INTERVAL), + (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a), + ) + self.params.m_adaptive_lowpass_x3 = ( + self._fix16_mul((self._f16(1.0) - a3), + self.params.m_adaptive_lowpass_x3) + ) + (self._fix16_mul(a3, sample)) return self.params.m_adaptive_lowpass_x3 diff --git a/examples/sgp40_indextest.py b/examples/sgp40_indextest.py index 97cbc09..8860418 100644 --- a/examples/sgp40_indextest.py +++ b/examples/sgp40_indextest.py @@ -1,7 +1,10 @@ +# SPDX-FileCopyrightText: 2020 by Bryan Siepert for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense import time import board -import adafruit_sgp40 import adafruit_bme280 +import adafruit_sgp40 # Boards i2c bus i2c = board.I2C() # uses board.SCL and board.SDA @@ -16,10 +19,12 @@ # For compensated raw gas readings compensated_raw_gas = sgp.measure_index( - temperature=temperature, relative_humidity=humidity) + temperature=temperature, relative_humidity=humidity + ) # For Compensated voc index readings - # It may take several minutes for the VOC index to start changing as it calibrates the baseline readings. + # It may take several minutes for the VOC index to start changing + # as it calibrates the baseline readings. voc_index = sgp.measure_raw( temperature=temperature, relative_humidity=humidity) From 82368d6cd955031f8f22b33e20dd1c3efea2aa23 Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Sat, 28 Aug 2021 07:44:41 -0700 Subject: [PATCH 6/9] black --- adafruit_sgp40/__init__.py | 7 +++-- adafruit_sgp40/voc_algorithm.py | 45 +++++++++++++-------------------- examples/sgp40_indextest.py | 3 +-- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/adafruit_sgp40/__init__.py b/adafruit_sgp40/__init__.py index 615b8ba..a6d7e4d 100644 --- a/adafruit_sgp40/__init__.py +++ b/adafruit_sgp40/__init__.py @@ -133,8 +133,7 @@ def initialize(self): featureset = self._read_word_from_command() if featureset[0] != 0x3220: - raise RuntimeError("Feature set does not match: %s" % - hex(featureset[0])) + raise RuntimeError("Feature set does not match: %s" % hex(featureset[0])) self._voc_algorithm.vocalgorithm_init() @@ -281,9 +280,9 @@ def _read_word_from_command( i2c.readinto(replybuffer, end=replylen) for i in range(0, replylen, 3): - if not self._check_crc8(replybuffer[i: i + 2], replybuffer[i + 2]): + if not self._check_crc8(replybuffer[i : i + 2], replybuffer[i + 2]): raise RuntimeError("CRC check failed while reading data") - readdata_buffer.append(unpack_from(">H", replybuffer[i: i + 2])[0]) + readdata_buffer.append(unpack_from(">H", replybuffer[i : i + 2])[0]) return readdata_buffer diff --git a/adafruit_sgp40/voc_algorithm.py b/adafruit_sgp40/voc_algorithm.py index 11d8068..4b97d0a 100644 --- a/adafruit_sgp40/voc_algorithm.py +++ b/adafruit_sgp40/voc_algorithm.py @@ -46,8 +46,7 @@ class DFRobot_vocalgorithmParams: - """Class for voc index algorithm - """ + """Class for voc index algorithm""" # pylint: disable=all # Complex math conversion from C @@ -127,8 +126,7 @@ def _fix16_mul(self, inarg0, inarg1): return FIX16_OVERFLOW product_lo_tmp = product_lo & 0xFFFFFFFF product_lo = (product_lo - 0x8000) & 0xFFFFFFFF - product_lo = ( - product_lo - ((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF + product_lo = (product_lo - ((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF if product_lo > product_lo_tmp: product_hi = product_hi - 1 result = (product_hi << 16) | (product_lo >> 16) @@ -237,16 +235,14 @@ def _fix16_exp(self, x): return res def vocalgorithm_init(self): - self.params.mvoc_index_offset = self._f16( - VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT) + self.params.mvoc_index_offset = self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT) self.params.mtau_mean_variance_hours = self._f16( VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS ) self.params.mgating_max_duration_minutes = self._f16( VOCALGORITHM_GATING_MAX_DURATION_MINUTES ) - self.params.msraw_std_initial = self._f16( - VOCALGORITHM_SRAW_STD_INITIAL) + self.params.msraw_std_initial = self._f16(VOCALGORITHM_SRAW_STD_INITIAL) self.params.muptime = self._f16(0.0) self.params.msraw = self._f16(0.0) self.params.mvoc_index = 0 @@ -278,8 +274,10 @@ def _vocalgorithm_get_states(self, state0, state1): def _vocalgorithm_set_states(self, state0, state1): self._vocalgorithm__mean_variance_estimator__set_states( - self.params, state0, state1, self._f16( - VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA) + self.params, + state0, + state1, + self._f16(VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA), ) self.params.msraw = state0 @@ -291,8 +289,7 @@ def _vocalgorithm_set_tuning_parameters( std_initial, ): self.params.mvoc_index_offset = self._fix16_from_int(voc_index_offset) - self.params.mtau_mean_variance_hours = self._fix16_from_int( - learning_time_hours) + self.params.mtau_mean_variance_hours = self._fix16_from_int(learning_time_hours) self.params.mgating_max_duration_minutes = self._fix16_from_int( gating_max_duration_minutes ) @@ -330,8 +327,7 @@ def vocalgorithm_process(self, sraw): self._vocalgorithm__mean_variance_estimator__get_std(), self._vocalgorithm__mean_variance_estimator__get_mean(), ) - voc_index = self._fix16_cast_to_int( - (self.params.mvoc_index + self._f16(0.5))) + voc_index = self._fix16_cast_to_int((self.params.mvoc_index + self._f16(0.5))) return voc_index def _vocalgorithm__mean_variance_estimator__init(self): @@ -387,8 +383,7 @@ def _vocalgorithm__mean_variance_estimator__set_parameters( self.params.m_mean_variance_estimator__gamma_variance = self._f16(0.0) self.params.m_mean_variance_estimator_uptime_gamma = self._f16(0.0) self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.0) - self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16( - 0.0) + self.params.m_mean_variance_estimator_gating_duration_minutes = self._f16(0.0) def _vocalgorithm__mean_variance_estimator__set_states( self, mean, std, uptime_gamma @@ -536,8 +531,7 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( ( self._fix16_mul( (self._f16(1.0) - sigmoid_gating_mean), - self._f16( - (1.0 + VOCALGORITHM_GATING_MAX_RATIO)), + self._f16((1.0 + VOCALGORITHM_GATING_MAX_RATIO)), ) ) - self._f16(VOCALGORITHM_GATING_MAX_RATIO) @@ -557,8 +551,7 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( self.params.m_mean_variance_estimator_gating_duration_minutes > self.params.m_mean_variance_estimator_gating_max_duration_minutes ): - self.params.m_mean_variance_estimator_uptime_gating = self._f16( - 0.0) + self.params.m_mean_variance_estimator_uptime_gating = self._f16(0.0) def _vocalgorithm__mean_variance_estimator__process( self, sraw, voc_index_from_prior @@ -682,8 +675,7 @@ def _vocalgorithm__mean_variance_estimator___sigmoid__process(self, sample): ) def _vocalgorithm__mox_model__init(self): - self._vocalgorithm__mox_model__set_parameters( - self._f16(1.0), self._f16(0.0)) + self._vocalgorithm__mox_model__set_parameters(self._f16(1.0), self._f16(0.0)) def _vocalgorithm__mox_model__set_parameters(self, SRAW_STD, SRAW_MEAN): self.params.m_mox_model_sraw_std = SRAW_STD @@ -727,8 +719,7 @@ def _vocalgorithm__sigmoid_scaled__process(self, sample): self._f16(VOCALGORITHM_SIGMOID_L) - ( self._fix16_mul( - self._f16( - 5.0), self.params.m_sigmoid_scaled_offset + self._f16(5.0), self.params.m_sigmoid_scaled_offset ) ) ), @@ -805,8 +796,7 @@ def _vocalgorithm__adaptive_lowpass__process(self, sample): ) tau_a = ( self._fix16_mul( - self._f16((VOCALGORITHM_LP_TAU_SLOW - - VOCALGORITHM_LP_TAU_FAST)), F1 + self._f16((VOCALGORITHM_LP_TAU_SLOW - VOCALGORITHM_LP_TAU_FAST)), F1 ) ) + self._f16(VOCALGORITHM_LP_TAU_FAST) a3 = self._fix16_div( @@ -814,7 +804,6 @@ def _vocalgorithm__adaptive_lowpass__process(self, sample): (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a), ) self.params.m_adaptive_lowpass_x3 = ( - self._fix16_mul((self._f16(1.0) - a3), - self.params.m_adaptive_lowpass_x3) + self._fix16_mul((self._f16(1.0) - a3), self.params.m_adaptive_lowpass_x3) ) + (self._fix16_mul(a3, sample)) return self.params.m_adaptive_lowpass_x3 diff --git a/examples/sgp40_indextest.py b/examples/sgp40_indextest.py index 8860418..e4a93d2 100644 --- a/examples/sgp40_indextest.py +++ b/examples/sgp40_indextest.py @@ -25,8 +25,7 @@ # For Compensated voc index readings # It may take several minutes for the VOC index to start changing # as it calibrates the baseline readings. - voc_index = sgp.measure_raw( - temperature=temperature, relative_humidity=humidity) + voc_index = sgp.measure_raw(temperature=temperature, relative_humidity=humidity) print(compensated_raw_gas) print(voc_index) From 63287a6cabf975034de5f5051409481b702ec574 Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Sat, 28 Aug 2021 07:52:23 -0700 Subject: [PATCH 7/9] fix docstring indent --- adafruit_sgp40/__init__.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/adafruit_sgp40/__init__.py b/adafruit_sgp40/__init__.py index a6d7e4d..d092f89 100644 --- a/adafruit_sgp40/__init__.py +++ b/adafruit_sgp40/__init__.py @@ -230,16 +230,14 @@ def measure_raw(self, temperature=25, relative_humidity=50): def measure_index(self, temperature=25, relative_humidity=50): """Measure VOC index after humidity compensation - :param float temperature: The temperature in degrees Celsius, defaults - to :const:`25` - :param float relative_humidity: The relative humidity in percentage, defaults - to :const:`50` + :param float temperature: The temperature in degrees Celsius, defaults to :const:`25` + :param float relative_humidity: The relative humidity in percentage, defaults to :const:`50` :note VOC index can indicate the quality of the air directly. The larger the value, the worse the air quality. - :note 0-100,no need to ventilate, purify - :note 100-200,no need to ventilate, purify - :note 200-400,ventilate, purify - :note 00-500,ventilate, purify intensely + :note 0-100,no need to ventilate, purify + :note 100-200,no need to ventilate, purify + :note 200-400,ventilate, purify + :note 00-500,ventilate, purify intensely :return int The VOC index measured, ranged from 0 to 500 """ raw = self.measure_raw(temperature, relative_humidity) From 8df1cc5eb0c1fe2705525e295c912f6afc3c9d26 Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Tue, 31 Aug 2021 06:21:56 -0700 Subject: [PATCH 8/9] conditional import, constants, example fix --- README.rst | 4 +- adafruit_sgp40/__init__.py | 23 ++-- adafruit_sgp40/voc_algorithm.py | 196 ++++++++++++++++---------------- examples/sgp40_indextest.py | 8 +- 4 files changed, 120 insertions(+), 111 deletions(-) diff --git a/README.rst b/README.rst index cc76fd3..1109538 100644 --- a/README.rst +++ b/README.rst @@ -92,10 +92,10 @@ For humidity compensated raw gas and voc index readings, we'll need a secondary humidity = bme280.relative_humidity # For compensated raw gas readings - compensated_raw_gas = sgp.measure_index(temperature = temperature, relative_humidity = humidity) + compensated_raw_gas = sgp.raw(temperature = temperature, relative_humidity = humidity) # For Compensated voc index readings - voc_index = sgp.measure_raw(temperature = temperature, relative_humidity = humidity) + voc_index = sgp.measure_index(temperature = temperature, relative_humidity = humidity) print(compensated_raw_gas) print(voc_index) diff --git a/adafruit_sgp40/__init__.py b/adafruit_sgp40/__init__.py index d092f89..0b1ebf1 100644 --- a/adafruit_sgp40/__init__.py +++ b/adafruit_sgp40/__init__.py @@ -32,7 +32,6 @@ from time import sleep from struct import unpack_from from adafruit_bus_device import i2c_device -from adafruit_sgp40.voc_algorithm import VOCAlgorithm __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SGP40.git" @@ -112,7 +111,7 @@ def __init__(self, i2c, address=0x59): self.i2c_device = i2c_device.I2CDevice(i2c, address) self._command_buffer = bytearray(2) self._measure_command = _READ_CMD - self._voc_algorithm = VOCAlgorithm() + self._voc_algorithm = None self.initialize() @@ -135,8 +134,6 @@ def initialize(self): raise RuntimeError("Feature set does not match: %s" % hex(featureset[0])) - self._voc_algorithm.vocalgorithm_init() - # Self Test self._command_buffer[0] = 0x28 self._command_buffer[1] = 0x0E @@ -234,12 +231,22 @@ def measure_index(self, temperature=25, relative_humidity=50): :param float relative_humidity: The relative humidity in percentage, defaults to :const:`50` :note VOC index can indicate the quality of the air directly. The larger the value, the worse the air quality. - :note 0-100,no need to ventilate, purify - :note 100-200,no need to ventilate, purify - :note 200-400,ventilate, purify - :note 00-500,ventilate, purify intensely + :note 0-100, no need to ventilate, purify + :note 100-200, no need to ventilate, purify + :note 200-400, ventilate, purify + :note 00-500, ventilate, purify intensely :return int The VOC index measured, ranged from 0 to 500 """ + # import/setup algorithm only on use of index + # pylint: disable=import-outside-toplevel + from adafruit_sgp40.voc_algorithm import ( + VOCAlgorithm, + ) + + if self._voc_algorithm is None: + self._voc_algorithm = VOCAlgorithm() + self._voc_algorithm.vocalgorithm_init() + raw = self.measure_raw(temperature, relative_humidity) if raw < 0: return -1 diff --git a/adafruit_sgp40/voc_algorithm.py b/adafruit_sgp40/voc_algorithm.py index 4b97d0a..3106fa2 100644 --- a/adafruit_sgp40/voc_algorithm.py +++ b/adafruit_sgp40/voc_algorithm.py @@ -12,37 +12,37 @@ """ -VOCALGORITHM_SAMPLING_INTERVAL = 1.0 -VOCALGORITHM_INITIAL_BLACKOUT = 45.0 -VOCALGORITHM_VOC_INDEX_GAIN = 230.0 -VOCALGORITHM_SRAW_STD_INITIAL = 50.0 -VOCALGORITHM_SRAW_STD_BONUS = 220.0 -VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = 12.0 -VOCALGORITHM_TAU_INITIAL_MEAN = 20.0 -VOCALGORITHM_INITI_DURATION_MEAN = 3600.0 * 0.75 -VOCALGORITHM_INITI_TRANSITION_MEAN = 0.01 -VOCALGORITHM_TAU_INITIAL_VARIANCE = 2500.0 -VOCALGORITHM_INITI_DURATION_VARIANCE = 3600.0 * 1.45 -VOCALGORITHM_INITI_TRANSITION_VARIANCE = 0.01 -VOCALGORITHM_GATING_THRESHOLD = 340.0 -VOCALGORITHM_GATING_THRESHOLD_INITIAL = 510.0 -VOCALGORITHM_GATING_THRESHOLD_TRANSITION = 0.09 -VOCALGORITHM_GATING_MAX_DURATION_MINUTES = 60.0 * 3.0 -VOCALGORITHM_GATING_MAX_RATIO = 0.3 -VOCALGORITHM_SIGMOID_L = 500.0 -VOCALGORITHM_SIGMOID_K = -0.0065 -VOCALGORITHM_SIGMOID_X0 = 213.0 -VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = 100.0 -VOCALGORITHM_LP_TAU_FAST = 20.0 -VOCALGORITHM_LP_TAU_SLOW = 500.0 -VOCALGORITHM_LP_ALPHA = -0.2 -VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = 3.0 * 3600.0 -VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = 64.0 -VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = 32767.0 -FIX16_MAXIMUM = 0x7FFFFFFF -FIX16_MINIMUM = 0x80000000 -FIX16_OVERFLOW = 0x80000000 -FIX16_ONE = 0x00010000 +_VOCALGORITHM_SAMPLING_INTERVAL = const(1) +_VOCALGORITHM_INITIAL_BLACKOUT = const(45) +_VOCALGORITHM_VOC_INDEX_GAIN = const(230) +_VOCALGORITHM_SRAW_STD_INITIAL = const(50) +_VOCALGORITHM_SRAW_STD_BONUS = const(220) +_VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS = const(12) +_VOCALGORITHM_TAU_INITIAL_MEAN = const(20) +_VOCALGORITHM_INITI_DURATION_MEAN = const(2700) +_VOCALGORITHM_INITI_TRANSITION_MEAN = 0.01 +_VOCALGORITHM_TAU_INITIAL_VARIANCE = const(2500) +_VOCALGORITHM_INITI_DURATION_VARIANCE = const(5220) +_VOCALGORITHM_INITI_TRANSITION_VARIANCE = 0.01 +_VOCALGORITHM_GATING_THRESHOLD = const(340) +_VOCALGORITHM_GATING_THRESHOLD_INITIAL = const(510) +_VOCALGORITHM_GATING_THRESHOLD_TRANSITION = 0.09 +_VOCALGORITHM_GATING_MAX_DURATION_MINUTES = const(180) +_VOCALGORITHM_GATING_MAX_RATIO = 0.3 +_VOCALGORITHM_SIGMOID_L = const(500) +_VOCALGORITHM_SIGMOID_K = -0.0065 +_VOCALGORITHM_SIGMOID_X0 = const(213) +_VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT = const(100) +_VOCALGORITHM_LP_TAU_FAST = const(20) +_VOCALGORITHM_LP_TAU_SLOW = const(500) +_VOCALGORITHM_LP_ALPHA = -0.2 +_VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA = const(10800) +_VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING = const(64) +_VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX = const(32767) +_FIX16_MAXIMUM = const(0x7FFFFFFF) +_FIX16_MINIMUM = const(0x80000000) +_FIX16_OVERFLOW = const(0x80000000) +_FIX16_ONE = const(0x00010000) class DFRobot_vocalgorithmParams: @@ -96,7 +96,7 @@ def _f16(self, x): return int((x) * 65536.0 - 0.5) def _fix16_from_int(self, a): - return int(a * FIX16_ONE) + return int(a * _FIX16_ONE) def _fix16_cast_to_int(self, a): return int(a) >> 16 @@ -123,7 +123,7 @@ def _fix16_mul(self, inarg0, inarg1): if product_lo < BD: product_hi = product_hi + 1 if (product_hi >> 31) != (product_hi >> 15): - return FIX16_OVERFLOW + return _FIX16_OVERFLOW product_lo_tmp = product_lo & 0xFFFFFFFF product_lo = (product_lo - 0x8000) & 0xFFFFFFFF product_lo = (product_lo - ((product_hi & 0xFFFFFFFF) >> 31)) & 0xFFFFFFFF @@ -137,7 +137,7 @@ def _fix16_div(self, a, b): a = int(a) b = int(b) if b == 0: - return FIX16_MINIMUM + return _FIX16_MINIMUM if a >= 0: remainder = a else: @@ -152,7 +152,7 @@ def _fix16_div(self, a, b): divider = divider << 1 bit <<= 1 if not bit: - return FIX16_OVERFLOW + return _FIX16_OVERFLOW if divider & 0x80000000: if remainder >= divider: quotient |= bit @@ -169,8 +169,8 @@ def _fix16_div(self, a, b): quotient += 1 result = quotient if (a ^ b) & 0x80000000: - if result == FIX16_MINIMUM: - return FIX16_OVERFLOW + if result == _FIX16_MINIMUM: + return _FIX16_OVERFLOW result = -result return result @@ -217,7 +217,7 @@ def _fix16_exp(self, x): self._f16(0.9980488), ] if x >= self._f16(10.3972): - return FIX16_MAXIMUM + return _FIX16_MAXIMUM if x <= self._f16(-11.7835): return 0 if x < 0: @@ -225,8 +225,8 @@ def _fix16_exp(self, x): exp_values = exp_neg_values else: exp_values = exp_pos_values - res = FIX16_ONE - arg = FIX16_ONE + res = _FIX16_ONE + arg = _FIX16_ONE for i in range(0, 4): while x >= arg: res = self._fix16_mul(res, exp_values[i]) @@ -235,14 +235,16 @@ def _fix16_exp(self, x): return res def vocalgorithm_init(self): - self.params.mvoc_index_offset = self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT) + self.params.mvoc_index_offset = self._f16( + _VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT + ) self.params.mtau_mean_variance_hours = self._f16( - VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS + _VOCALGORITHM_TAU_MEAN_VARIANCE_HOURS ) self.params.mgating_max_duration_minutes = self._f16( - VOCALGORITHM_GATING_MAX_DURATION_MINUTES + _VOCALGORITHM_GATING_MAX_DURATION_MINUTES ) - self.params.msraw_std_initial = self._f16(VOCALGORITHM_SRAW_STD_INITIAL) + self.params.msraw_std_initial = self._f16(_VOCALGORITHM_SRAW_STD_INITIAL) self.params.muptime = self._f16(0.0) self.params.msraw = self._f16(0.0) self.params.mvoc_index = 0 @@ -251,7 +253,7 @@ def vocalgorithm_init(self): def _vocalgorithm__init_instances(self): self._vocalgorithm__mean_variance_estimator__init() self._vocalgorithm__mean_variance_estimator__set_parameters( - self._f16(VOCALGORITHM_SRAW_STD_INITIAL), + self._f16(_VOCALGORITHM_SRAW_STD_INITIAL), self.params.mtau_mean_variance_hours, self.params.mgating_max_duration_minutes, ) @@ -277,7 +279,7 @@ def _vocalgorithm_set_states(self, state0, state1): self.params, state0, state1, - self._f16(VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA), + self._f16(_VOCALGORITHM_PERSISTENCE_UPTIME_GAMMA), ) self.params.msraw = state0 @@ -297,9 +299,9 @@ def _vocalgorithm_set_tuning_parameters( self._vocalgorithm__init_instances() def vocalgorithm_process(self, sraw): - if self.params.muptime <= self._f16(VOCALGORITHM_INITIAL_BLACKOUT): + if self.params.muptime <= self._f16(_VOCALGORITHM_INITIAL_BLACKOUT): self.params.muptime = self.params.muptime + self._f16( - VOCALGORITHM_SAMPLING_INTERVAL + _VOCALGORITHM_SAMPLING_INTERVAL ) else: if (sraw > 0) and (sraw < 65000): @@ -352,31 +354,31 @@ def _vocalgorithm__mean_variance_estimator__set_parameters( self.params.m_mean_variance_estimator_gamma = self._fix16_div( self._f16( ( - VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING - * (VOCALGORITHM_SAMPLING_INTERVAL / 3600.0) + _VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + * (_VOCALGORITHM_SAMPLING_INTERVAL / 3600.0) ) ), ( tau_mean_variance_hours - + self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 3600.0)) + + self._f16((_VOCALGORITHM_SAMPLING_INTERVAL / 3600.0)) ), ) self.params.m_mean_variance_estimator_gamma_initial_mean = self._f16( ( ( - VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING - * VOCALGORITHM_SAMPLING_INTERVAL + _VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + * _VOCALGORITHM_SAMPLING_INTERVAL ) - / (VOCALGORITHM_TAU_INITIAL_MEAN + VOCALGORITHM_SAMPLING_INTERVAL) + / (_VOCALGORITHM_TAU_INITIAL_MEAN + _VOCALGORITHM_SAMPLING_INTERVAL) ) ) self.params.m_mean_variance_estimator_gamma_initial_variance = self._f16( ( ( - VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING - * VOCALGORITHM_SAMPLING_INTERVAL + _VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + * _VOCALGORITHM_SAMPLING_INTERVAL ) - / (VOCALGORITHM_TAU_INITIAL_VARIANCE + VOCALGORITHM_SAMPLING_INTERVAL) + / (_VOCALGORITHM_TAU_INITIAL_VARIANCE + _VOCALGORITHM_SAMPLING_INTERVAL) ) ) self.params.m_mean_variance_estimator_gamma_mean = self._f16(0.0) @@ -407,26 +409,26 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( ): uptime_limit = self._f16( ( - VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX - - VOCALGORITHM_SAMPLING_INTERVAL + _VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX + - _VOCALGORITHM_SAMPLING_INTERVAL ) ) if self.params.m_mean_variance_estimator_uptime_gamma < uptime_limit: self.params.m_mean_variance_estimator_uptime_gamma = ( self.params.m_mean_variance_estimator_uptime_gamma - + self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + + self._f16(_VOCALGORITHM_SAMPLING_INTERVAL) ) if self.params.m_mean_variance_estimator_uptime_gating < uptime_limit: self.params.m_mean_variance_estimator_uptime_gating = ( self.params.m_mean_variance_estimator_uptime_gating - + self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + + self._f16(_VOCALGORITHM_SAMPLING_INTERVAL) ) self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( self._f16(1.0), - self._f16(VOCALGORITHM_INITI_DURATION_MEAN), - self._f16(VOCALGORITHM_INITI_TRANSITION_MEAN), + self._f16(_VOCALGORITHM_INITI_DURATION_MEAN), + self._f16(_VOCALGORITHM_INITI_TRANSITION_MEAN), ) sigmoid_gamma_mean = ( self._vocalgorithm__mean_variance_estimator___sigmoid__process( @@ -442,12 +444,12 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( sigmoid_gamma_mean, ) ) - gating_threshold_mean = self._f16(VOCALGORITHM_GATING_THRESHOLD) + ( + gating_threshold_mean = self._f16(_VOCALGORITHM_GATING_THRESHOLD) + ( self._fix16_mul( self._f16( ( - VOCALGORITHM_GATING_THRESHOLD_INITIAL - - VOCALGORITHM_GATING_THRESHOLD + _VOCALGORITHM_GATING_THRESHOLD_INITIAL + - _VOCALGORITHM_GATING_THRESHOLD ) ), self._vocalgorithm__mean_variance_estimator___sigmoid__process( @@ -458,7 +460,7 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( self._f16(1.0), gating_threshold_mean, - self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION), + self._f16(_VOCALGORITHM_GATING_THRESHOLD_TRANSITION), ) sigmoid_gating_mean = ( @@ -472,8 +474,8 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( self._f16(1.0), - self._f16(VOCALGORITHM_INITI_DURATION_VARIANCE), - self._f16(VOCALGORITHM_INITI_TRANSITION_VARIANCE), + self._f16(_VOCALGORITHM_INITI_DURATION_VARIANCE), + self._f16(_VOCALGORITHM_INITI_TRANSITION_VARIANCE), ) sigmoid_gamma_variance = ( @@ -492,12 +494,12 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( ) ) - gating_threshold_variance = self._f16(VOCALGORITHM_GATING_THRESHOLD) + ( + gating_threshold_variance = self._f16(_VOCALGORITHM_GATING_THRESHOLD) + ( self._fix16_mul( self._f16( ( - VOCALGORITHM_GATING_THRESHOLD_INITIAL - - VOCALGORITHM_GATING_THRESHOLD + _VOCALGORITHM_GATING_THRESHOLD_INITIAL + - _VOCALGORITHM_GATING_THRESHOLD ) ), self._vocalgorithm__mean_variance_estimator___sigmoid__process( @@ -509,7 +511,7 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( self._vocalgorithm__mean_variance_estimator___sigmoid__set_parameters( self._f16(1.0), gating_threshold_variance, - self._f16(VOCALGORITHM_GATING_THRESHOLD_TRANSITION), + self._f16(_VOCALGORITHM_GATING_THRESHOLD_TRANSITION), ) sigmoid_gating_variance = ( @@ -526,15 +528,15 @@ def _vocalgorithm__mean_variance_estimator___calculate_gamma( self.params.m_mean_variance_estimator_gating_duration_minutes + ( self._fix16_mul( - self._f16((VOCALGORITHM_SAMPLING_INTERVAL / 60.0)), + self._f16((_VOCALGORITHM_SAMPLING_INTERVAL / 60.0)), ( ( self._fix16_mul( (self._f16(1.0) - sigmoid_gating_mean), - self._f16((1.0 + VOCALGORITHM_GATING_MAX_RATIO)), + self._f16((1.0 + _VOCALGORITHM_GATING_MAX_RATIO)), ) ) - - self._f16(VOCALGORITHM_GATING_MAX_RATIO) + - self._f16(_VOCALGORITHM_GATING_MAX_RATIO) ), ) ) @@ -576,7 +578,7 @@ def _vocalgorithm__mean_variance_estimator__process( ) delta_sgp = self._fix16_div( (sraw - self.params.m_mean_variance_estimator_mean), - self._f16(VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), + self._f16(_VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING), ) if delta_sgp < self._f16(0.0): c = self.params.m_mean_variance_estimator_std - delta_sgp @@ -592,7 +594,7 @@ def _vocalgorithm__mean_variance_estimator__process( additional_scaling, ( self._f16( - VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + _VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING ) - self.params.m_mean_variance_estimator__gamma_variance ), @@ -610,7 +612,7 @@ def _vocalgorithm__mean_variance_estimator__process( ( self._fix16_mul( self._f16( - VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING + _VOCALGORITHM_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING ), additional_scaling, ) @@ -689,12 +691,12 @@ def _vocalgorithm__mox_model__process(self, sraw): ( -( self.params.m_mox_model_sraw_std - + self._f16(VOCALGORITHM_SRAW_STD_BONUS) + + self._f16(_VOCALGORITHM_SRAW_STD_BONUS) ) ), ) ), - self._f16(VOCALGORITHM_VOC_INDEX_GAIN), + self._f16(_VOCALGORITHM_VOC_INDEX_GAIN), ) def _vocalgorithm__sigmoid_scaled__init(self): @@ -705,18 +707,18 @@ def _vocalgorithm__sigmoid_scaled__set_parameters(self, offset): def _vocalgorithm__sigmoid_scaled__process(self, sample): x = self._fix16_mul( - self._f16(VOCALGORITHM_SIGMOID_K), - (sample - self._f16(VOCALGORITHM_SIGMOID_X0)), + self._f16(_VOCALGORITHM_SIGMOID_K), + (sample - self._f16(_VOCALGORITHM_SIGMOID_X0)), ) if x < self._f16(-50.0): - return self._f16(VOCALGORITHM_SIGMOID_L) + return self._f16(_VOCALGORITHM_SIGMOID_L) elif x > self._f16(50.0): return self._f16(0.0) else: if sample >= self._f16(0.0): shift = self._fix16_div( ( - self._f16(VOCALGORITHM_SIGMOID_L) + self._f16(_VOCALGORITHM_SIGMOID_L) - ( self._fix16_mul( self._f16(5.0), self.params.m_sigmoid_scaled_offset @@ -727,7 +729,7 @@ def _vocalgorithm__sigmoid_scaled__process(self, sample): ) return ( self._fix16_div( - (self._f16(VOCALGORITHM_SIGMOID_L) + shift), + (self._f16(_VOCALGORITHM_SIGMOID_L) + shift), (self._f16(1.0) + self._fix16_exp(x)), ) ) - shift @@ -736,12 +738,12 @@ def _vocalgorithm__sigmoid_scaled__process(self, sample): ( self._fix16_div( self.params.m_sigmoid_scaled_offset, - self._f16(VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT), + self._f16(_VOCALGORITHM_VOC_INDEX_OFFSET_DEFAULT), ) ), ( self._fix16_div( - self._f16(VOCALGORITHM_SIGMOID_L), + self._f16(_VOCALGORITHM_SIGMOID_L), (self._f16(1.0) + self._fix16_exp(x)), ) ), @@ -753,14 +755,14 @@ def _vocalgorithm__adaptive_lowpass__init(self): def _vocalgorithm__adaptive_lowpass__set_parameters(self): self.params.m_adaptive_lowpass_a1 = self._f16( ( - VOCALGORITHM_SAMPLING_INTERVAL - / (VOCALGORITHM_LP_TAU_FAST + VOCALGORITHM_SAMPLING_INTERVAL) + _VOCALGORITHM_SAMPLING_INTERVAL + / (_VOCALGORITHM_LP_TAU_FAST + _VOCALGORITHM_SAMPLING_INTERVAL) ) ) self.params.m_adaptive_lowpass_a2 = self._f16( ( - VOCALGORITHM_SAMPLING_INTERVAL - / (VOCALGORITHM_LP_TAU_SLOW + VOCALGORITHM_SAMPLING_INTERVAL) + _VOCALGORITHM_SAMPLING_INTERVAL + / (_VOCALGORITHM_LP_TAU_SLOW + _VOCALGORITHM_SAMPLING_INTERVAL) ) ) self.params.m_adaptive_lowpass_initialized = 0 @@ -792,16 +794,16 @@ def _vocalgorithm__adaptive_lowpass__process(self, sample): if abs_delta < self._f16(0.0): abs_delta = -abs_delta F1 = self._fix16_exp( - (self._fix16_mul(self._f16(VOCALGORITHM_LP_ALPHA), abs_delta)) + (self._fix16_mul(self._f16(_VOCALGORITHM_LP_ALPHA), abs_delta)) ) tau_a = ( self._fix16_mul( - self._f16((VOCALGORITHM_LP_TAU_SLOW - VOCALGORITHM_LP_TAU_FAST)), F1 + self._f16((_VOCALGORITHM_LP_TAU_SLOW - _VOCALGORITHM_LP_TAU_FAST)), F1 ) - ) + self._f16(VOCALGORITHM_LP_TAU_FAST) + ) + self._f16(_VOCALGORITHM_LP_TAU_FAST) a3 = self._fix16_div( - self._f16(VOCALGORITHM_SAMPLING_INTERVAL), - (self._f16(VOCALGORITHM_SAMPLING_INTERVAL) + tau_a), + self._f16(_VOCALGORITHM_SAMPLING_INTERVAL), + (self._f16(_VOCALGORITHM_SAMPLING_INTERVAL) + tau_a), ) self.params.m_adaptive_lowpass_x3 = ( self._fix16_mul((self._f16(1.0) - a3), self.params.m_adaptive_lowpass_x3) diff --git a/examples/sgp40_indextest.py b/examples/sgp40_indextest.py index e4a93d2..691ebad 100644 --- a/examples/sgp40_indextest.py +++ b/examples/sgp40_indextest.py @@ -18,16 +18,16 @@ humidity = bme280.relative_humidity # For compensated raw gas readings - compensated_raw_gas = sgp.measure_index( + compensated_raw_gas = sgp.measure_raw( temperature=temperature, relative_humidity=humidity ) # For Compensated voc index readings # It may take several minutes for the VOC index to start changing # as it calibrates the baseline readings. - voc_index = sgp.measure_raw(temperature=temperature, relative_humidity=humidity) + voc_index = sgp.measure_index(temperature=temperature, relative_humidity=humidity) - print(compensated_raw_gas) - print(voc_index) + print("Raw Data:", compensated_raw_gas) + print("VOC Index:", voc_index) print("") time.sleep(1) From a4960dfbe7dae97a409e67e141965a564e9a52e2 Mon Sep 17 00:00:00 2001 From: Andrew VanVlack Date: Tue, 31 Aug 2021 08:40:20 -0700 Subject: [PATCH 9/9] fix examples, tested --- README.rst | 4 +++- examples/sgp40_indextest.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 1109538..6d972f2 100644 --- a/README.rst +++ b/README.rst @@ -92,7 +92,9 @@ For humidity compensated raw gas and voc index readings, we'll need a secondary humidity = bme280.relative_humidity # For compensated raw gas readings - compensated_raw_gas = sgp.raw(temperature = temperature, relative_humidity = humidity) + compensated_raw_gas = sgp.measure_raw(temperature = temperature, relative_humidity = humidity) + + time.sleep(1) # For Compensated voc index readings voc_index = sgp.measure_index(temperature = temperature, relative_humidity = humidity) diff --git a/examples/sgp40_indextest.py b/examples/sgp40_indextest.py index 691ebad..5d9dd4d 100644 --- a/examples/sgp40_indextest.py +++ b/examples/sgp40_indextest.py @@ -22,6 +22,8 @@ temperature=temperature, relative_humidity=humidity ) + time.sleep(1) + # For Compensated voc index readings # It may take several minutes for the VOC index to start changing # as it calibrates the baseline readings.