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