104 lines
3.3 KiB
Python
104 lines
3.3 KiB
Python
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
|
|
|