2026-07-10 10:38:57 +02:00

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