import sys import os import xml.etree.cElementTree as ET from misc import * def getVoltage(productname): if productname == "shamu": return 4.2 return 1 def getCoreNum(productname): if productname == "shamu": return 4 return 2 class Model(): ''' The energy model module ''' def __init__(self, isDebugging=False, unit="mW"): self.freqs = [] self.cpu_single_core = {} self.cpu_multi_core = {} ''' cpu format: { cpu num: { freq: [active current, idle current], ...} } ''' self.net_wifi = {} self.net_LTE = {} self.net_3G = {} ''' net format: { 'prom': { 'index': [rssi, current, length], ...}, 'active': { rssi: [rx current, tx current, rx xput, tx xput]} 'tail': { 'index': [rssi, current, length], ...}, } ''' self.wifi_min_spike_interval = 0.03 self.wifi_timeout = 0.21 self.voltage = 1 self.unit = unit self.DEBUG = isDebugging self.logger = EmptyLogger( "Model", isDebugging=self.DEBUG, printout=True) if 'A' in self.unit: tmp = self.unit.replace('A', 'W') self.logger.info( "Will use power instead: {0} -> {1}".format(tmp, unit)) self.unit = tmp if 'W' in self.unit: self.using_power = True else: self.using_power = False def load(self, productname, dir="./models/"): self.voltage = getVoltage(productname) filepath = "{0}/{1}.xml".format(dir, productname) if not os.path.isfile(filepath): self.logger.error("File {0} does not exist.".format(filepath)) sys.exit(-1) tree = ET.parse(filepath) root = tree.getroot() cpumodel = root.find("cpumodel") self.parseFreqs(cpumodel.find("freqs")) cores = cpumodel.find("cores") self.parseCPUSingleCore(cores) self.parseCPUMultiCore(cores) netmodel = root.find("netmodel") for net in netmodel.findall("net"): if net.attrib['id'] == 'WIFI': self.parseNet(net, self.net_wifi) elif net.attrib['id'] == 'LTE': self.parseNet(net, self.net_LTE) elif net.attrib['id'] == '3G': self.parseNet(net, self.net_3G) def parseNet(self, node, net_node): prom = node.find("prom") if prom is None or prom.attrib['numstates'] == '0': net_node['prom'] = None else: net_node['prom'] = {} for pst in prom.findall("promstate"): net_node['prom'][pst.attrib['index']] = \ [int(pst.attrib['index']), float(pst.attrib['prompwr']), float(pst.attrib['promlen'])] active = node.find("active") if active is None: net_node['active'] = None else: net_node['active'] = {} for ast in active.findall("activestate"): net_node['active'][int(ast.attrib['rssi'])] = \ [float(ast.attrib['rxpwr']), float(ast.attrib['txpwr']), float(ast.attrib['rxxput']), float(ast.attrib['txxput'])] tail = node.find("tail") if tail is None or tail.attrib['numstates'] == '0': net_node['tail'] = None else: net_node['tail'] = {} for tst in tail.findall("tailstate"): net_node['tail'][tst.attrib['index']] = \ [int(tst.attrib['index']), float(tst.attrib['tailpwr']), float(tst.attrib['taillen'])] def parseCPUMultiCore(self, node): for core in node.findall("core"): if core.attrib['mode'] == 'multicore': myid = int(core.attrib['id']) if myid in self.cpu_multi_core: myfreq = int(core.attrib['freq']) self.cpu_multi_core[myid][myfreq] = \ [float(core.attrib['active']), float(core.attrib['idle'])] else: self.cpu_multi_core[myid] = \ {int(core.attrib['freq']): [float(core.attrib['active']), float(core.attrib['idle'])]} def parseCPUSingleCore(self, node): for core in node.findall("core"): if core.attrib['mode'] == 'singlecore': myid = int(core.attrib['id']) if myid in self.cpu_single_core: myfreq = int(core.attrib['freq']) self.cpu_single_core[myid][myfreq] = \ [float(core.attrib['active']), float(core.attrib['idle'])] else: self.cpu_single_core[myid] = \ {int(core.attrib['freq']): [float(core.attrib['active']), float(core.attrib['idle'])]} def parseFreqs(self, node): for freq in node.findall("freq"): self.freqs.append(int(freq.attrib['val'])) def get_final_energy(self, current, time): ''' @param current: mA @param time: s @return defined energy with unit conversion ''' # self.logger.debug("current: {0}, time_diff: {1:.8f}".format( # current, time)) if 'W' in self.unit: tmp = current * self.voltage elif 'J' in self.unit: tmp = current * self.voltage * time else: self.logger.error( "Unit {0} not supported!".format(self.unit)) sys.exit(-1) # self.logger.debug("tmp: {0}".format(tmp, current * self.voltage)) if 'm' == self.unit[0]: return tmp else: return tmp / 1000.0 def get_cpu_energy(self, time_diff, freq, util): ''' @param freq: list of cpu frequencies @param util: list of cpu utilization @return: energy in desired unit, default is mW ''' if len(freq) != len(util) or len(freq) < 1: self.logger.error("freq & util have different length!") sys.exit(-1) current = 0 if len(freq) > 1: db = self.cpu_multi_core else: db = self.cpu_single_core for i in xrange(len(freq)): if freq[i] <= 0: continue if freq[i] not in db[i]: minDiff = float("inf") myJ = None for j in xrange(len(self.freqs)): tmp = abs(self.freqs[j] - freq[i]) if tmp < minDiff: minDiff = tmp myJ = j closestFreq = self.freqs[j] self.logger.debug("Freq outlier: {0}. ".format(freq[i]) + "Use {0} instead.".format(closestFreq)) freq[i] = closestFreq active_current = db[i][freq[i]][0] idle_current = db[i][freq[i]][1] current += util[i] * active_current + (1 - util[i]) * idle_current # derive power or energy result = self.get_final_energy(current, time_diff) self.logger.debug( "duration: {0:.4f}s, cpu_energy: {1:.4f}{2}".format( time_diff, result, self.unit)) return result def get_lte_prom_energy(self, time_diff, rssi, isTX=True): self.logger.error('TODO: not implemented yet') return None def get_lte_tail_energy(self, time_diff, rssi, isTX=True): self.logger.error('TODO: not implemented yet') return None def get_lte_active_energy(self, time_diff, rssi, isTX=True): self.logger.error('TODO: not implemented yet') return None def get_wifi_active_energy(self, time_diff, rssi, isTX=True): table_rssi = sorted(self.net_wifi['active'].keys(), reverse=True) if isTX: currentIdx = 1 else: currentIdx = 0 # fetch current current = None if rssi >= table_rssi[0]: current = self.net_wifi['active'][table_rssi[0]][currentIdx] elif rssi < table_rssi[-1]: current = self.net_wifi['active'][table_rssi[-1]][currentIdx] else: for i in xrange(1, len(table_rssi)): if rssi >= table_rssi[i]: endp = self.net_wifi['active'][table_rssi[i-1]][currentIdx] startp = self.net_wifi['active'][table_rssi[i]][currentIdx] rssi_diff = table_rssi[i - 1] - table_rssi[i] current = (endp + 1.0 * (endp - startp) * (rssi - table_rssi[i-1]) / rssi_diff) break if current is None: self.logger.error("Current {0} is nothing!".format(current)) sys.exit(-1) # derive power or energy result = self.get_final_energy(current, time_diff) # self.logger.debug( # "cpu_energy: {0:.4f}{1}".format(result, self.unit)) return result def get_wifi_tail_energy(self, time_diff): current = self.net_wifi['tail']['0'][1] result = self.get_final_energy(current, time_diff) # self.logger.debug( # "cpu_energy: {0:.4f}{1}".format(result, self.unit)) return result if __name__ == "__main__": print "Usage: from model import *"