import math from ScEpTIC.emulator.energy import energy_utils from ScEpTIC.emulator.energy.mcu.options import MCUClockCycleAction, ADCPowerState from ScEpTIC.emulator.energy.options import OpModeName from ScEpTIC.emulator.energy.voltage_drawner import VoltageDrawner class SGP40(VoltageDrawner): """ Energy model of a SGP40 VOC sensor https://sensirion.com/media/documents/296373BB/6203C5DF/Sensirion_Gas_Sensors_Datasheet_SGP40.pdf """ STATES = ['measuring', 'off', 'idle'] datasheet_e = { 'idle': {'V': 3.3, 'I_min': '34u', 'I_max': '105u'}, 'measuring': {'V': 3.3, 'I_min': '2.6m', 'I_max': '3.0m'}, } datasheet_t = { 'idle': {'min': '0.4m', 'max': '0.6m'}, 'measuring': '30m' } def __init__(self): self.state = 'off' self.resistance = {} self.timings = { 'idle': (energy_utils.str_to_float(self.datasheet_t['idle']['min']) + energy_utils.str_to_float(self.datasheet_t['idle']['max'])) / 2.0, 'measuring': energy_utils.str_to_float(self.datasheet_t['measuring']), } for state, s_data in self.datasheet_e.items(): avg_i = (energy_utils.str_to_float(s_data['I_min']) + energy_utils.str_to_float(s_data['I_max'])) / 2.0 self.resistance[state] = s_data['V'] / avg_i self.system_model = None def attach_system_model(self, system_model): self.system_model = system_model def set_state(self, state): """ Set state (off, on) """ if state not in self.STATES: raise Exception('SGP40 - Invalid state') self.state = state def simulate_set_state(self, state): ticks = 0 # activate sensor ticks += self.system_model.run_step(MCUClockCycleAction.NO_MEMORY_ACCESS, OpModeName.SIMULATE_SENSOR) self.set_state(state) if state in self.timings: wait_time = self.timings[state] # enter LPM and wait for sensor to be in designed state ticks += self.system_model.run_step(MCUClockCycleAction.LPM_ENTER, OpModeName.LPM_ENTER) lpm_start_time = self.system_model.get_simulation_time() lpm_end_time = lpm_start_time + wait_time while self.system_model.get_simulation_time() < lpm_end_time: ticks += self.system_model.run_step(MCUClockCycleAction.LPM_NOP, OpModeName.SIMULATE_SENSOR) ticks += self.system_model.run_step(MCUClockCycleAction.LPM_EXIT, OpModeName.LPM_EXIT) return ticks def simulate_read(self): self.system_model.enable_lpm_precise_ticks() ticks = self.simulate_set_state('idle') ticks += self.simulate_set_state('measuring') # read data ticks += self.system_model.run_step(MCUClockCycleAction.NO_MEMORY_ACCESS, OpModeName.SIMULATE_SENSOR) ticks += self.simulate_set_state('off') self.system_model.disable_lpm_precise_ticks() return ticks def get_drained_energy(self, t): """ Calculates the energy consumed by the component. :param t: elapsed time :return: the consumed energy """ if self.state == 'off': return 0.0 voltage = self.voltage_source.get_voltage() energy_from_R = energy_utils.energy_from_R_t(voltage, self.resistance[self.state], t) return energy_from_R