Subversion Repositories Projects

Rev

Rev 701 | Rev 717 | 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

#
# Mikrokopter VibrationTest  Rev: $Rev: 701 $
#
# Author: Frederic Goddeeris   (frederic@rc-flight.be)
#

import sys
import os
import time
import thread
import ConfigParser
import math

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

import MkProtocol



CHANNEL_NAMES = ["GyroYaw", "GyroRoll", "GyroNick", "Pressure", "Batt", "AccTop", "AccRoll", "AccNick"]
MOTOR_MAX = 16

FS = 11111
pi = 3.14
COLOR_YELLOW = wx.Colour(255, 240, 0)
COLOR_BACKGROUND = wx.Colour(0x80, 0x80, 0x80)

COLORS = [wx.RED, wx.GREEN, wx.BLUE, COLOR_YELLOW, COLOR_BACKGROUND, wx.BLACK,]*2

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()
       
        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
       
        # Configure Voltage Ctrl
        self.voltageCtrl.SetAngleRange(0,pi)
        intervals = range(0, 5)
        self.voltageCtrl.SetIntervals(intervals)
        colours = [wx.RED, wx.GREEN, wx.GREEN, COLOR_YELLOW]
        self.voltageCtrl.SetIntervalColours(colours)
        ticks = ["", "", "", "", ""]
        self.voltageCtrl.SetTicks(ticks)
        self.voltageCtrl.SetTicksColour(wx.WHITE)
        self.voltageCtrl.SetHandColour(COLOR_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(COLOR_BACKGROUND)        
        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 = [COLOR_BACKGROUND]*(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(COLOR_YELLOW)
        self.speedCtrl.SetSpeedBackground(COLOR_BACKGROUND)  
        self.speedCtrl.SetArcColour(wx.WHITE)        
        self.speedCtrl.SetSpeedValue(0)


    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),4)
            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, "Delete All\tAlt+D", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.Append(302, "Delete Selected\tCtrl+D", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendSeparator()
        wxglade_tmp_menu.Append(310, "Select All\tCtrl+A", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendSeparator()
        wxglade_tmp_menu.Append(303, "Load\tCtrl+L", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.Append(304, "Save\tCtrl+S", "", 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.label_1 = wx.StaticText(self, -1, "Test Description :", style=wx.ALIGN_RIGHT)
        self.descrCtrl = wx.TextCtrl(self, -1, "N/A")
        self.label_1_copy = wx.StaticText(self, -1, "Channel(s) :", style=wx.ALIGN_RIGHT)
        self.gyroYawCb = wx.CheckBox(self, -1, "Gyro Yaw")
        self.gyroRollCb = wx.CheckBox(self, -1, "Gyro Roll")
        self.gyroNickCb = wx.CheckBox(self, -1, "Gyro Nick")
        self.accTopCb = wx.CheckBox(self, -1, "ACC Top")
        self.accRollCb = wx.CheckBox(self, -1, "ACC Roll")
        self.accNickCb = wx.CheckBox(self, -1, "ACC Nick")
        self.label_3 = wx.StaticText(self, -1, "Motor(s) :", style=wx.ALIGN_RIGHT)
        self.motorsCtrl = wx.TextCtrl(self, -1, "1")
        self.label_4 = wx.StaticText(self, -1, "Speed(s) :")
        self.speedCtrl = wx.TextCtrl(self, -1, "100-200:10")
        self.bitmap_button_1 = wx.BitmapButton(self, -1, wx.Bitmap("Resources\\Fairytale_player_play.png", wx.BITMAP_TYPE_ANY))
        self.static_line_1 = wx.StaticLine(self, -1)
        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", "75", "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.bitmap_button_1)
        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)
        self.graphCtrl.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
        self.measureState = 0
       

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

    def __set_properties(self):
        # begin wxGlade: MainFrame.__set_properties
        self.SetTitle("VibrationTest")
        self.SetSize((850, 700))
        self.label_1.SetMinSize((110, -1))
        self.label_1.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.descrCtrl.SetMinSize((350, -1))
        self.label_1_copy.SetMinSize((110, -1))
        self.label_1_copy.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.gyroYawCb.SetMinSize((100, -1))
        self.gyroYawCb.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.gyroRollCb.SetMinSize((100, -1))
        self.gyroRollCb.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.gyroNickCb.SetMinSize((100, -1))
        self.gyroNickCb.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.accTopCb.SetMinSize((100, -1))
        self.accTopCb.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.accRollCb.SetMinSize((100, -1))
        self.accRollCb.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.accRollCb.SetValue(1)
        self.accNickCb.SetMinSize((100, -1))
        self.accNickCb.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.label_3.SetMinSize((110, -1))
        self.label_3.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.label_4.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.speedCtrl.SetToolTipString("e.g. \n 100 \n 100,150 \n 100-200:10 \n 50,100-200:10 \n 5*100,5*200")
        self.bitmap_button_1.SetToolTipString("Start Measurement")
        self.bitmap_button_1.SetSize(self.bitmap_button_1.GetBestSize())
        self.static_line_1.SetMinSize((800,3))
        self.graphCtrl.SetMinSize((800,300))
        self.label_40.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.graphTypeChoice.SetSelection(0)
        self.label_41.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.yAxisChoice.SetSelection(1)
        self.copyGraphButton.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        self.TestListCtrl.SetMinSize((800,300))
        self.TestListCtrl.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        # 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_17 = wx.BoxSizer(wx.VERTICAL)
        sizer_9 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_7 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_13 = wx.BoxSizer(wx.VERTICAL)
        sizer_16 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_10 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_18 = wx.BoxSizer(wx.VERTICAL)
        sizer_20 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_19 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_14 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_3.Add((20, 20), 0, 0, 0)
        sizer_8.Add((20, 20), 0, 0, 0)
        sizer_14.Add(self.label_1, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_14.Add((20, 20), 0, 0, 0)
        sizer_14.Add(self.descrCtrl, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_13.Add(sizer_14, 0, 0, 0)
        sizer_13.Add((20, 5), 0, 0, 0)
        sizer_10.Add(self.label_1_copy, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_10.Add((20, 20), 0, 0, 0)
        sizer_19.Add(self.gyroYawCb, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_19.Add((20, 20), 0, 0, 0)
        sizer_19.Add(self.gyroRollCb, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_19.Add((20, 20), 0, 0, 0)
        sizer_19.Add(self.gyroNickCb, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_18.Add(sizer_19, 1, wx.EXPAND, 0)
        sizer_20.Add(self.accTopCb, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_20.Add((20, 20), 0, 0, 0)
        sizer_20.Add(self.accRollCb, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_20.Add((20, 20), 0, 0, 0)
        sizer_20.Add(self.accNickCb, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_18.Add(sizer_20, 1, wx.EXPAND, 0)
        sizer_10.Add(sizer_18, 1, wx.EXPAND, 0)
        sizer_13.Add(sizer_10, 1, wx.EXPAND, 0)
        sizer_13.Add((20, 5), 0, 0, 0)
        sizer_16.Add(self.label_3, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_16.Add((20, 20), 0, 0, 0)
        sizer_16.Add(self.motorsCtrl, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_16.Add((50, 20), 0, 0, 0)
        sizer_16.Add(self.label_4, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_16.Add((20, 20), 0, 0, 0)
        sizer_16.Add(self.speedCtrl, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_13.Add(sizer_16, 0, wx.EXPAND, 0)
        sizer_7.Add(sizer_13, 1, wx.EXPAND, 0)
        sizer_7.Add((20, 20), 0, 0, 0)
        sizer_7.Add((20, 20), 0, 0, 0)
        sizer_9.Add(sizer_7, 0, 0, 0)
        sizer_9.Add(self.bitmap_button_1, 0, 0, 0)
        sizer_8.Add(sizer_9, 0, 0, 0)
        sizer_17.Add((20, 20), 0, 0, 0)
        sizer_17.Add(self.static_line_1, 0, wx.EXPAND, 0)
        sizer_17.Add((20, 20), 0, 0, 0)
        sizer_8.Add(sizer_17, 0, wx.EXPAND, 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._fillRowInTestList(index, test)
        self.TestListCtrl.Select(index)

    def _fillRowInTestList(self, index, test):
        self.TestListCtrl.SetStringItem(index, 0, 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 = test.getVibValue()
        vvs = "|%s| (%.1f)" % ("----------------------------------------------------------------------------------------------------"[0:min(int(vv+1)/2,100)], vv)
        self.TestListCtrl.SetStringItem(index, 4, vvs)

    def refreshData(self):
        for idx in range(len(self.app.VibTests)):
            self._fillRowInTestList(idx, self.app.getTest(idx))
        self.drawGraph()
           
    def OnTestSelected(self, event):
         testId = event.m_itemIndex
         print "Test Selected id=%d" % (testId)
         self.activeTestId = testId
         self.drawGraph()
         
         
    def orderSelectedTests(self):
        tests = []
        idx = self.TestListCtrl.GetFirstSelected()
        while idx != -1:
            header = "%s %s"%(self.app.getTest(idx).descr,self.app.getTest(idx).channel)
            found = False
            for t in tests:
              if t[0] == header:
                t.append(idx)
                found = True
                break
            if not found:
                tests.append([header, idx])
            idx = self.TestListCtrl.GetNextSelected(idx)
        return tests
     
    def OnMouseLeftDown(self,event):
        x,y = self.graphCtrl._getXY(event)
        s= "Left Mouse Down at Point: (%.4f, %.4f)" % (x,y)
        print s
        if self.measureState == 0:
            self.measureX1 = x
            self.measureState += 1
        elif self.measureState == 1:
            dx = x-self.measureX1
            rpm = 60.0*1000/dx
            s = "Time-Difference: %.1fms\n"%dx
            for i in range(1,8):
                s += "\n%dT -> %d RPM" %(i,rpm*i)
            dial = wx.MessageDialog(None, s, 'RPM Measurement', wx.OK )
            dial.ShowModal()
         
            self.measureState = 0
           
       

    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()
             
             tests = self.orderSelectedTests()
             
             lines = []
             maxX = 0
             cCnt = 0
             for s in tests:
               data = []
               x=1
               for t in s[1:]:
                 data.append([x,self.app.getTest(t).getVibValue()])
                 x += 1
               lines.append(wx.lib.plot.PolyLine(data, legend= s[0], colour=COLORS[cCnt], width=2))
               lines.append(wx.lib.plot.PolyMarker(data, legend= "", colour=COLORS[cCnt], marker='circle',size=2))
               maxX = max(maxX, x)
               cCnt += 1
                       
             title = "Comparing tests"
             self.graphCtrl.setLogScale((False,False))
             self.graphCtrl.Draw(wx.lib.plot.PlotGraphics(lines, title, "Test", "Vibration Value"), xAxis=(1,maxX), yAxis=(0,y))
             self.graphCtrl.SetEnableGrid('Horizontal')
             self.graphCtrl.SetEnableLegend(True)
             

         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/2,y/2))
                 self.graphCtrl.SetEnableGrid('Horizontal')
                 self.graphCtrl.SetEnableLegend(False)
                 
   
             if self.graphTypeChoice.GetSelection() == 1:
                 xydata = _Numeric.linspace(0,0.09*nb,2*nb)
                 xydata.shape = (nb, 2)
                 xydata[:,1] = vibTest.getFilteredData()
                 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/2,y/2))
                 self.graphCtrl.SetEnableGrid('Horizontal')
                 self.graphCtrl.SetEnableLegend(False)
   
             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.setLogScale((True,False))
                 self.graphCtrl.Draw(wx.lib.plot.PlotGraphics([line,markers, filterLine1, filterLine2], title, "Freq (Hz)", "Acc"), xAxis=(20,500), yAxis= (0,y))
                 self.graphCtrl.SetEnableGrid(True)
                 self.graphCtrl.SetEnableLegend(False)
                 


    def OnImport(self, event): # wxGlade: MainFrame.<event_handler>
        dlg = wx.FileDialog(
            self, message="Choose a file",
            defaultDir="%s/Data/" % os.getcwd(),
            defaultFile=".",
            wildcard="Text files (*.txt)|*.txt|All files (*.*)|*.*",
            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=".",
              wildcard="Text files (*.txt)|*.txt|All files (*.*)|*.*",
              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(True)

    def onStartMeasure(self, event): # wxGlade: MainFrame.<event_handler>
        # Collect measure parameters
        mp = MeasureParameters()
        decoding = ""
       
        try:
          decoding = "Description"
          mp.descr = self.descrCtrl.GetValue()

          decoding = "Motor(s)"
          mp.motors = map(int,self.motorsCtrl.GetValue().split(','))
          for motor in mp.motors:
              if (motor<1) or (motor>MOTOR_MAX):
                  raise Exception("Motor number should be between 1 and %d" % MOTOR_MAX)

          decoding = "Channel(s)"
          mp.channels = []
          if self.gyroYawCb.IsChecked(): mp.channels.append(0)
          if self.gyroRollCb.IsChecked(): mp.channels.append(1)
          if self.gyroNickCb.IsChecked(): mp.channels.append(2)
          if self.accTopCb.IsChecked(): mp.channels.append(5)
          if self.accRollCb.IsChecked(): mp.channels.append(6)
          if self.accNickCb.IsChecked(): mp.channels.append(7)

          decoding = "Speed(s)"
          mp.speeds = []
          for speedTxt in self.speedCtrl.GetValue().split(","):
              if speedTxt.count("-") == 1:
                  # assume from-to:step format
                  speedTxt = speedTxt.split("-")
                  if len(speedTxt) != 2: raise Exception("Invalid format")
                  speedTxt[1] = speedTxt[1].split(":")
                  if len(speedTxt[1]) != 2: raise Exception("Invalid format")
                  mp.speeds = range(int(speedTxt[0]),int(speedTxt[1][0])+int(speedTxt[1][1]),int(speedTxt[1][1]))
              else:
                  # assume s or s*n format
                  if speedTxt.count("*") == 1:
                      speedTxt = speedTxt.split("*")
                      for i in range(int(speedTxt[0])):
                        mp.speeds.append(int(speedTxt[1]))
                  else:
                      mp.speeds.append(int(speedTxt))
          for speed in mp.speeds:
                if (speed<0) or (speed>255):
                    raise Exception("Speed values should be between 0 and 255")

        except Exception,e:
          dial = wx.MessageDialog(None, 'Invalid paramter \"%s\"\n\n%s' % (decoding, str(e)), 'Error', wx.OK |
            wx.ICON_ERROR)
          dial.ShowModal()
          raise e

         
           
        print mp.descr
        print mp.motors
        print mp.channels
        print mp.speeds
       
           
       
        # 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>
        dial = wx.MessageDialog(None, 'Clear ALL tests?', 'Question',
            wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
        if dial.ShowModal() == wx.ID_YES:
          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>
        dial = wx.MessageDialog(None, 'Clear Selected tests?', 'Question',
            wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
        if dial.ShowModal() == wx.ID_YES:
          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 = "v1.2 ($Rev: 701 $)".replace("$","")
        info.Copyright = ""
        info.Developers=["Frederic Goddeeris  (Frederic@rc-flight.be)"]
        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())
             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 = MkProtocol.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]*MOTOR_MAX
       
        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)
        self.currSpeed = speed
       
                   
    def _run(self):
        self.running = True
        self._sendEvent("Starting test \"%s\"" % self.param.descr)    
        self.currSpeed = 0
       
        try:
            self._sendEvent("Opening SerialPort \"%s\"" % self.param.serialPort)
            self.mk.open(comPort=self.param.serialPort)
           
            print "Sending FC->NC forwarding",
            self.mk.sendNCRedirectUartFromFC()
            print "Done"
               
            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:
                if speed != self.currSpeed:
                    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._setMotorSpeed(speed, .1)    
            time.sleep(1)
            msg = self.mk.getDebugMsg()
            voltage = msg.getVoltage()
           
            self._sendEvent("Done !", parVoltage=(minVoltage, maxVoltage, voltage))            
           
        except Exception, e:
            self._sendEvent("Exception \"%s\"" % e, error=True)  
       
        try:        
          self.mk.close()
        except:
          print "Failure closing MK serial port"
          pass
       
        self.running = False
        self._sendEvent("", speed = 0)    
       

class VibTest:
    useRms = True
    fc1 = None
    fc2 = None
       
   
    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.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 refresh(self):
        self.filteredData = None
        self.vibValue = None

    def getFilteredData(self):
        if self.filteredData == None:
            tmpfft = self.fft.copy()
            fc = (float(self.fc1))/(FS/2)*len(tmpfft)
            print "fc1=%d => fc=%f" % (self.fc1, fc)
            for i in range(0,int(fc)+2):
                tmpfft[i] = 0
            fc = (float(self.fc2))/(FS/2)*len(tmpfft)
            print "fc2=%d => fc=%f" % (self.fc2,fc)
            for i in range(int(fc)+2, len(tmpfft)):
                tmpfft[i] = 0
            self.filteredData = _Numeric.fft.irfft(tmpfft)
           
        return self.filteredData
       
    def getVibValue(self):
      if self.vibValue == None:
        fd = self.getFilteredData()[100:-100];
        if self.useRms:
            print "RMS"
            self.vibValue = math.sqrt(sum([x*x for x in fd])/len(fd))*2*math.sqrt(2)
        else:
            print "PP"
            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)", 40)
        self.settings["lpf"] = Setting("LP Filter cutoff (Hz)", 400)
        self.settings["calcmethod"] = Setting("Calculation Method", "rms")        
        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("%s/%s" % (rootPath, 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"
        self.onSettingsChanged(False)

    def storeSettings(self):
        print "Storing settings"

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

        file = open("%s/%s" % (rootPath, App.SETTINGSFILE), "w")
        cp.write(file)
        file.close()


    def onSettingsChanged(self, store):
        if store:
            self.storeSettings()
           
        if self.settings["calcmethod"].value == "rms":
            VibTest.useRms = True
        else:
            VibTest.useRms = False
           
        VibTest.fc1 = self.settings["hpf"].value
        VibTest.fc2 = self.settings["lpf"].value
       
        for test in self.VibTests:
            test.refresh()
        self.frame_1.refreshData()
     
    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()