127 lines
4.0 KiB
Python
127 lines
4.0 KiB
Python
import math
|
|
|
|
from ScEpTIC.emulator.energy import energy_utils
|
|
from ScEpTIC.emulator.energy.mcu.options import MCUClockCycleAction
|
|
from ScEpTIC.emulator.energy.options import OpModeName
|
|
from ScEpTIC.emulator.energy.voltage_drawner import VoltageDrawner
|
|
|
|
class CC1101Model(VoltageDrawner):
|
|
"""
|
|
Energy model of the CC1101 radio
|
|
"""
|
|
STATES = ['active', 'inactive', 'off']
|
|
HEADER_BYTES = 10
|
|
PACKET_SIZE = 64
|
|
BAUD_RATE = 500000
|
|
|
|
# 10dBm @ 433MHz
|
|
data = {
|
|
'active': {'I': '29.2m', 'V': 1.8},
|
|
'inactive': {'I': '200n', 'V': 1.8}
|
|
}
|
|
|
|
t_wakeup = {
|
|
'off': {'inactive': '150u', 'active': '390u'},
|
|
'inactive': {'active': '240u'},
|
|
}
|
|
|
|
def __init__(self):
|
|
self.state = 'off'
|
|
self.resistance = {
|
|
'inactive': energy_utils.equivalent_resistance(self.data['inactive']['V'], self.data['inactive']['I']),
|
|
'active': energy_utils.equivalent_resistance(self.data['active']['V'], self.data['active']['I']),
|
|
}
|
|
self.system_model = None
|
|
|
|
def attach_system_model(self, system_model):
|
|
self.system_model = system_model
|
|
|
|
def simulate_set_state(self, state):
|
|
ticks = 0
|
|
cycles = self.get_wait_cycles(state)
|
|
|
|
for _ in range(cycles):
|
|
ticks += self.system_model.run_step(MCUClockCycleAction.NO_MEMORY_ACCESS, OpModeName.SIMULATE_RADIO)
|
|
|
|
self.set_state(state)
|
|
|
|
return ticks
|
|
|
|
def simulate_transmit(self, bytes, word_bytes=2):
|
|
#print(f"Transmitting {bytes} bytes")
|
|
ticks = self.simulate_set_state('active')
|
|
|
|
# first word to transmit
|
|
first_packet_words = min(math.ceil(self.PACKET_SIZE / word_bytes), math.ceil(bytes / word_bytes))
|
|
|
|
for _ in range(first_packet_words):
|
|
ticks += self.system_model.run_step(MCUClockCycleAction.NO_MEMORY_ACCESS, OpModeName.SIMULATE_RADIO)
|
|
|
|
# transmission cycles
|
|
register_ops = math.ceil(bytes / word_bytes) - first_packet_words
|
|
transmission_cycles = self.get_transmission_cycles(bytes)
|
|
total_cycles = max(register_ops, transmission_cycles)
|
|
|
|
# transmit
|
|
for _ in range(total_cycles):
|
|
ticks += self.system_model.run_step(MCUClockCycleAction.NO_MEMORY_ACCESS, OpModeName.SIMULATE_RADIO)
|
|
|
|
ticks += self.simulate_set_state('off')
|
|
|
|
return ticks
|
|
|
|
|
|
def set_state(self, state):
|
|
"""
|
|
Set radio state (off, inactive, active)
|
|
"""
|
|
if state not in self.STATES:
|
|
raise Exception('CC1101 - Invalid state')
|
|
|
|
self.state = state
|
|
|
|
def get_transmission_cycles(self, bytes):
|
|
"""
|
|
Returns transmission cycles
|
|
"""
|
|
n_packets = math.ceil(bytes / self.PACKET_SIZE)
|
|
total_size = n_packets * self.HEADER_BYTES + bytes
|
|
transmission_time = total_size / self.BAUD_RATE
|
|
return math.ceil(transmission_time * self.system_model.mcu.get_frequency())
|
|
|
|
def get_wakeup_time(self, state):
|
|
"""
|
|
Returns the time required to get from self.state to state
|
|
"""
|
|
try:
|
|
return energy_utils.str_to_float(self.t_wakeup[self.state][state])
|
|
except KeyError:
|
|
return 0.0
|
|
|
|
def get_wait_cycles(self, state):
|
|
"""
|
|
Returns the MCU wait cycles required to get from self.state to state
|
|
"""
|
|
if state not in self.STATES:
|
|
raise Exception('CC1101 - Invalid state')
|
|
|
|
if state == 'active' and (self.state == 'off' or state == 'inactive'):
|
|
return math.ceil(self.get_wakeup_time(state) * self.system_model.mcu.get_frequency())
|
|
|
|
return 0
|
|
|
|
def get_drained_energy(self, t):
|
|
"""
|
|
Calculates the energy consumed by the component.
|
|
:param t: elapsed time
|
|
:return: the consumed energy
|
|
"""
|
|
voltage = self.voltage_source.get_voltage()
|
|
|
|
if self.state == 'off':
|
|
return 0.0
|
|
|
|
energy_from_R = energy_utils.energy_from_R_t(voltage, self.resistance[self.state], t)
|
|
|
|
return energy_from_R
|