245 lines
8.7 KiB
Python
245 lines
8.7 KiB
Python
from ScEpTIC.emulator.energy import energy_utils
|
|
from ScEpTIC.emulator.energy.voltage_source import VoltageSource
|
|
from ScEpTIC.exceptions import ConfigurationException
|
|
|
|
|
|
class EnergyBufferModel(VoltageSource):
|
|
"""
|
|
Model of an energy buffer
|
|
"""
|
|
|
|
def __init__(self, voltage_upper_bound, energy_upper_bound):
|
|
"""
|
|
:param voltage_upper_bound: maximum voltage of the energy buffer (string with unit multiplier or float)
|
|
:param energy_upper_bound: maximum energy level of the energy buffer (string with unit multiplier or float)
|
|
"""
|
|
|
|
# current data
|
|
self._current_voltage = 0.0
|
|
self._current_energy = 0.0
|
|
|
|
# lower bounds
|
|
self._voltage_lower_bound = 0.0
|
|
self._energy_lower_bound = 0.0
|
|
|
|
# upper bounds
|
|
if voltage_upper_bound is None and energy_upper_bound is None:
|
|
raise ConfigurationException(f"{self.__class__.__name__} configuration error: voltage_upper_bound or energy_upper_bound must be specified!")
|
|
|
|
if voltage_upper_bound is not None:
|
|
self._voltage_upper_bound = energy_utils.str_to_float(voltage_upper_bound)
|
|
self._energy_upper_bound = self._calculate_energy_from_voltage(self._voltage_upper_bound)
|
|
else:
|
|
self._energy_upper_bound = energy_utils.str_to_float(energy_upper_bound)
|
|
self._voltage_upper_bound = self._calculate_voltage_from_energy(self._energy_upper_bound)
|
|
|
|
self.discharge_enabled = True
|
|
|
|
|
|
def get_max_voltage(self):
|
|
"""
|
|
:return: the maximum voltage supported by the energy buffer
|
|
"""
|
|
return self._voltage_upper_bound
|
|
|
|
|
|
def get_voltage(self):
|
|
"""
|
|
:return: current voltage level
|
|
"""
|
|
return self._current_voltage
|
|
|
|
|
|
def get_energy(self):
|
|
"""
|
|
:return: current energy level
|
|
"""
|
|
return self._current_energy
|
|
|
|
|
|
def enable_discharge(self):
|
|
"""
|
|
Enable the energy buffer discharge
|
|
"""
|
|
self.discharge_enabled = True
|
|
|
|
|
|
def disable_discharge(self):
|
|
"""
|
|
Enable the energy buffer discharge
|
|
"""
|
|
self.discharge_enabled = False
|
|
|
|
|
|
def get_usable_voltage(self):
|
|
"""
|
|
:return: The difference between current voltage and the voltage lower bound
|
|
"""
|
|
return self._current_voltage - self._voltage_lower_bound
|
|
|
|
|
|
def get_usable_energy(self):
|
|
"""
|
|
:return: The remaining usable energy (i.e., the one from current voltage level to the voltage lower bound)
|
|
"""
|
|
return self._current_energy - self._energy_lower_bound
|
|
|
|
|
|
def set_voltage_lower_bound(self, v_min):
|
|
"""
|
|
Sets the voltage lower bound to v_min and updates the energy lower bound.
|
|
The voltage lower bound usually corresponds to the voltage at which the MCU shuts down and specifies the level of the energy buffer that cannot be used for useful computation
|
|
:param v_min: minimum voltage (string with multiplier or float)
|
|
"""
|
|
self._voltage_lower_bound = energy_utils.str_to_float(v_min)
|
|
self._energy_lower_bound = self._calculate_energy_from_voltage(self._voltage_lower_bound)
|
|
|
|
|
|
def set_energy_lower_bound(self, e_min):
|
|
"""
|
|
Sets the energy lower bound to e_min and updates the voltage lower bound.
|
|
The energy lower bound corresponds to the energy that cannot be used for useful computation due to a voltage level below the MCU minimum operating voltage
|
|
:param e_min: minimum energy (string with multiplier or float)
|
|
"""
|
|
self._energy_lower_bound = energy_utils.str_to_float(e_min)
|
|
self._voltage_lower_bound = self._calculate_voltage_from_energy(self._energy_lower_bound)
|
|
|
|
|
|
def increment_voltage(self, v):
|
|
"""
|
|
Increments the energy buffer voltage by v and updates the energy level
|
|
:param v: voltage to increment
|
|
"""
|
|
new_voltage = self._current_voltage + float(v)
|
|
self.set_voltage(new_voltage)
|
|
|
|
|
|
def set_voltage(self, new_voltage):
|
|
"""
|
|
Sets the energy buffer voltage to v and updates the energy level
|
|
Note: if new_voltage > voltage_upper_bound, new_voltage = voltage_upper_bound
|
|
"""
|
|
# enforce upper bound and min 0V
|
|
self._current_voltage = max(0.0, min(float(new_voltage), self._voltage_upper_bound))
|
|
|
|
self._update_energy_from_voltage()
|
|
|
|
|
|
def execute_full_refill(self):
|
|
"""
|
|
Sets the energy buffer to the maximum level
|
|
"""
|
|
self.set_voltage(self._voltage_upper_bound)
|
|
|
|
|
|
def _update_voltage_from_energy(self):
|
|
"""
|
|
Updates the voltage with respect to the current energy level
|
|
"""
|
|
self._current_voltage = self._calculate_voltage_from_energy(self._current_energy)
|
|
|
|
|
|
def increment_energy(self, e):
|
|
"""
|
|
Increments the energy buffer energy by e and updates the voltage
|
|
:param e: energy to increment
|
|
"""
|
|
new_energy = self._current_energy + float(e)
|
|
self.set_energy(new_energy)
|
|
|
|
def set_energy(self, new_energy):
|
|
"""
|
|
Sets the energy buffer energy to e and updates the voltage
|
|
Note: if new_energy > energy_upper_bound, new_energy = energy_upper_bound
|
|
"""
|
|
# enforce upper bound and min 0J
|
|
self._current_energy = max(0.0, min(float(new_energy), self._energy_upper_bound))
|
|
|
|
self._update_voltage_from_energy()
|
|
|
|
def get_energy_charge(self, v_supply, t_elapsed, R):
|
|
"""
|
|
Returns the amount of energy that the energy buffer would recharge from v_supply
|
|
:param v_supply: voltage of the energy source
|
|
:param t_elapsed: time elapsed
|
|
:param R: circuit equivalent resistance
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement get_energy_charge()')
|
|
|
|
|
|
def get_size(self):
|
|
"""
|
|
:return: the energy buffer size
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement get_size()')
|
|
|
|
|
|
def get_nominal_size(self):
|
|
"""
|
|
:return: the energy buffer size in textual representation
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement get_nominal_size()')
|
|
|
|
|
|
def update_size(self, size):
|
|
"""
|
|
Updates the energy buffer size
|
|
:param size: new size
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement update_size()')
|
|
|
|
|
|
def update(self, voltage_intervals, e_drawn, charge_r, elapsed_time, preserve_voltage=False):
|
|
"""
|
|
Updates the voltage of the energy buffer (discharge / charge cycles)
|
|
Note: if discharge is not enabled, the energy buffer level remains constant
|
|
:param voltage_intervals: list of (voltage, time)
|
|
:param e_drawn: energy drawn
|
|
:param charge_r: equivalent resistance of the system when the energy buffer is going to be charged
|
|
:param elapsed_time: elapsed time
|
|
:param preserve_voltage: do not update the voltage
|
|
:return: the energy harvested
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement update()')
|
|
|
|
|
|
def calculate_recharge_time_to_voltage(self, v_target, v_supply, eq_r):
|
|
"""
|
|
Calculates the time required to reach the voltage v_target
|
|
:param v_target: the target voltage
|
|
:param v_supply: the voltage of the power supply
|
|
:param eq_r: the equivalent resistance of the circuit
|
|
:return: the time required to recharge the energy buffer to v_target
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement calculate_recharge_time_to_voltage()')
|
|
|
|
|
|
def calculate_recharge_energy_to_voltage(self, v_target):
|
|
"""
|
|
Calculates the energy required to reach the voltage v_target
|
|
:param v_target: the target voltage
|
|
:return: the energy required to recharge the energy buffer to v_target
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement calcualte_recharged_energy_to_voltage()')
|
|
|
|
|
|
def _update_energy_from_voltage(self):
|
|
"""
|
|
Updates the energy level with respect to the current voltage
|
|
"""
|
|
self._current_energy = self._calculate_energy_from_voltage(self._current_voltage)
|
|
|
|
|
|
def _calculate_energy_from_voltage(self, v):
|
|
"""
|
|
:return: the energy level at the voltage v
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement _calculate_energy_from_voltage()')
|
|
|
|
|
|
def _calculate_voltage_from_energy(self, e):
|
|
"""
|
|
:return: the voltage at the energy level e
|
|
"""
|
|
raise NotImplementedError(f'{self.__class__.__name__} must implement _calculate_voltage_from_energy()')
|