def defineName(self):
        self.partObject = Partition(self)
        partModeList = {}
        for a in self.partObject.supportedMethods:
            partModeList[a.partitionType] = a

        self.diffObject = DifferenceMode()
        diffModeList = {}
        for a in self.diffObject.supportedMethods:
            diffModeList[a.differenceType] = a

        self.addGroup("generatePartitions")
        self.addGroup("generatePartitionStats")
        self.addGroup("generatePartitionDiffs")
        self.addGroup("displayPartitionDiffs")

        self.getParams().addChildren([
              {'name':'Comparison Mode', 'key':'diffmode', 'type':'list', 'values':diffModeList, 'value':self.diffObject.diffMethodClass, 'action':lambda _: self.updateScript()},
              {'name':'Partition Mode', 'key':'partmode', 'type':'list', 'values':partModeList, 'value':self.partObject.partMethodClass, 'action':lambda _: self.updateScript()},
              {'name':'Display', 'type':'action', 'action':lambda _:self.runAction()},

              {'name':'Auto-Save Data to Project', 'key':'part-saveints', 'type':'bool', 'value':False, 'action':lambda _: self.updateScript()},
              {'name':'Auto-Load Data from Project', 'key':'part-loadints', 'type':'bool', 'value':False, 'action':lambda _: self.updateScript()},

              {'name':'Points of Interest', 'key':'poi', 'type':'group', 'children':[
                 {'name':'Selection Mode', 'type':'list', 'values':{'Max N Points/Subkey':'maxn'}, 'value':'maxn'},
                 {'name':'Point Range', 'key':'poi-pointrng', 'type':'range', 'limits':(0, 0), 'default':(0, 0), 'value':(0, 0), 'action':lambda _: self.updatePOI()},
                 {'name':'Num POI/Subkey', 'key':'poi-nummax', 'type':'int', 'limits':(1, 200), 'value':1, 'action':lambda _: self.updatePOI()},
                 {'name':'Min Spacing between POI', 'key':'poi-minspace', 'type':'int', 'limits':(1, 100E6), 'value':1, 'step':100, 'action':lambda _: self.updatePOI()},
                 {'name':'Hill detection', 'key':'poi-hilldet', 'type':'bool', 'value':True, 'tip':"Extend the bounds downhill for each peak", 'action':lambda _: self.updatePOI()},
                 # {'name':'Threshold', 'key':'threshold', 'type':'int', 'visible':False},
                 {'name':'Open POI Table', 'type':'action', 'action':lambda _: self.poiDock.show()},
              ]},
        ])
    def defineName(self):
        self.name = 'Partition Comparison'

        self.partObject = Partition(self)
        partModeList = {}
        for a in self.partObject.supportedMethods:
            partModeList[a.partitionType] = a

        self.diffObject = DifferenceMode()
        diffModeList = {}
        for a in self.diffObject.supportedMethods:
            diffModeList[a.differenceType] = a

        self.addGroup("generatePartitions")
        self.addGroup("generatePartitionStats")
        self.addGroup("generatePartitionDiffs")
        self.addGroup("displayPartitionDiffs")

        self.poi = POI(self)
        self.poidock = self.parent.addDock(self.poi, "Points of Interest", area=Qt.RightDockWidgetArea)
        self.poidock.hide()

        self.params = [
              {'name':'Comparison Mode', 'key':'diffmode', 'type':'list', 'values':diffModeList, 'value':self.diffObject.diffMethodClass, 'set':self.updateScript},
              {'name':'Partition Mode', 'key':'partmode', 'type':'list', 'values':partModeList, 'value':self.partObject.partMethodClass, 'set':self.updateScript},
              {'name':'Display', 'type':'action', 'action':self.runAction},

              {'name':'Auto-Save Data to Project', 'key':'part-saveints', 'type':'bool', 'value':False, 'set':self.updateScript},
              {'name':'Auto-Load Data from Project', 'key':'part-loadints', 'type':'bool', 'value':False, 'set':self.updateScript},

              {'name':'Points of Interest', 'key':'poi', 'type':'group', 'children':[
                 {'name':'Selection Mode', 'type':'list', 'values':{'Max N Points/Subkey':'maxn'}, 'value':'maxn'},
                 {'name':'Point Range', 'key':'poi-pointrng', 'type':'range', 'limits':(0, 0), 'set':self.updatePOI},
                 {'name':'Num POI/Subkey', 'key':'poi-nummax', 'type':'int', 'limits':(1, 200), 'value':1, 'set':self.updatePOI},
                 {'name':'Min Spacing between POI', 'key':'poi-minspace', 'type':'int', 'limits':(1, 100E6), 'value':1, 'step':100, 'set':self.updatePOI},
                 # {'name':'Threshold', 'key':'threshold', 'type':'int', 'visible':False},
                 {'name':'Open POI Table', 'type':'action', 'action':self.poidock.show},
              ]},
             ]

        self.updateScript()
    def addTraces(self, tracedata, tracerange, progressBar=None, pointRange=None, attack=None):

        # NOTE: this is for calling substuff, not for legacy code here!
        if pointRange is None: pointRange = (0,-1)

        #---

        roundwidth = 0

        if attack is not None:
            round528 = attack.keeloq_round528
            roundwidth = attack.keeloq_roundwidth
            # print "Using KEELOQ timing values from attack: %d %d" % (round528, roundwidth)

        if self.config['roundtiming'] != True:
            roundwidth = 0

        #--- prepare environment

        partClass  = keeloqPartition_CiphertextMSB

        partObject = Partition()
        partObject.setPartMethod(partClass)
        partObject.setTraceSource(tracedata)

        partDisplay = PartitionDisplay(None)
        partDisplay._traces = tracedata

        if progressBar:
            progressBar.setMaximum(64 + 1)

        #--- loop for all unknown keybits

        keystream = ""

        for bit in range(0, 64):

            #--- Analize all possible values (0 and 1)

            guessNum   = 2
            guessDiffs = [] * guessNum

            #--- Partition and find differences using T-Test

            if bit==48:
                print "\nTODO: Sorry for becoming slower, the cipher implementation has not been optimized yet.\n"

            for guessBit in range (0, guessNum):

                # Probe known keystream + guessbit + dummybit
                #
                # The dummybit is necessary because of a property of Keeloq.  The last bit of the stream is
                # mixed into the attacked data bit with XOR.  This results in exactly complementary partitioning
                # for values 0 vs 1, making it impossible to pick a "winner".  Therefore we append the dummybit
                # at this position, so that guessbit is the last input that actually makes a measureable difference.

                partData  = partObject.generatePartitions(partitionClass=None,
                                         saveFile=False, loadFile=False, tRange=tracerange,
                                         partitionConfig={"keystream":"%s %d 0" % (keystream, guessBit)})

                partStats = partDisplay.generatePartitionStats(partitionData={"partclass":partClass, "partdata":partData},
                                         saveFile=False, loadFile=False,  tRange=tracerange, pointRange=pointRange)

                partDiffs = partDisplay.generatePartitionDiffs(DifferenceModeTTest,
                                         statsInfo={"partclass":partClass, "stats":partStats},
                                         saveFile=False, loadFile=False, tRange=tracerange)

                guessDiffs.append(partDiffs[0])
# TODO: since we calculate 0 and 1 anyway we should call the partition mode just once and use a mode that produces 2 keys (0,1)

            #--- Determine range in which high correlation is expected

            # TODO: for strict round timing, it is faster to do partStats only for the desired pointRange

            if roundwidth <= 0: # look in whole pointRange
                lookStart = 0
                lookStop  = len(guessDiffs[0])

            else: # look only at expected position (enforce round timing)
                round      = 528 - 32 - (bit+1)
                roundStart = round528 - ((528-round) * roundwidth)
                roundStop  = roundStart + roundwidth

                lookStart  = roundStart - pointRange[0]
                lookStop   = roundStop  - pointRange[0]

                # print "Round=%d Pos=%d-%d look=%d-%d" % (round, roundStart, roundStop, lookStart, lookStop)

                if (lookStart < 0) or (lookStop > len(guessDiffs[0])):
                    print "Enforced round timing range (%d,%d) is outside of analyzed pointRange(%d,%d). "\
                          "Can't detect key bit %d. Result so far: %s" %\
                                   (roundStart, roundStop, pointRange[0], pointRange[1], bit, keystream)
                    return

            #--- Detect highest correlation
 
            # print "Looking at range %d-%d of %d" % (lookStart, lookStop, len(guessDiffs[0]))
            # if bit == 0:
            #     print guessDiffs[0][lookStart:lookStop]
            #     print guessDiffs[1][lookStart:lookStop]

            winBit = 0 if (np.nanmax(guessDiffs[0][lookStart:lookStop]) > np.nanmax(guessDiffs[1][lookStart:lookStop])) else 1
            badBit = winBit ^ 1

            winOffset     = np.argmax(guessDiffs[winBit][lookStart:lookStop]) + lookStart
            badOffset     = np.argmax(guessDiffs[badBit][lookStart:lookStop]) + lookStart

            winDiff       =           guessDiffs[winBit][winOffset]
            badDiff       =           guessDiffs[badBit][badOffset]
            winRatioSpot  = winDiff / guessDiffs[badBit][winOffset]
            winRatioTrace = winDiff / badDiff

            keystream = "%s%d" % (keystream, winBit)

            #--- report

            print "Analysis of keybit #%02d (of 64): %d (diff=%f spot=%f trace=%f pos=%d) vs %d (diff=%f pos=%d)" %\
                                              (bit,
                                               winBit, winDiff, winRatioSpot, winRatioTrace, winOffset + pointRange[0],
                                               badBit, badDiff,                              badOffset + pointRange[0])

            if progressBar:
                progressBar.setText("Attacking key bits (%d of 64)" % bit)
                progressBar.setStatusMask("key=%s\nkeystream=%s" % (keeloqFormatKeystream(keystream), keystream))
                progressBar.updateStatus(bit)
                if progressBar.wasAborted():
                    print "Aborting. Result so far: %s" % keystream
                    return

        #---

        keyStr = "%s%s" % (keystream[16:64], keystream[0:16])
        keyInt = int(keyStr, 2)

        print "Keystream = %s" % keystream
        print "Key: %016x" % keyInt

        if progressBar:
            progressBar.setText("Attack finished")
            progressBar.setStatusMask("Key: %016x" % keyInt)
            progressBar.updateStatus(64)

        #--- Decrypt the data associated with all traces

        progress = 64

        if True:
            start = tracerange[0]
            end   = tracerange[1]
            if end == -1:
                end = tracedata.numTraces()
            tnum = start
            while tnum < end:
                t = tracedata.getSegment(tnum)
                # Discover where this trace starts & ends
                tmapstart = t.mappedRange[0]
                tmapend = t.mappedRange[1]

                if progressBar:
                    increment = tmapend + 1 - tmapstart
                    progressBar.setMaximum(progress + increment + 1)
                    progressBar.setText("Decrypting traces")

                for tnum in range(tmapstart, tmapend + 1):
                    if progressBar:
                        progressBar.updateStatus(progress)
                        progress += 1
                        if progressBar.wasAborted():
                            return
                    textout = t.getTextout(tnum - tmapstart)
                    if (textout is not None) and (len(textout) >= 4):
                        data = (textout[0] << 24) | (textout[1] << 16) | (textout[2] << 8) | (textout[3] << 0)
                        decrypt = keeloqDecrypt(data, keyInt)
                        print "Trace %0d: %08x -> %08x" % (tnum - tmapstart, data, decrypt)

                tnum = tmapend + 1

        print "Key: %016x" % keyInt

        #--- cleanup

        if progressBar:
            progressBar.setMaximum(100)
            progressBar.updateStatus(100)
    def defineName(self):
        self.partObject = Partition()
        partModeList = {}
        for a in self.partObject.supportedMethods:
            partModeList[a.partitionType] = a

        self.diffObject = DifferenceMode()
        diffModeList = {}
        for a in self.diffObject.supportedMethods:
            diffModeList[a.differenceType] = a

        self.addGroup("generatePartitions")
        self.addGroup("generatePartitionStats")
        self.addGroup("generatePartitionDiffs")
        self.addGroup("displayPartitionDiffs")

        self.getParams().addChildren([
            {
                'name': 'Comparison Mode',
                'key': 'diffmode',
                'type': 'list',
                'values': diffModeList,
                'value': self.diffObject.diffMethodClass,
                'action': lambda _: self.updateScript()
            },
            {
                'name': 'Partition Mode',
                'key': 'partmode',
                'type': 'list',
                'values': partModeList,
                'value': self.partObject.partMethodClass,
                'action': lambda _: self.updateScript()
            },
            {
                'name': 'Display',
                'type': 'action',
                'action': lambda _: self.runAction()
            },
            {
                'name': 'Auto-Save Data to Project',
                'key': 'part-saveints',
                'type': 'bool',
                'value': False,
                'action': lambda _: self.updateScript()
            },
            {
                'name': 'Auto-Load Data from Project',
                'key': 'part-loadints',
                'type': 'bool',
                'value': False,
                'action': lambda _: self.updateScript()
            },
            {
                'name':
                'Points of Interest',
                'key':
                'poi',
                'type':
                'group',
                'children': [
                    {
                        'name': 'Selection Mode',
                        'type': 'list',
                        'values': {
                            'Max N Points/Subkey': 'maxn'
                        },
                        'value': 'maxn'
                    },
                    {
                        'name': 'Point Range',
                        'key': 'poi-pointrng',
                        'type': 'range',
                        'limits': (0, 0),
                        'default': (0, 0),
                        'value': (0, 0),
                        'action': lambda _: self.updatePOI()
                    },
                    {
                        'name': 'Num POI/Subkey',
                        'key': 'poi-nummax',
                        'type': 'int',
                        'limits': (1, 200),
                        'value': 1,
                        'action': lambda _: self.updatePOI()
                    },
                    {
                        'name': 'Min Spacing between POI',
                        'key': 'poi-minspace',
                        'type': 'int',
                        'limits': (1, 100E6),
                        'value': 1,
                        'step': 100,
                        'action': lambda _: self.updatePOI()
                    },
                    {
                        'name': 'Hill detection',
                        'key': 'poi-hilldet',
                        'type': 'bool',
                        'value': True,
                        'tip': "Extend the bounds downhill for each peak",
                        'action': lambda _: self.updatePOI()
                    },
                    # {'name':'Threshold', 'key':'threshold', 'type':'int', 'visible':False},
                    {
                        'name': 'Open POI Table',
                        'type': 'action',
                        'action': lambda _: self.poiDock.show()
                    },
                ]
            },
        ])
class PartitionDisplay(Parameterized, AutoScript):
    _name = "Partition Comparison"

    def __init__(self, parent):
        AutoScript.__init__(self)
        self._autoscript_init = False
        self.parent = parent
        self.poi = POI(self)
        self.poiDock = CWMainGUI.getInstance().addDock(
            self.poi,
            "Partition Comparison POI Table",
            area=Qt.TopDockWidgetArea)
        self.poiDock.hide()
        self.defineName()
        self._traces = None

        self.api = CWCoreAPI.getInstance()
        self.graph = GraphWidget()
        self.bselection = QToolBar()
        self.graph.addWidget(self.bselection)
        self.graphDock = CWMainGUI.getInstance().addDock(
            self.graph,
            "Partition Comparison Graph",
            area=Qt.TopDockWidgetArea)
        self.graphDock.hide()

    def defineName(self):
        self.partObject = Partition()
        partModeList = {}
        for a in self.partObject.supportedMethods:
            partModeList[a.partitionType] = a

        self.diffObject = DifferenceMode()
        diffModeList = {}
        for a in self.diffObject.supportedMethods:
            diffModeList[a.differenceType] = a

        self.addGroup("generatePartitions")
        self.addGroup("generatePartitionStats")
        self.addGroup("generatePartitionDiffs")
        self.addGroup("displayPartitionDiffs")

        self.getParams().addChildren([
            {
                'name': 'Comparison Mode',
                'key': 'diffmode',
                'type': 'list',
                'values': diffModeList,
                'value': self.diffObject.diffMethodClass,
                'action': lambda _: self.updateScript()
            },
            {
                'name': 'Partition Mode',
                'key': 'partmode',
                'type': 'list',
                'values': partModeList,
                'value': self.partObject.partMethodClass,
                'action': lambda _: self.updateScript()
            },
            {
                'name': 'Display',
                'type': 'action',
                'action': lambda _: self.runAction()
            },
            {
                'name': 'Auto-Save Data to Project',
                'key': 'part-saveints',
                'type': 'bool',
                'value': False,
                'action': lambda _: self.updateScript()
            },
            {
                'name': 'Auto-Load Data from Project',
                'key': 'part-loadints',
                'type': 'bool',
                'value': False,
                'action': lambda _: self.updateScript()
            },
            {
                'name':
                'Points of Interest',
                'key':
                'poi',
                'type':
                'group',
                'children': [
                    {
                        'name': 'Selection Mode',
                        'type': 'list',
                        'values': {
                            'Max N Points/Subkey': 'maxn'
                        },
                        'value': 'maxn'
                    },
                    {
                        'name': 'Point Range',
                        'key': 'poi-pointrng',
                        'type': 'range',
                        'limits': (0, 0),
                        'default': (0, 0),
                        'value': (0, 0),
                        'action': lambda _: self.updatePOI()
                    },
                    {
                        'name': 'Num POI/Subkey',
                        'key': 'poi-nummax',
                        'type': 'int',
                        'limits': (1, 200),
                        'value': 1,
                        'action': lambda _: self.updatePOI()
                    },
                    {
                        'name': 'Min Spacing between POI',
                        'key': 'poi-minspace',
                        'type': 'int',
                        'limits': (1, 100E6),
                        'value': 1,
                        'step': 100,
                        'action': lambda _: self.updatePOI()
                    },
                    {
                        'name': 'Hill detection',
                        'key': 'poi-hilldet',
                        'type': 'bool',
                        'value': True,
                        'tip': "Extend the bounds downhill for each peak",
                        'action': lambda _: self.updatePOI()
                    },
                    # {'name':'Threshold', 'key':'threshold', 'type':'int', 'visible':False},
                    {
                        'name': 'Open POI Table',
                        'type': 'action',
                        'action': lambda _: self.poiDock.show()
                    },
                ]
            },
        ])

    def updatePOI(self, _=None):
        self.updateScript()

        if self._autoscript_init == False:
            return

        # Some sort of race condition - applying Therac-25 type engineering and just
        # randomly hope this is enough delay
        QTimer.singleShot(
            500, lambda: self.runScriptFunction.emit(
                "TraceExplorerDialog_PartitionDisplay_findPOI"))

    def setBytePlot(self, num, sel):
        self.enabledbytes[num] = sel
        if self.doRedraw:
            self.redrawPlot()

    def setByteAll(self, status):
        self.doRedraw = False
        for i, t in enumerate(self.byteNumAct):
            t.defaultWidget().setChecked(status)
            self.setBytePlot(i, status)
        self.doRedraw = True
        self.redrawPlot()

    def redrawPlot(self):
        self.graph.clearPushed()

        for bnum in range(0, self.numKeys):
            if self.enabledbytes[bnum]:
                self.graph.setColorInt(bnum, self.numKeys)
                self.graph.passTrace(self.SADList[bnum],
                                     pen=pg.mkPen(pg.intColor(bnum, 16)),
                                     idString=str(bnum))

    def updateScript(self, ignored=None):
        ##Partitioning & Differences
        try:
            diffMethodStr = self.findParam('diffmode').getValue().__name__
            partMethodStr = self.findParam('partmode').getValue().__name__
        except AttributeError as e:
            return

        self.importsAppend(
            'from chipwhisperer.analyzer.utils.Partition import PartitionRandDebug, PartitionRandvsFixed, PartitionEncKey, PartitionHWIntermediate, PartitionHDLastRound'
        )
        self.importsAppend(
            'from chipwhisperer.analyzer.utils.TraceExplorerScripts.PartitionDisplay import DifferenceModeTTest, DifferenceModeSAD'
        )
        self.importsAppend(
            'from chipwhisperer.analyzer.ui.CWAnalyzerGUI import CWAnalyzerGUI'
        )

        self.addGroup("displayPartitionStats")
        self.addVariable('displayPartitionStats', 'ted', 'self.')
        self.addFunction('displayPartitionStats',
                         'setTraceSource',
                         'UserScript.traces',
                         obj='ted')
        self.addFunction('displayPartitionStats',
                         'parent.getProgressIndicator',
                         '',
                         'progressBar',
                         obj='ted')
        self.addFunction('displayPartitionStats',
                         'partObject.setPartMethod',
                         partMethodStr,
                         obj='ted')
        self.addFunction('displayPartitionStats',
                         'partObject.generatePartitions',
                         'saveFile=True, loadFile=False',
                         'partData',
                         obj='ted')
        self.addFunction(
            'displayPartitionStats',
            'generatePartitionStats',
            'partitionData={"partclass":%s, "partdata":partData}, saveFile=True, progressBar=progressBar'
            % partMethodStr,
            'partStats',
            obj='ted')
        self.addFunction(
            'displayPartitionStats',
            'generatePartitionDiffs',
            '%s, statsInfo={"partclass":%s, "stats":partStats}, saveFile=True, loadFile=False, progressBar=progressBar'
            % (diffMethodStr, partMethodStr),
            'partDiffs',
            obj='ted')
        self.addFunction('displayPartitionStats',
                         'displayPartitions',
                         'differences={"partclass":%s, "diffs":partDiffs}' %
                         partMethodStr,
                         obj='ted')
        self.addFunction('displayPartitionStats',
                         'poi.setDifferences',
                         'partDiffs',
                         obj='ted')

        ##Points of Interest
        ptrng = self.findParam(["Points of Interest",
                                'poi-pointrng']).getValue()
        self.addGroup("findPOI")
        self.addVariable('findPOI', 'ted', 'self.')
        self.addFunction(
            'findPOI',
            'poi.calcPOI',
            'numMax=%d, pointRange=(%d, %d), minSpace=%d' %
            (self.findParam(["Points of Interest", 'poi-nummax'
                             ]).getValue(), ptrng[0], ptrng[1],
             self.findParam(["Points of Interest", 'poi-minspace'
                             ]).getValue()),
            obj='ted')

        #Check if this updateScript was called as a result of showing the TraceExplorer window
        if ignored == "traceexplorer_show":
            self._autoscript_init = True

    def generatePartitionStats(self,
                               partitionData={
                                   "partclass": None,
                                   "partdata": None
                               },
                               saveFile=False,
                               loadFile=False,
                               tRange=(0, -1),
                               progressBar=None):

        traces = self._traces

        if tRange[1] < 0:
            tRange = (tRange[0], traces.numTraces() + 1 + tRange[1])

        self.partObject.setPartMethod(partitionData["partclass"])

        self.numKeys = len(
            self.partObject.partMethod.getPartitionNum(traces, 0))

        numPoints = traces.numPoints()

        if loadFile:
            cfgsecs = self.api.project().getDataConfig(
                sectionName="Trace Statistics",
                subsectionName="Total Trace Statistics")
            foundsecs = []
            for cfg in cfgsecs:
                desiredsettings = {}
                desiredsettings["tracestart"] = tRange[0]
                desiredsettings["traceend"] = tRange[1]
                desiredsettings[
                    "partitiontype"] = self.partObject.partMethod.__class__.__name__
                if self.api.project().checkDataConfig(cfg, desiredsettings):
                    foundsecs.append(cfg)
        else:
            foundsecs = []

        if len(foundsecs) > 1:
            IOError("Too many sections!!!")
        elif len(foundsecs) == 1:
            fname = self.api.project().convertDataFilepathAbs(
                foundsecs[0]["filename"])
            stats = np.load(fname)
        else:
            # Array to hold average + stddev of all traces/partitions
            A_k = []
            A_j = []
            Q_k = []
            dataArrays = [A_k, A_j, Q_k]
            ACnt = []
            for bnum in range(0, self.numKeys):
                for d in dataArrays:
                    d.append([])
                ACnt.append([])
                for i in range(0,
                               self.partObject.partMethod.getNumPartitions()):
                    for d in dataArrays:
                        d[bnum].append(np.zeros(numPoints))
                    ACnt[bnum].append(0)

            # Get segment list
            segList = traces.getSegmentList()

            if progressBar:
                progressBar.setWindowTitle("Phase 1: Trace Statistics")
                progressBar.setMaximum(
                    bnum)  # len(segList['offsetList']) * self.numKeys)
                progressBar.show()

            # TODO: Double-check this fix
            # for tsegn, segtrace in enumerate(segList['offsetList']):
            tsegn = 0
            if progressBar: progressBar.setText("Segment %d" % tsegn)

            # Average data needs to be calculated
            # Require partition list
            partData = partitionData["partdata"]

            # print "Calculating Average + Std-Dev"
            # Std-Dev calculation:
            # A[0] = 0
            # A[k] = A[k-1] + (x[k] - A[k-1]) / k
            # Q[0] = 0
            # Q[k] = Q[k-1] + (x[k] - A[k-1])(x[k] - A[k])
            for bnum in range(0, self.numKeys):
                progressBar.updateStatus(tsegn * self.numKeys + bnum)
                if progressBar.wasAborted():
                    break
                for i in range(0,
                               self.partObject.partMethod.getNumPartitions()):
                    util.updateUI()
                    tlist = partData[bnum][i]
                    if len(tlist) > 0:
                        for tnum in tlist:
                            if tnum > tRange[0] and tnum < tRange[1]:
                                t = traces.getTrace(tnum)
                                ACnt[bnum][i] += 1
                                A_k[bnum][i] = A_k[bnum][i] + (
                                    t - A_j[bnum][i]) / ACnt[bnum][i]
                                Q_k[bnum][i] = Q_k[bnum][i] + (
                                    (t - A_j[bnum][i]) * (t - A_k[bnum][i]))
                                A_j[bnum][i] = A_k[bnum][i]

            if progressBar.wasAborted():
                progressBar.hide()
                return

            # Finally get variance
            for bnum in range(0, self.numKeys):
                for i in range(0,
                               self.partObject.partMethod.getNumPartitions()):
                    # TODO: Should be using population variance or sample variance (e.g. /n or /n-1)?
                    #      Since this is taken over very large sample sizes I imagine it won't matter
                    #      ultimately.
                    Q_k[bnum][i] = Q_k[bnum][i] / max(ACnt[bnum][i] - 1, 1)

            # Average is in A_k
            stats = {"mean": A_k, "variance": Q_k, "number": ACnt}

            # Wasn't cancelled - save this to project file for future use if requested
            if saveFile:
                progressBar.setText("Saving Mean/Variance Partitions")
                cfgsec = self.api.project().addDataConfig(
                    sectionName="Trace Statistics",
                    subsectionName="Total Trace Statistics")
                cfgsec["tracestart"] = tRange[0]
                cfgsec["traceend"] = tRange[1]
                cfgsec[
                    "partitiontype"] = self.partObject.partMethod.__class__.__name__
                fname = self.api.project().getDataFilepath(
                    'tracestats-%s-%d-%s.npz' %
                    (cfgsec["partitiontype"], tRange[0], tRange[1]),
                    'analysis')

                # Save mean/variance for trace
                np.savez(fname["abs"], mean=A_k, variance=Q_k, number=ACnt)
                cfgsec["filename"] = fname["rel"]

        progressBar.hide()
        return stats

    def generatePartitionDiffs(self,
                               diffModule,
                               statsInfo={
                                   "partclass": None,
                                   "stats": None
                               },
                               saveFile=False,
                               loadFile=False,
                               tRange=(0, -1),
                               progressBar=None):

        traces = self._traces

        if tRange[1] < 0:
            tRange = (tRange[0], traces.numTraces() + 1 + tRange[1])

        self.partObject.setPartMethod(statsInfo["partclass"])
        self.diffObject.setDiffMethod(diffModule)

        self.numKeys = len(
            self.partObject.partMethod.getPartitionNum(traces, 0))

        numPoints = traces.numPoints()

        foundsecs = []
        if loadFile:
            cfgsecs = self.api.project().getDataConfig(
                sectionName="Trace Statistics",
                subsectionName="Partition Differences")
            for cfg in cfgsecs:
                desiredsettings = {}
                desiredsettings["tracestart"] = tRange[0]
                desiredsettings["traceend"] = tRange[1]
                desiredsettings[
                    "partitiontype"] = self.partObject.partMethod.__class__.__name__
                desiredsettings[
                    "comparetype"] = self.diffObject.mode.moduleName
                if self.api.project().checkDataConfig(cfg, desiredsettings):
                    foundsecs.append(cfg)
        else:
            cfgsecs = []

        if len(foundsecs) > 1:
            IOError("Too many sections!!!")
        elif len(foundsecs) == 1:
            fname = self.api.project().convertDataFilepathAbs(
                foundsecs[0]["filename"])
            SADList = np.load(fname)
        else:
            progressBar.setWindowTitle(
                "Phase 2: Calculating Partition Differences")
            progressBar.setText("Calculating all Differences based on " +
                                self.diffObject.mode.differenceType)
            SADList = self.diffObject.difference(
                self.numKeys, self.partObject.partMethod.getNumPartitions(),
                None, numPoints, statsInfo["stats"], progressBar)

            if saveFile:
                cfgsec = self.api.project().addDataConfig(
                    sectionName="Trace Statistics",
                    subsectionName="Partition Differences")
                cfgsec["tracestart"] = tRange[0]
                cfgsec["traceend"] = tRange[1]
                cfgsec[
                    "partitiontype"] = self.partObject.partMethod.__class__.__name__
                cfgsec["comparetype"] = self.diffObject.mode.moduleName
                fname = self.api.project().getDataFilepath(
                    'partdiffs-%s-%s-%d-%s.npy' %
                    (cfgsec["partitiontype"], cfgsec["comparetype"], tRange[0],
                     tRange[1]), 'analysis')
                np.save(fname["abs"], SADList)
                cfgsec["filename"] = fname["rel"]

        progressBar.updateStatus(progressBar.maximum)
        progressBar.setWindowTitle('Debug Fail')
        if progressBar.wasAborted():
            return

        self.SADList = SADList
        return SADList

    def displayPartitions(self,
                          differences={
                              "partclass": None,
                              "diffs": None
                          },
                          tRange=(0, -1)):
        self.graphDock.show()
        traces = self._traces

        if tRange[1] < 0:
            tRange = (tRange[0], traces.numTraces() + 1 + tRange[1])

        self.partObject.setPartMethod(differences["partclass"])
        self.numKeys = len(
            self.partObject.partMethod.getPartitionNum(traces, 0))
        self.SADList = differences["diffs"]

        # Place byte selection option on graph
        if hasattr(self, 'enabledbytes') and len(
                self.enabledbytes) == self.numKeys:
            pass
        else:
            self.enabledbytes = [False] * self.numKeys

        self.doRedraw = True

        self.byteNumAct = []
        for i in range(0, self.numKeys):
            ql = QToolButton()
            ql.setText('%d' % i)
            color = pg.intColor(i, self.numKeys)
            ql.setStyleSheet("color: rgb(%d, %d, %d)" %
                             (color.red(), color.green(), color.blue()))
            qa = QWidgetAction(self.graph)
            qa.setDefaultWidget(ql)
            qa.setStatusTip('%d' % i)
            ql.setCheckable(True)
            ql.setChecked(self.enabledbytes[i])
            ql.clicked[bool].connect(partial(self.setBytePlot, i))
            self.byteNumAct.append(qa)

        byteNumAllOn = QAction('All On', self.graph)
        byteNumAllOff = QAction('All Off', self.graph)
        byteNumAllOn.triggered.connect(partial(self.setByteAll, True))
        byteNumAllOff.triggered.connect(partial(self.setByteAll, False))

        self.bselection.clear()
        for i in range(0, self.numKeys):
            self.bselection.addAction(self.byteNumAct[i])
        self.bselection.addAction(byteNumAllOn)
        self.bselection.addAction(byteNumAllOff)
        self.graph.setPersistance(True)

        self.poi.setDifferences(self.SADList)

        self.findParam(["Points of Interest", 'poi-pointrng']).setLimits(
            (0, len(self.SADList[0])))
        self.findParam(["Points of Interest", 'poi-pointrng']).setValue(
            (0, len(self.SADList[0])))
        self.redrawPlot()

    def runAction(self):
        self.runScriptFunction.emit(
            'TraceExplorerDialog_PartitionDisplay_displayPartitionStats')

    def setTraceSource(self, traces):
        self._traces = traces
        self.partObject.setTraceSource(self._traces)
class PartitionDisplay(Parameterized, AutoScript):
    _name = "Partition Comparison"

    def __init__(self, parent):
        AutoScript.__init__(self)
        self._autoscript_init = False
        self.parent = parent
        self.poi = POI(self)
        self.poiDock = CWMainGUI.getInstance().addDock(self.poi, "Partition Comparison POI Table", area=Qt.TopDockWidgetArea)
        self.poiDock.hide()
        self.defineName()
        self._traces = None

        self.api = CWCoreAPI.getInstance()
        self.graph = GraphWidget()
        self.bselection = QToolBar()
        self.graph.addWidget(self.bselection)
        self.graphDock = CWMainGUI.getInstance().addDock(self.graph, "Partition Comparison Graph", area=Qt.TopDockWidgetArea)
        self.graphDock.hide()

    def defineName(self):
        self.partObject = Partition(self)
        partModeList = {}
        for a in self.partObject.supportedMethods:
            partModeList[a.partitionType] = a

        self.diffObject = DifferenceMode()
        diffModeList = {}
        for a in self.diffObject.supportedMethods:
            diffModeList[a.differenceType] = a

        self.addGroup("generatePartitions")
        self.addGroup("generatePartitionStats")
        self.addGroup("generatePartitionDiffs")
        self.addGroup("displayPartitionDiffs")

        self.getParams().addChildren([
              {'name':'Comparison Mode', 'key':'diffmode', 'type':'list', 'values':diffModeList, 'value':self.diffObject.diffMethodClass, 'action':lambda _: self.updateScript()},
              {'name':'Partition Mode', 'key':'partmode', 'type':'list', 'values':partModeList, 'value':self.partObject.partMethodClass, 'action':lambda _: self.updateScript()},
              {'name':'Display', 'type':'action', 'action':lambda _:self.runAction()},

              {'name':'Auto-Save Data to Project', 'key':'part-saveints', 'type':'bool', 'value':False, 'action':lambda _: self.updateScript()},
              {'name':'Auto-Load Data from Project', 'key':'part-loadints', 'type':'bool', 'value':False, 'action':lambda _: self.updateScript()},

              {'name':'Points of Interest', 'key':'poi', 'type':'group', 'children':[
                 {'name':'Selection Mode', 'type':'list', 'values':{'Max N Points/Subkey':'maxn'}, 'value':'maxn'},
                 {'name':'Point Range', 'key':'poi-pointrng', 'type':'range', 'limits':(0, 0), 'default':(0, 0), 'value':(0, 0), 'action':lambda _: self.updatePOI()},
                 {'name':'Num POI/Subkey', 'key':'poi-nummax', 'type':'int', 'limits':(1, 200), 'value':1, 'action':lambda _: self.updatePOI()},
                 {'name':'Min Spacing between POI', 'key':'poi-minspace', 'type':'int', 'limits':(1, 100E6), 'value':1, 'step':100, 'action':lambda _: self.updatePOI()},
                 {'name':'Hill detection', 'key':'poi-hilldet', 'type':'bool', 'value':True, 'tip':"Extend the bounds downhill for each peak", 'action':lambda _: self.updatePOI()},
                 # {'name':'Threshold', 'key':'threshold', 'type':'int', 'visible':False},
                 {'name':'Open POI Table', 'type':'action', 'action':lambda _: self.poiDock.show()},
              ]},
        ])

    def updatePOI(self, ignored=None):
        self.updateScript()

        if self._autoscript_init == False:
            return

        # Some sort of race condition - applying Therac-25 type engineering and just
        # randomly hope this is enough delay
        QTimer.singleShot(500, lambda:self.runScriptFunction.emit("TraceExplorerDialog_PartitionDisplay_findPOI"))

    def setBytePlot(self, num, sel):
        self.enabledbytes[num] = sel
        if self.doRedraw:
            self.redrawPlot()

    def setByteAll(self, status):
        self.doRedraw = False
        for i, t in enumerate(self.byteNumAct):
            t.defaultWidget().setChecked(status)
            self.setBytePlot(i, status)
        self.doRedraw = True
        self.redrawPlot()

    def redrawPlot(self):
        self.graph.clearPushed()

        for bnum in range(0, self.numKeys):
            if self.enabledbytes[bnum]:
                self.graph.setColorInt(bnum, self.numKeys)
                self.graph.passTrace(self.SADList[bnum], pen=pg.mkPen(pg.intColor(bnum, 16)), idString = str(bnum))

    def updateScript(self, ignored=None):
        ##Partitioning & Differences
        try:
            diffMethodStr = self.findParam('diffmode').getValue().__name__
            partMethodStr = self.findParam('partmode').getValue().__name__
        except AttributeError as e:
            return

        self.importsAppend('from chipwhisperer.analyzer.utils.Partition import PartitionRandDebug, PartitionRandvsFixed, PartitionEncKey, PartitionHWIntermediate, PartitionHDLastRound')
        self.importsAppend('from chipwhisperer.analyzer.utils.TraceExplorerScripts.PartitionDisplay import DifferenceModeTTest, DifferenceModeSAD')
        self.importsAppend('from chipwhisperer.analyzer.ui.CWAnalyzerGUI import CWAnalyzerGUI')

        self.addGroup("displayPartitionStats")
        self.addVariable('displayPartitionStats', 'ted', 'self.')
        self.addFunction('displayPartitionStats', 'setTraceSource', 'UserScript.traces', obj='ted')
        self.addFunction('displayPartitionStats', 'parent.getProgressIndicator', '', 'progressBar', obj='ted')
        self.addFunction('displayPartitionStats', 'partObject.setPartMethod', partMethodStr, obj='ted')
        self.addFunction('displayPartitionStats', 'partObject.generatePartitions', 'saveFile=True, loadFile=False', 'partData', obj='ted')
        self.addFunction('displayPartitionStats', 'generatePartitionStats',
                            'partitionData={"partclass":%s, "partdata":partData}, saveFile=True, progressBar=progressBar' % partMethodStr,
                            'partStats', obj='ted')
        self.addFunction('displayPartitionStats', 'generatePartitionDiffs',
                            '%s, statsInfo={"partclass":%s, "stats":partStats}, saveFile=True, loadFile=False, progressBar=progressBar'%
                            (diffMethodStr, partMethodStr),
                            'partDiffs', obj='ted')
        self.addFunction('displayPartitionStats', 'displayPartitions', 'differences={"partclass":%s, "diffs":partDiffs}' % partMethodStr, obj='ted')
        self.addFunction('displayPartitionStats', 'poi.setDifferences', 'partDiffs', obj='ted')

        ##Points of Interest
        ptrng = self.findParam(["Points of Interest",'poi-pointrng']).getValue()
        self.addGroup("findPOI")
        self.addVariable('findPOI', 'ted', 'self.')
        self.addFunction('findPOI', 'poi.calcPOI', 'numMax=%d, pointRange=(%d, %d), minSpace=%d' % (
                            self.findParam(["Points of Interest",'poi-nummax']).getValue(),
                            ptrng[0], ptrng[1],
                            self.findParam(["Points of Interest",'poi-minspace']).getValue()),
                          obj='ted')

        #Check if this updateScript was called as a result of showing the TraceExplorer window
        if ignored == "traceexplorer_show":
            self._autoscript_init = True

    def generatePartitionStats(self, partitionData={"partclass":None, "partdata":None}, saveFile=False, loadFile=False,  tRange=(0, -1), progressBar=None):

        traces = self._traces

        if tRange[1] < 0:
            tRange = (tRange[0], traces.numTraces() + 1 + tRange[1])

        self.partObject.setPartMethod(partitionData["partclass"])

        self.numKeys = len(self.partObject.partMethod.getPartitionNum(traces, 0))

        numPoints = traces.numPoints()

        if loadFile:
            cfgsecs = self.api.project().getDataConfig(sectionName="Trace Statistics", subsectionName="Total Trace Statistics")
            foundsecs = []
            for cfg in cfgsecs:
                desiredsettings = {}
                desiredsettings["tracestart"] = tRange[0]
                desiredsettings["traceend"] = tRange[1]
                desiredsettings["partitiontype"] = self.partObject.partMethod.__class__.__name__
                if self.api.project().checkDataConfig(cfg, desiredsettings):
                    foundsecs.append(cfg)
        else:
            foundsecs = []

        if len(foundsecs) > 1:
            IOError("Too many sections!!!")
        elif len(foundsecs) == 1:
            fname = self.api.project().convertDataFilepathAbs(foundsecs[0]["filename"])
            stats = np.load(fname)
        else:
            # Array to hold average + stddev of all traces/partitions
            A_k = []
            A_j = []
            Q_k = []
            dataArrays = [A_k, A_j, Q_k]
            ACnt = []
            for bnum in range(0, self.numKeys):
                for d in dataArrays:
                    d.append([])
                ACnt.append([])
                for i in range(0, self.partObject.partMethod.getNumPartitions()):
                    for d in dataArrays:
                        d[bnum].append(np.zeros(numPoints))
                    ACnt[bnum].append(0)

            # Get segment list
            segList = traces.getSegmentList()

            if progressBar:
                progressBar.setWindowTitle("Phase 1: Trace Statistics")
                progressBar.setMaximum(bnum)  # len(segList['offsetList']) * self.numKeys)
                progressBar.show()

            # TODO: Double-check this fix
            # for tsegn, segtrace in enumerate(segList['offsetList']):
            tsegn = 0
            if progressBar: progressBar.setText("Segment %d" % tsegn)

            # Average data needs to be calculated
            # Require partition list
            partData = partitionData["partdata"]


            # print "Calculating Average + Std-Dev"
            # Std-Dev calculation:
            # A[0] = 0
            # A[k] = A[k-1] + (x[k] - A[k-1]) / k
            # Q[0] = 0
            # Q[k] = Q[k-1] + (x[k] - A[k-1])(x[k] - A[k])
            for bnum in range(0, self.numKeys):
                progressBar.updateStatus(tsegn * self.numKeys + bnum)
                if progressBar.wasAborted():
                    break
                for i in range(0, self.partObject.partMethod.getNumPartitions()):
                    util.updateUI()
                    tlist = partData[bnum][i]
                    if len(tlist) > 0:
                        for tnum in tlist:
                            if tnum > tRange[0] and tnum < tRange[1]:
                                t = traces.getTrace(tnum)
                                ACnt[bnum][i] += 1
                                A_k[bnum][i] = A_k[bnum][i] + (t - A_j[bnum][i]) / ACnt[bnum][i]
                                Q_k[bnum][i] = Q_k[bnum][i] + ((t - A_j[bnum][i]) * (t - A_k[bnum][i]))
                                A_j[bnum][i] = A_k[bnum][i]

            if progressBar.wasAborted():
                progressBar.hide()
                return

            # Finally get variance
            for bnum in range(0, self.numKeys):
                    for i in range(0, self.partObject.partMethod.getNumPartitions()):
                        # TODO: Should be using population variance or sample variance (e.g. /n or /n-1)?
                        #      Since this is taken over very large sample sizes I imagine it won't matter
                        #      ultimately.
                        Q_k[bnum][i] = Q_k[bnum][i] / max(ACnt[bnum][i] - 1, 1)

            # Average is in A_k
            stats = {"mean":A_k, "variance":Q_k, "number":ACnt}

            # Wasn't cancelled - save this to project file for future use if requested
            if saveFile:
                progressBar.setText("Saving Mean/Variance Partitions")
                cfgsec = self.api.project().addDataConfig(sectionName="Trace Statistics", subsectionName="Total Trace Statistics")
                cfgsec["tracestart"] = tRange[0]
                cfgsec["traceend"] = tRange[1]
                cfgsec["partitiontype"] = self.partObject.partMethod.__class__.__name__
                fname = self.api.project().getDataFilepath('tracestats-%s-%d-%s.npz' % (cfgsec["partitiontype"], tRange[0], tRange[1]), 'analysis')

                # Save mean/variance for trace
                np.savez(fname["abs"], mean=A_k, variance=Q_k, number=ACnt)
                cfgsec["filename"] = fname["rel"]

        progressBar.hide()
        return stats

    def generatePartitionDiffs(self, diffModule, statsInfo={"partclass":None, "stats":None}, saveFile=False, loadFile=False, tRange=(0, -1), progressBar=None):

        traces = self._traces

        if tRange[1] < 0:
            tRange = (tRange[0], traces.numTraces() + 1 + tRange[1])

        self.partObject.setPartMethod(statsInfo["partclass"])
        self.diffObject.setDiffMethod(diffModule)

        self.numKeys = len(self.partObject.partMethod.getPartitionNum(traces, 0))

        numPoints = traces.numPoints()

        foundsecs = []
        if loadFile:
            cfgsecs = self.api.project().getDataConfig(sectionName="Trace Statistics", subsectionName="Partition Differences")
            for cfg in cfgsecs:
                desiredsettings = {}
                desiredsettings["tracestart"] = tRange[0]
                desiredsettings["traceend"] = tRange[1]
                desiredsettings["partitiontype"] = self.partObject.partMethod.__class__.__name__
                desiredsettings["comparetype"] = self.diffObject.mode.moduleName
                if self.api.project().checkDataConfig(cfg, desiredsettings):
                    foundsecs.append(cfg)
        else:
            cfgsecs = []

        if len(foundsecs) > 1:
            IOError("Too many sections!!!")
        elif len(foundsecs) == 1:
            fname = self.api.project().convertDataFilepathAbs(foundsecs[0]["filename"])
            SADList = np.load(fname)
        else:
            progressBar.setWindowTitle("Phase 2: Calculating Partition Differences")
            progressBar.setText("Calculating all Differences based on " + self.diffObject.mode.differenceType)
            SADList = self.diffObject.difference(self.numKeys, self.partObject.partMethod.getNumPartitions(), None, numPoints, statsInfo["stats"], progressBar)

            if saveFile:
                cfgsec = self.api.project().addDataConfig(sectionName="Trace Statistics", subsectionName="Partition Differences")
                cfgsec["tracestart"] = tRange[0]
                cfgsec["traceend"] = tRange[1]
                cfgsec["partitiontype"] = self.partObject.partMethod.__class__.__name__
                cfgsec["comparetype"] = self.diffObject.mode.moduleName
                fname = self.api.project().getDataFilepath('partdiffs-%s-%s-%d-%s.npy' % (cfgsec["partitiontype"], cfgsec["comparetype"], tRange[0], tRange[1]), 'analysis')
                np.save(fname["abs"], SADList)
                cfgsec["filename"] = fname["rel"]

        progressBar.updateStatus(progressBar.maximum)
        progressBar.setWindowTitle('Debug Fail')
        if progressBar.wasAborted():
            return

        self.SADList = SADList
        return SADList

    def displayPartitions(self, differences={"partclass":None, "diffs":None}, tRange=(0, -1)):
        self.graphDock.show()
        traces = self._traces

        if tRange[1] < 0:
            tRange = (tRange[0], traces.numTraces() + 1 + tRange[1])

        self.partObject.setPartMethod(differences["partclass"])
        self.numKeys = len(self.partObject.partMethod.getPartitionNum(traces, 0))
        self.SADList = differences["diffs"]

        # Place byte selection option on graph
        if hasattr(self, 'enabledbytes') and len(self.enabledbytes) == self.numKeys:
            pass
        else:
            self.enabledbytes = [False] * self.numKeys

        self.doRedraw = True

        self.byteNumAct = []
        for i in range(0, self.numKeys):
            ql = QToolButton()
            ql.setText('%d' % i)
            color = pg.intColor(i, self.numKeys)
            ql.setStyleSheet("color: rgb(%d, %d, %d)" % (color.red(), color.green(), color.blue()))
            qa = QWidgetAction(self.graph)
            qa.setDefaultWidget(ql)
            qa.setStatusTip('%d' % i)
            ql.setCheckable(True)
            ql.setChecked(self.enabledbytes[i])
            ql.clicked[bool].connect(partial(self.setBytePlot, i))
            self.byteNumAct.append(qa)

        byteNumAllOn = QAction('All On', self.graph)
        byteNumAllOff = QAction('All Off', self.graph)
        byteNumAllOn.triggered.connect(partial(self.setByteAll, True))
        byteNumAllOff.triggered.connect(partial(self.setByteAll, False))

        self.bselection.clear()
        for i in range(0, self.numKeys):
            self.bselection.addAction(self.byteNumAct[i])
        self.bselection.addAction(byteNumAllOn)
        self.bselection.addAction(byteNumAllOff)
        self.graph.setPersistance(True)

        self.poi.setDifferences(self.SADList)

        self.findParam(["Points of Interest",'poi-pointrng']).setLimits((0, len(self.SADList[0])))
        self.findParam(["Points of Interest",'poi-pointrng']).setValue((0, len(self.SADList[0])))
        self.redrawPlot()

    def runAction(self):
        self.runScriptFunction.emit('TraceExplorerDialog_PartitionDisplay_displayPartitionStats')

    def setTraceSource(self, traces):
        self._traces = traces
        self.partObject.setTraceSource(self._traces)
Beispiel #7
0
 def __init__(self, tmanager=None):
     self._traceSource = None
     self.partObject = Partition()
 def __init__(self, tmanager=None):
     self._tmanager = None
     self.partObject = Partition(self)