Subversion Repositories Projects

Rev

Rev 628 | Rev 633 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# generated by wxGlade 0.6.3 on Thu Sep 24 15:46:36 2009

import sys
import os
import time
import thread
import ConfigParser

import wx
import wx.lib
import wx.lib.plot
import wx.lib.newevent
import wx.lib.agw.speedmeter as speedmeter

import mkProto



CHANNEL_NAMES = ["GyroYaw", "GyroRoll", "GyroNick", "Pressure", "Batt", "AccTop", "AccRoll", "AccNick"]
FS = 11111
pi = 3.14

rootPath = ""

# Needs Numeric or numarray or NumPy
try:
    import numpy.oldnumeric as _Numeric
except:
    try:
        import numarray as _Numeric  #if numarray is used it is renamed Numeric
    except:
        try:
            import Numeric as _Numeric
        except:
            msg= """
            This module requires the Numeric/numarray or NumPy module,
            which could not be imported.  It probably is not installed
            (it's not part of the standard Python distribution). See the
            Numeric Python site (http://numpy.scipy.org) for information on
            downloading source or binaries."""

            raise ImportError, "Numeric,numarray or NumPy not found. \n" + msg

# begin wxGlade: extracode
# end wxGlade


# This creates a new Event class and a EVT binder function
(MeasStatusUpdateEvent, EVT_MEAS_STATUS_UPDATE) = wx.lib.newevent.NewEvent()  
(MeasDataEvent, EVT_MEAS_DATA) = wx.lib.newevent.NewEvent()  

class MeasureDialog(wx.Dialog):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MeasureDialog.__init__
        kwds["style"] = wx.CAPTION|wx.RESIZE_BORDER|wx.THICK_FRAME
        wx.Dialog.__init__(self, *args, **kwds)
        self.text_ctrl_1 = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        self.button = wx.Button(self, -1, "STOP")
        self.voltageCtrl = speedmeter.SpeedMeter(self, extrastyle=speedmeter.SM_DRAW_HAND | speedmeter.SM_DRAW_PARTIAL_SECTORS |  speedmeter.SM_DRAW_MIDDLE_ICON )
        self.speedCtrl = speedmeter.SpeedMeter(self, extrastyle=speedmeter.SM_DRAW_HAND | speedmeter.SM_DRAW_PARTIAL_SECTORS | speedmeter.SM_DRAW_MIDDLE_TEXT | speedmeter.SM_DRAW_SECONDARY_TICKS)

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_BUTTON, self.onButton, self.button)
        # end wxGlade
       
        self.button.SetFocus()
       
        YELLOW = wx.Colour(255, 240, 0)
        BG = wx.Colour(0x80, 0x80, 0x80)
        # Configure Voltage Ctrl
        self.voltageCtrl.SetAngleRange(0,pi)
        intervals = range(0, 5)
        self.voltageCtrl.SetIntervals(intervals)
        colours = [wx.RED, wx.GREEN, wx.GREEN, YELLOW]
        self.voltageCtrl.SetIntervalColours(colours)
        ticks = ["", "", "", "", ""]
        self.voltageCtrl.SetTicks(ticks)
        self.voltageCtrl.SetTicksColour(wx.WHITE)
        self.voltageCtrl.SetHandColour(YELLOW)

        icon = wx.Icon("%s/Resources/fuel.ico" % rootPath, wx.BITMAP_TYPE_ICO)
        icon.SetWidth(24)
        icon.SetHeight(24)

        self.voltageCtrl.SetMiddleIcon(icon)        
        self.voltageCtrl.SetSpeedBackground(BG)        
        self.voltageCtrl.SetArcColour(wx.WHITE)
        self.voltageCtrl.SetSpeedValue(2)


        # Configure Speed Ctr;
        self.speedCtrl.SetAngleRange(0,pi)        
        intervals = range(0, 261, 20)
        self.speedCtrl.SetIntervals(intervals)

        colours = [BG]*(len(intervals)-1)
        for i in range(5,10):
          colours[i] = wx.GREEN
        self.speedCtrl.SetIntervalColours(colours)
        ticks = [str(interval) for interval in intervals]
        self.speedCtrl.SetTicks(ticks)
        self.speedCtrl.SetTicksColour(wx.WHITE)
        self.speedCtrl.SetNumberOfSecondaryTicks(1)
        self.speedCtrl.SetTicksFont(wx.Font(7, wx.SWISS, wx.NORMAL, wx.NORMAL))
        self.speedCtrl.SetMiddleText("Speed")
        self.speedCtrl.SetMiddleTextColour(wx.WHITE)
        self.speedCtrl.SetMiddleTextFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.BOLD))
        self.speedCtrl.SetHandColour(YELLOW)
        self.speedCtrl.SetSpeedBackground(BG)  
        self.speedCtrl.SetArcColour(wx.WHITE)        
        self.speedCtrl.SetSpeedValue(0)

       
        self.running = True
        self.Bind(EVT_MEAS_STATUS_UPDATE, self.OnUpdate)
        self.Bind(EVT_MEAS_DATA, self.OnData)
        # The first argument that is passed to the constructor is the parent
        self.app = args[0].app
        self.error = False
        self.firstVoltage = True


    def __set_properties(self):
        # begin wxGlade: MeasureDialog.__set_properties
        self.SetTitle("Measuring Status")
        self.text_ctrl_1.SetMinSize((400,300))
        self.voltageCtrl.SetMinSize((50,-1))
        self.speedCtrl.SetMinSize((50,-1))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MeasureDialog.__do_layout
        sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_4 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add((20, 20), 0, 0, 0)
        sizer_2.Add((20, 20), 0, 0, 0)
        sizer_2.Add(self.text_ctrl_1, 1, wx.EXPAND, 0)
        sizer_2.Add((20, 20), 0, 0, 0)
        sizer_2.Add(self.button, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
        sizer_2.Add((20, 20), 0, 0, 0)
        sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
        sizer_1.Add((20, 20), 0, 0, 0)
        sizer_4.Add(self.voltageCtrl, 1, wx.EXPAND, 0)
        sizer_4.Add(self.speedCtrl, 1, wx.EXPAND, 0)
        sizer_1.Add(sizer_4, 1, wx.EXPAND, 0)
        sizer_1.Add((20, 20), 0, 0, 0)
        self.SetSizer(sizer_1)
        sizer_1.Fit(self)
        self.Layout()
        # end wxGlade
       
    def OnData(self, evt):
        print "Received Data"
        self.app.AddTest2(evt.vibTest)
       
    def OnUpdate(self, evt):
        print "Status update"
        self.running = evt.running
        if evt.error:
            self.error = True;
            self.text_ctrl_1.WriteText("ERROR: ")
            self.text_ctrl_1.SetBackgroundColour("Red")  
        self.text_ctrl_1.WriteText("%s\n"%evt.msg)
        if (not self.running):
            if (not self.error):
                self.text_ctrl_1.SetBackgroundColour("Green")
                self.text_ctrl_1.write(" ") # so that the background is redrawn
            self.button.SetLabel("Close")
           
        if evt.speed != None:
            self.speedCtrl.SetSpeedValue(evt.speed)
           
        if evt.voltage != None:
            vmin,vmax,v = evt.voltage
            if self.firstVoltage:
                ticks = ["", "%.1f V"%vmin, "", "%.1f V"%vmax, ""]
                self.voltageCtrl.SetTicks(ticks)
                self.firstVoltage = False
            i = (v-vmin)/(vmax-vmin)  # 0..1
            i *= 2
            i = i+1
            i = min(max(i,0.1),4.9)
            self.voltageCtrl.SetSpeedValue(i)
 
           
       
    def onButton(self, event): # wxGlade: MeasureDialog.<event_handler>
        if (not self.running):
            self.Destroy()
        else:
            self.app.cancelMeasurement()

# end of class MeasureDialog


class SettingsDialog(wx.Dialog):
    def __init__(self, *args, **kwds):
        # begin wxGlade: SettingsDialog.__init__
        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
        wx.Dialog.__init__(self, *args, **kwds)
        self.button_5 = wx.Button(self, wx.ID_CANCEL, "")
        self.button_6 = wx.Button(self, wx.ID_OK, "")

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_BUTTON, self.onOK, self.button_6)
        # end wxGlade

        # The first argument that is passed to the constructor is the parent
        self.settings = args[0].app.settings
        # Add text-boxes for all settings
        self.tb = []
        self.grid_sizer_2.SetRows(len(self.settings))
        for setting in self.settings.iteritems():
            lb = wx.StaticText(self, -1, setting[1].descr, style=wx.ALIGN_RIGHT)
            tb = wx.TextCtrl(self, -1, str(setting[1].value))
            self.grid_sizer_2.Add(lb, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
            self.grid_sizer_2.Add(tb, 0, 0, 0)
            self.tb.append(tb)
        self.sizer_5.Fit(self)
        self.Layout()

    def __set_properties(self):
        # begin wxGlade: SettingsDialog.__set_properties
        self.SetTitle("Settings")
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: SettingsDialog.__do_layout
        sizer_5 = wx.BoxSizer(wx.VERTICAL)
        grid_sizer_3 = wx.GridSizer(1, 2, 0, 0)
        sizer_6 = wx.BoxSizer(wx.HORIZONTAL)
        grid_sizer_2 = wx.GridSizer(1, 2, 4, 4)
        sizer_5.Add((20, 20), 0, 0, 0)
        sizer_6.Add((20, 20), 0, 0, 0)
        sizer_6.Add(grid_sizer_2, 0, 0, 0)
        sizer_6.Add((20, 20), 0, 0, 0)
        sizer_5.Add(sizer_6, 1, wx.EXPAND, 0)
        sizer_5.Add((20, 20), 0, 0, 0)
        grid_sizer_3.Add(self.button_5, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_3.Add(self.button_6, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_5.Add(grid_sizer_3, 0, wx.EXPAND, 0)
        sizer_5.Add((20, 20), 0, 0, 0)
        self.SetSizer(sizer_5)
        sizer_5.Fit(self)
        self.Layout()
        # end wxGlade

        # Store some of the items, we will need them later
        self.grid_sizer_2 = grid_sizer_2  
        self.sizer_5 = sizer_5

       
    def onOK(self, event): # wxGlade: SettingsDialog.<event_handler>
        print "Updating parameters"
        try:
            i=0
            for setting in self.settings.iteritems():
              print setting[0], self.tb[i].GetValue()
              setting[1].set(self.tb[i].GetValue())
              i += 1
            event.Skip()
        except:
            wx.MessageBox("Invalid format for \"%s\" setting." % setting[1].descr)
       
# end of class SettingsDialog



class MainFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MainFrame.__init__
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
       
        # Menu Bar
        self.frame_1_menubar = wx.MenuBar()
        wxglade_tmp_menu = wx.Menu()
        wxglade_tmp_menu.Append(101, "Settings", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendSeparator()
        wxglade_tmp_menu.Append(150, "Exit", "", wx.ITEM_NORMAL)
        self.frame_1_menubar.Append(wxglade_tmp_menu, "File")
        wxglade_tmp_menu = wx.Menu()
        wxglade_tmp_menu.Append(301, "Clear All", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.Append(302, "Clear Selected", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendSeparator()
        wxglade_tmp_menu.Append(310, "Select All", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendSeparator()
        wxglade_tmp_menu.Append(303, "Load", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.Append(304, "Save", "", wx.ITEM_NORMAL)
        self.frame_1_menubar.Append(wxglade_tmp_menu, "TestSet")
        wxglade_tmp_menu = wx.Menu()
        wxglade_tmp_menu.Append(401, "Flash VibTest FC software", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.Append(402, "Restore original FC software", "", wx.ITEM_NORMAL)
        self.frame_1_menubar.Append(wxglade_tmp_menu, "MK")
        wxglade_tmp_menu = wx.Menu()
        wxglade_tmp_menu.Append(1099, "About", "", wx.ITEM_NORMAL)
        self.frame_1_menubar.Append(wxglade_tmp_menu, "Help")
        self.SetMenuBar(self.frame_1_menubar)
        # Menu Bar end
        self.Description = wx.StaticText(self, -1, "Description")
        self.tcDescr = wx.TextCtrl(self, -1, "Test")
        self.label_37 = wx.StaticText(self, -1, "Speed(s)")
        self.tcSpeeds = wx.TextCtrl(self, -1, "100-200:10")
        self.label_35 = wx.StaticText(self, -1, "Motor(s)")
        self.tcMotors = wx.TextCtrl(self, -1, "1")
        self.label_38 = wx.StaticText(self, -1, "")
        self.panel_2 = wx.Panel(self, -1)
        self.label_36 = wx.StaticText(self, -1, "Channel(s)")
        self.tcChannels = wx.TextCtrl(self, -1, "6")
        self.label_39 = wx.StaticText(self, -1, "")
        self.panel_3 = wx.Panel(self, -1)
        self.button_4 = wx.Button(self, -1, "Measure")
        self.graphCtrl = wx.lib.plot.PlotCanvas(self, size=(800,300))
        self.label_40 = wx.StaticText(self, -1, "Graph Type ")
        self.graphTypeChoice = wx.Choice(self, -1, choices=["Raw Signal", "Filtered Signal", "Spectrum"])
        self.label_41 = wx.StaticText(self, -1, "Y Axis Range ")
        self.yAxisChoice = wx.Choice(self, -1, choices=["25", "50", "100", "200"])
        self.copyGraphButton = wx.Button(self, -1, "Copy Graph Data")
        self.TestListCtrl = wx.ListCtrl(self, -1, style=wx.LC_REPORT|wx.SUNKEN_BORDER)

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_MENU, self.OnSettings, id=101)
        self.Bind(wx.EVT_MENU, self.onExit, id=150)
        self.Bind(wx.EVT_MENU, self.onClear, id=301)
        self.Bind(wx.EVT_MENU, self.onClearSelected, id=302)
        self.Bind(wx.EVT_MENU, self.onSelectAll, id=310)
        self.Bind(wx.EVT_MENU, self.OnImport, id=303)
        self.Bind(wx.EVT_MENU, self.onExport, id=304)
        self.Bind(wx.EVT_MENU, self.onAbout, id=1099)
        self.Bind(wx.EVT_BUTTON, self.onStartMeasure, self.button_4)
        self.Bind(wx.EVT_CHOICE, self.onGraphTypeChange, self.graphTypeChoice)
        self.Bind(wx.EVT_CHOICE, self.onYAxisChange, self.yAxisChoice)
        self.Bind(wx.EVT_BUTTON, self.onCopyGraphData, self.copyGraphButton)
        # end wxGlade
        favicon = wx.Icon('%s/Resources/60px-Procman.ico' % rootPath, wx.BITMAP_TYPE_ICO, 32, 32)
        wx.Frame.SetIcon(self, favicon)

    def setApp(self, app):
        self.app = app

    def __set_properties(self):
        # begin wxGlade: MainFrame.__set_properties
        self.SetTitle("VibrationTest")
        self.SetSize((850, 700))
        self.Description.SetMinSize((53, 13))
        self.button_4.SetMinSize((80, 80))
        self.graphCtrl.SetMinSize((800,300))
        self.graphTypeChoice.SetSelection(0)
        self.yAxisChoice.SetSelection(1)
        self.TestListCtrl.SetMinSize((800,300))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MainFrame.__do_layout
        sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_8 = wx.BoxSizer(wx.VERTICAL)
        sizer_11 = wx.BoxSizer(wx.VERTICAL)
        sizer_12 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_9 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_10 = wx.BoxSizer(wx.HORIZONTAL)
        grid_sizer_1 = wx.GridSizer(3, 4, 4, 5)
        sizer_3.Add((20, 20), 0, 0, 0)
        sizer_8.Add((20, 20), 0, 0, 0)
        grid_sizer_1.Add(self.Description, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.tcDescr, 0, 0, 0)
        grid_sizer_1.Add(self.label_37, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.tcSpeeds, 0, 0, 0)
        grid_sizer_1.Add(self.label_35, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.tcMotors, 0, 0, 0)
        grid_sizer_1.Add(self.label_38, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.panel_2, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.label_36, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.tcChannels, 0, 0, 0)
        grid_sizer_1.Add(self.label_39, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.panel_3, 1, wx.EXPAND, 0)
        sizer_9.Add(grid_sizer_1, 0, 0, 0)
        sizer_10.Add((50, 20), 0, 0, 0)
        sizer_10.Add(self.button_4, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_9.Add(sizer_10, 1, wx.EXPAND, 0)
        sizer_8.Add(sizer_9, 0, 0, 0)
        sizer_8.Add((20, 30), 0, 0, 0)
        sizer_11.Add(self.graphCtrl, 1, wx.EXPAND, 0)
        sizer_11.Add((20, 5), 0, 0, 0)
        sizer_12.Add(self.label_40, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_12.Add(self.graphTypeChoice, 0, 0, 0)
        sizer_12.Add((40, 20), 0, 0, 0)
        sizer_12.Add(self.label_41, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_12.Add(self.yAxisChoice, 0, 0, 0)
        sizer_12.Add((80, 20), 0, 0, 0)
        sizer_12.Add(self.copyGraphButton, 0, 0, 0)
        sizer_11.Add(sizer_12, 0, 0, 0)
        sizer_8.Add(sizer_11, 0, 0, 0)
        sizer_8.Add((20, 30), 0, 0, 0)
        sizer_8.Add(self.TestListCtrl, 1, 0, 0)
        sizer_8.Add((20, 20), 0, 0, 0)
        sizer_3.Add(sizer_8, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_3)
        self.Layout()
        self.SetSize((850, 700))
        # end wxGlade

        # List events
        self.TestListCtrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnTestSelected, self.TestListCtrl)

        # Configure Graph
        #self.graphCtrl = wx.lib.plot.PlotCanvas(self.GraphPanel, size=(800,300))
       
        self.graphCtrl.SetPointLabelFunc(self.DrawPointLabel)
       
        self.graphCtrl.SetFont(wx.Font(10,wx.SWISS,wx.NORMAL,wx.NORMAL))
        self.graphCtrl.SetFontSizeAxis(10)
        self.graphCtrl.SetFontSizeLegend(7)
        self.graphCtrl.setLogScale((False,False))


        # Configure TestListCtrl
        self.TestListCtrl.InsertColumn(0, "Description")
        self.TestListCtrl.InsertColumn(1, "Voltage")
        self.TestListCtrl.InsertColumn(2, "Speed")
        self.TestListCtrl.InsertColumn(3, "Channel")
        self.TestListCtrl.InsertColumn(4, "Vibration Value")
        self.TestListCtrl.SetColumnWidth(4, 500)

    def DrawPointLabel(self, dc, mDataDict):
        """This is the fuction that defines how the pointLabels are plotted
            dc - DC that will be passed
            mDataDict - Dictionary of data that you want to use for the pointLabel

            As an example I have decided I want a box at the curve point
            with some text information about the curve plotted below.
            Any wxDC method can be used.
        """

        # ----------
        dc.SetPen(wx.Pen(wx.BLACK))
        dc.SetBrush(wx.Brush( wx.BLACK, wx.SOLID ) )
       
        sx, sy = mDataDict["scaledXY"] #scaled x,y of closest point
        dc.DrawRectangle( sx-5,sy-5, 10, 10)  #10by10 square centered on point
        px,py = mDataDict["pointXY"]
        cNum = mDataDict["curveNum"]
        pntIn = mDataDict["pIndex"]
        legend = mDataDict["legend"]
        #make a string to display
        s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum, legend, px, py, pntIn)
        dc.DrawText(s, sx , sy+1)
        # -----------


    def onNewTest(self, test):
        index = self.TestListCtrl.InsertStringItem(sys.maxint, test.descr)
        self.TestListCtrl.SetStringItem(index, 1, "%.1f V" %test.voltage)
        self.TestListCtrl.SetStringItem(index, 2, str(test.speed))
        self.TestListCtrl.SetStringItem(index, 3, test.channel)

        vv = int(test.getVibValue(self.app.settings["hpf"].value, self.app.settings["lpf"].value))
        vvs = "|%s| (%d)" % ("----------------------------------------------------------------------------------------------------"[0:min(vv,100)], vv)
        self.TestListCtrl.SetStringItem(index, 4, vvs)
        self.TestListCtrl.Select(index)


    def OnTestSelected(self, event):
         testId = event.m_itemIndex
         print "Test Selected id=%d" % (testId)
         self.activeTestId = testId
         self.drawGraph()

    def drawGraph(self):
         
         y = int(self.yAxisChoice.GetStringSelection())

         nbSelected = self.TestListCtrl.SelectedItemCount

         if nbSelected == 0:
              self.graphCtrl.Clear()
         
         elif nbSelected > 1:
             self.graphTypeChoice.Disable()
             self.copyGraphButton.Enable()
             x = 1
             data = []
             idx = self.TestListCtrl.GetFirstSelected()
             while idx != -1:
                 data.append([x,self.app.getTest(idx).getVibValue(self.app.settings["hpf"].value, self.app.settings["lpf"].value)])
                 x += 1
                 idx = self.TestListCtrl.GetNextSelected(idx)
             line = wx.lib.plot.PolyLine(data, legend= 'Vibrations', colour='red', width=2)
             markers = wx.lib.plot.PolyMarker(data, legend= '', colour='red', marker='circle',size=2)
             title = "Comparing tests"
             self.graphCtrl.setLogScale((False,False))
             self.graphCtrl.Draw(wx.lib.plot.PlotGraphics([line, markers], title, "Test", "Vibration Value"), xAxis=(1,max(x,10)), yAxis=(0,y))
             self.graphCtrl.SetEnableGrid('Horizontal')

         else:
             self.graphTypeChoice.Enable()
             self.copyGraphButton.Disable()
             vibTest = self.app.getTest(self.activeTestId)
             nb = vibTest.getDataLen()

             if self.graphTypeChoice.GetSelection() == 0:
                 xydata = _Numeric.linspace(0,0.09*nb,2*nb)
                 xydata.shape = (nb, 2)
                 xydata[:,1] = vibTest.getRawData()
                 line = wx.lib.plot.PolyLine(xydata, legend= 'Raw Data', colour='red', width=2)
       
                 title = "Raw Signal: %s %s %d" %(vibTest.descr, vibTest.channel, vibTest.speed)
                 self.graphCtrl.setLogScale((False,False))
                 self.graphCtrl.Draw(wx.lib.plot.PlotGraphics([line], title, "Time (ms)", "Acc"), yAxis= (-y,y))
                 self.graphCtrl.SetEnableGrid('Horizontal')
   
             if self.graphTypeChoice.GetSelection() == 1:
                 xydata = _Numeric.linspace(0,0.09*nb,2*nb)
                 xydata.shape = (nb, 2)
                 xydata[:,1] = vibTest.getFilteredData(self.app.settings["hpf"].value, self.app.settings["lpf"].value)
                 line = wx.lib.plot.PolyLine(xydata, legend= 'Raw Data', colour='red', width=2)
       
                 title = "Filtered Signal: %s %s %d" %(vibTest.descr, vibTest.channel, vibTest.speed)
                 self.graphCtrl.setLogScale((False,False))
                 self.graphCtrl.Draw(wx.lib.plot.PlotGraphics([line], title, "Time (ms)", "Acc"), yAxis= (-y,y))
                 self.graphCtrl.SetEnableGrid('Horizontal')
   
             elif self.graphTypeChoice.GetSelection() == 2:
                 xydata = _Numeric.linspace(0,FS/2,nb)
                 xydata.shape = (nb/2, 2)
                 
                 xydata[:,1] = vibTest.getSpectrum()
                 
                 #print xydata
   
                 line = wx.lib.plot.PolyLine(xydata, legend= 'Spectrum', colour='red')
                 markers = wx.lib.plot.PolyMarker(xydata, legend= '', colour='red', marker='circle',size=2)
                 
                 fc = self.app.settings["hpf"].value
                 filterLine1 = wx.lib.plot.PolyLine(((fc,0),(fc,y)), legend='HP Filter', colour='Black', width=4)
                 fc = self.app.settings["lpf"].value
                 filterLine2 = wx.lib.plot.PolyLine(((fc,0),(fc,y)), legend='HP Filter', colour='Black', width=4)
       
                 title = "Spectrum: %s %s %d" %(vibTest.descr, vibTest.channel, vibTest.speed)
                 self.graphCtrl.SetEnableGrid(True)
                 self.graphCtrl.setLogScale((False,False))
                 self.graphCtrl.Draw(wx.lib.plot.PlotGraphics([line,markers, filterLine1, filterLine2], title, "Freq (Hz)", "Acc"), xAxis=(0,300), yAxis= (-0,y))
                 


    def OnImport(self, event): # wxGlade: MainFrame.<event_handler>
        dlg = wx.FileDialog(
            self, message="Choose a file",
            defaultDir="%s/Data/" % os.getcwd(),
            defaultFile="*.txt",
            wildcard="*.txt",
            style=wx.OPEN | wx.CHANGE_DIR
            )
        if dlg.ShowModal() == wx.ID_OK:
            paths = dlg.GetPaths();
            self.app.loadTests(paths[0])
        dlg.Destroy()

    def onExport(self, event): # wxGlade: MainFrame.<event_handler>
        dlg = wx.FileDialog(
              self, message="Save file as ...",
              defaultDir="%s/Data/" % os.getcwd(),
              defaultFile="*.txt",
              wildcard="",
              style=wx.SAVE
              )
        if dlg.ShowModal() == wx.ID_OK:
            paths = dlg.GetPaths();
            self.app.saveTests(paths[0])
        dlg.Destroy()

    def onYAxisChange(self, event): # wxGlade: MainFrame.<event_handler>
        self.drawGraph()

    def onGraphTypeChange(self, event): # wxGlade: MainFrame.<event_handler>
        self.drawGraph()

    def OnSettings(self, event): # wxGlade: MainFrame.<event_handler>
        dlg = SettingsDialog(self, -1, "Sample Dialog", size=(350, 200),
                         #style=wx.CAPTION | wx.SYSTEM_MENU | wx.THICK_FRAME,
                         style=wx.DEFAULT_DIALOG_STYLE, # & ~wx.CLOSE_BOX
                         )
        dlg.CenterOnScreen()
        val = dlg.ShowModal()  # this does not return until the dialog is closed.
        dlg.Destroy()
        self.app.onSettingsChanged()

    def onStartMeasure(self, event): # wxGlade: MainFrame.<event_handler>
        # Collect measure parameters
        mp = MeasureParameters()
        mp.descr = self.tcDescr.GetValue()
        mp.motors = map(int,self.tcMotors.GetValue().split(','))
        mp.channels = map(int,self.tcChannels.GetValue().split(','))
        s = self.tcSpeeds.GetValue()
        if s=="test":
          mp.speeds = (100,100,100,100,100, 150,150,150,150,150, 200,200,200,200,200, 100,150,200, 100,150,200, 100,150,200, 100,150,200)
        elif s.count("-") == 1:
            # assume from-to:step format
            s = s.split("-")
            if len(s) != 2: raise Exception("Invalid format")
            s[1] = s[1].split(":")
            if len(s[1]) != 2: raise Exception("Invalid format")
            mp.speeds = range(int(s[0]),int(s[1][0])+int(s[1][1]),int(s[1][1]))
        else:
            mp.speeds = map(int,s.split(','))
           
       
        # create the dialog that will show the satus
        dlg = MeasureDialog(self)
        dlg.CenterOnScreen()
       
        # Signal the application
        self.app.startMeasure(mp, dlg)
       
        # Show the dialog
        val = dlg.ShowModal()  # this does not return until the dialog is closed.
        dlg.Destroy()

    def _removeTest(self, idx):
        print "Deleting test %d" % idx
        self.app.removeTest(idx)
        self.TestListCtrl.DeleteItem(idx)
       
           
    def onClear(self, event): # wxGlade: MainFrame.<event_handler>
        print "Clear all tests"
        for i in range(len(self.app.VibTests)-1, -1, -1):
            self._removeTest(i)
        self.drawGraph()
           
       
    def onClearSelected(self, event): # wxGlade: MainFrame.<event_handler>
        while True:
            idx = self.TestListCtrl.GetFirstSelected()
            if idx == -1: break
            self._removeTest(idx)
           
       

    def onAbout(self, event): # wxGlade: MainFrame.<event_handler>
         # First we create and fill the info object
        print "about"
        info = wx.AboutDialogInfo()
        info.Name = "MK Vibration Test"
        info.Version = "0.2"
        info.Copyright = ""
        info.Developers=["Frederic Goddeeris"]
        info.Description = "Please consult the WIKI page for a complete description of the tool:"
        info.WebSite = ("http://www.mikrokopter.de/ucwiki/en/VibrationTest", "VibrationTest WIKI page")
        wx.AboutBox(info)
       
       
    def onExit(self, event): # wxGlade: MainFrame.<event_handler>
        self.Close(True)


    def onSelectAll(self, event): # wxGlade: MainFrame.<event_handler>
        for i in xrange(self.TestListCtrl.GetItemCount()):
          self.TestListCtrl.Select(i)

    def onCopyGraphData(self, event): # wxGlade: MainFrame.<event_handler>
        clipdata = wx.TextDataObject()
        txt = ""
        idx = self.TestListCtrl.GetFirstSelected()
        while idx != -1:
             txt += ("%d\n" % self.app.getTest(idx).getVibValue(self.app.settings["hpf"].value, self.app.settings["lpf"].value))
             idx = self.TestListCtrl.GetNextSelected(idx)
        clipdata.SetText(txt)
        wx.TheClipboard.Open()
        wx.TheClipboard.SetData(clipdata)
        wx.TheClipboard.Close()

# end of class MainFrame

class Setting:
    def __init__(self, descr, defaultValue):
        self.descr = descr
        self.value = defaultValue
       
    def set(self, newValue):
        if isinstance(self.value, int):
            self.value = int(newValue)
        else:
            self.value = str(newValue)

class MeasureParameters:
      pass

class MeasureThread:
    def __init__(self, measureParameters, evtConsumer):
        self.mk = mkProto.MkComm()
        self.param = measureParameters
        self.evtConsumer = evtConsumer
        self.cancel = False
        self.running = False
       
    def start(self):
        thread.start_new_thread(self._run, ())
       
    def stop(self):
        self.cancel = True

    def _testCancel(self):
        if self.cancel:
            raise Exception("Operation cancelled")
               
    def _sendEvent(self, msg=None, error=False, parVoltage=None, speed=None):
        evt = MeasStatusUpdateEvent(running=self.running, msg=msg, error=error, voltage=parVoltage, speed=speed)
        wx.PostEvent(self.evtConsumer, evt)

    def _setMotorSpeed(self, speed, settlingTime):
        speeds = [0,0,0,0]
        for motor in self.param.motors:
            speeds[motor-1] = speed
        for i in range(int(settlingTime*10)):
          self._testCancel()
          self.mk.setMotorTest(speeds)
          time.sleep(.1)
       
                   
    def _run(self):
        self.running = True
        self._sendEvent("Starting test \"%s\"" % self.param.descr)    

        try:
            self._sendEvent("Opening SerialPort \"%s\"" % self.param.serialPort)
            self.mk.open(comPort=self.param.serialPort)
               
            msg = self.mk.getVersionMsg()
            version = msg.getVersion()
            self._sendEvent("Version: %d.%d" % version)
           
            msg = self.mk.getDebugMsg()
            voltage = msg.getVoltage()
            if (voltage == 0):
              # Board is probably fed by USB
              minVoltage = 0
              maxVoltage = 1
            else:
              # Determine the n umber of cells
              if (voltage > 4.2*3):
                nbCells = 4  
              else:
                nbCells = 3
              # Set minimum and maximum voltages
              if self.param.minVoltage > 0:
                minVoltage =  self.param.minVoltage
              else:
                minVoltage = nbCells*3.5  # auto
              if self.param.maxVoltage > 0:
                maxVoltage =  self.param.maxVoltage
              else:
                maxVoltage = nbCells*3.9  
               
            self._sendEvent("Voltage: %2.1fV" % voltage)
            self._sendEvent("Min/Max Voltage: %2.1fV-%2.1fV" % (minVoltage, maxVoltage), parVoltage=(minVoltage, maxVoltage, voltage))
           
            self._sendEvent("Starting motor(s) (speed=%d)... " % self.param.motorStartupSpeed, speed=self.param.motorStartupSpeed)
            self._setMotorSpeed(self.param.motorStartupSpeed, self.param.motorStartupSettlingTime)
         
            for speed in self.param.speeds:
                self._sendEvent("Changing motor speed to %d... " % speed, speed=speed)
                self._setMotorSpeed(speed, 1)
               
                for channel in self.param.channels:
                    self._setMotorSpeed(speed, .1)
                    msg = self.mk.getDebugMsg()
                    voltage = msg.getVoltage()
           
                    self._sendEvent("Getting data from channel %s" % CHANNEL_NAMES[channel], parVoltage=(minVoltage, maxVoltage, voltage))
                    data = self.mk.doVibrationTest(1000, channel)
                   
                    vt = VibTest(self.param.descr, voltage, self.param.motors, speed, CHANNEL_NAMES[channel], data)
                    evt = MeasDataEvent(vibTest = vt)
                    wx.PostEvent(self.evtConsumer, evt)
                   
                    if voltage<minVoltage:
                        raise Exception("Voltage too low")
                   
            self._sendEvent("Done !")            
            self._setMotorSpeed(speed, .1)
       
        except Exception, e:
            self._sendEvent("Exception \"%s\"" % e, error=True)  
               
        self.running = False
        self._sendEvent("", speed = 0)    
       

class VibTest:
    def __init__(self, descr, voltage, motor, speed, channel, rawData):
        self.descr = descr
        self.voltage = voltage
        self.motor = motor
        self.speed = speed
        self.channel = channel

        self.dataLen = len(rawData)

        self.rawData = _Numeric.array(rawData)
        self.dc = self.rawData.mean()
        self.rawData -= self.dc

        self.fft = _Numeric.fft.rfft(self.rawData)

        self.spectrum = None
        self.filteredData = None
        self.fc1 = None
        self.fc2 = None
       
        self.vibValue = None
       
    def getDescr(self):
        return self.Descr

    def getRawData(self):
        return self.rawData

    def getDataLen(self):
        return self.dataLen

    def getSpectrum(self):
        if self.spectrum == None:
            self.spectrum = _Numeric.absolute(self.fft[1:self.dataLen/2+1]) / (self.dataLen/2)
        return self.spectrum

    def getFilteredData(self, fc1, fc2):
        if self.fc1 != fc1 or self.fc2 != fc2:
            self.filteredData = None  
           
        if self.filteredData == None:
            tmpfft = self.fft.copy()
            fc = (float(fc1))/(FS/2)*len(tmpfft)
            print "fc1=%d => fc=%d" % (fc1,fc)
            for i in range(0,int(fc)):
                tmpfft[i] = 0
            fc = (float(fc2))/(FS/2)*len(tmpfft)
            print "fc2=%d => fc=%d" % (fc2,fc)
            for i in range(int(fc), len(tmpfft)):
                tmpfft[i] = 0
            self.filteredData = _Numeric.fft.irfft(tmpfft)
            self.fc1 = fc1
            self.fc2 = fc2
           
        return self.filteredData
       
    def getVibValue(self, fc1, fc2):
      if self.fc1 != fc1 or self.fc2 != fc2:
        self.vibValue = None  
      if self.vibValue == None:
        fd = self.getFilteredData(fc1, fc2)[100:-100];
        self.vibValue = max(fd)-min(fd)
      return self.vibValue



class App(wx.App):

    SETTINGSFILE = "settings.cfg"

    def __init__(self, par):
        self.VibTests = []
        wx.App.__init__(self, par)

        # Init settings
        self.settings={}
        self.settings["serialport"] = Setting("Serial Port", "COM1")
        self.settings["startupspeed"] = Setting("Motor Startup Speed", 25)
        self.settings["startupsettling"] = Setting("Motor Startup Setting time (s)", 3)
        self.settings["serialport"] = Setting("Serial Port", "COM1")
        self.settings["hpf"] = Setting("HP Filter cutoff (Hz)", 50)
        self.settings["lpf"] = Setting("LP Filter cutoff (Hz)", 290)
        self.settings["minvoltage"] = Setting("Minimum Bettery Voltage (0=Automatic) (V) ", 0)
        self.settings["maxvoltage"] = Setting("Maximum Bettery Voltage (0=Automatic) (V) ", 0)


        self.readSettings()

        if len(sys.argv)>1:
            self.loadTests(sys.argv[1])


    def readSettings(self):
        print "Reading settings"
        cp = ConfigParser.ConfigParser()

        try:
            cp.read(App.SETTINGSFILE)
            for setting in cp.items("DEFAULT"):
                print " ",setting
                try:
                    self.settings[setting[0]].set(setting[1])
                except:
                    print "WARNING, unknown setting"
        except:
            print "ERROR reading settingsfile"


    def storeSettings(self):
        print "Storing settings"

        cp = ConfigParser.ConfigParser()
        for setting in self.settings.iteritems():
            cp.set("", setting[0], setting[1].value)

        file = open(App.SETTINGSFILE, "w")
        cp.write(file)
        file.close()


    def onSettingsChanged(self):
        self.storeSettings()
     
    def AddTest2(self, vibTest):
        self.VibTests.append(vibTest)
        self.frame_1.onNewTest(vibTest)
         
    def AddTest(self, descr, voltage, motor, speed, channel, rawData):
        test = VibTest(descr, voltage, motor, speed, channel, rawData)
        self.AddTest2(test)

    def removeTest(self, idx):
        del(self.VibTests[idx])
   
    def getTest(self, testId):
        return self.VibTests[testId]

    def OnInit(self):
        wx.InitAllImageHandlers()
        self.frame_1 = MainFrame(None, -1, "")
        self.frame_1.setApp(self);
        self.SetTopWindow(self.frame_1)

        self.frame_1.CenterOnScreen()
        self.frame_1.Show()
        return 1

    def saveTests(self, filePath):
        try:
          logfile = open(filePath, "r")
          newFile = False
          logfile.close()
        except:
          newFile = True
         
        for test in self.VibTests:
          if newFile:
            logfile = open(filePath, "w")
            print "Writing result to %s ..." % filePath,
            logfile.write("%s %d %s\n" % (test.descr, test.speed, test.channel))
            for value in test.rawData:
              logfile.write("%d\n" % value)
            logfile.close()  
            print "OK"
          else:
            print "Appending result to %s ..." % filePath,
            logfile = open(filePath, "r")
            prevData = []
            for line in logfile:
              prevData.append(line[:-1])
            logfile.close()
            logfile = open(filePath, "w")
            logfile.write("%s,%s %d %s\n" % (prevData[0], test.descr, test.speed, test.channel))
            i = 1
            for value in test.rawData:
              logfile.write("%s,%d\n" % (prevData[i], value))
              i += 1
            logfile.close()
            print "OK"
          newFile = False
           
           
    def loadTests(self, filePath):
       
        print "Importing file \"%s\"" % filePath

        logfile = open(filePath, "r")
        data = None

        headers = (logfile.readline()[:-1]).split(',')
        nbCols = len(headers)
        print "NbCols =", nbCols

        data = []
        descr = []
        speed = []
        channel = []
        for c in range(nbCols):
            data.append([])
            h = headers[c].split(' ')
            descr.append(h[0]);
            speed.append(h[1]);
            channel.append(h[2]);

        for line in logfile:
            values = line.split(',')
            for i in range(nbCols):
                data[i].append(int(values[i]))
        logfile.close()

        for c in range(nbCols):
            if (len(data[c]) % 2) != 0:
                data[c].append(data[c][-1])
            self.AddTest(descr[c], 0, 0, int(speed[c]), channel[c], data[c])
           
    def startMeasure(self, measureParams, dialog):
        print "Start measuring"
       
        measureParams.serialPort = self.settings["serialport"].value
        measureParams.motorStartupSpeed = self.settings["startupspeed"].value
        measureParams.motorStartupSettlingTime = self.settings["startupsettling"].value
        measureParams.minVoltage = self.settings["minvoltage"].value
        measureParams.maxVoltage = self.settings["maxvoltage"].value        
       
        self.measureThread = MeasureThread(measureParams, dialog)
        self.measureThread.start()
       
    def cancelMeasurement(self):
        print "Measuring CANCEL"
       
        self.measureThread.stop()
       
       
       
       
       
           

       
# end of class App

if __name__ == "__main__":

    rootPath = os.path.abspath(os.path.dirname(sys.argv[0]))
   
    print rootPath

    VibrationTestGui = App(0)
    VibrationTestGui.MainLoop()