#! /usr/bin/env python
"""
Acoustic Startle Program
Python version

This program generates sound in one channel (L), followed by a startle stimulus
(noise burst) in the other channel (R).
The conditioning sound can be one of:
tone pip before the startle
tone with gap before the startle
bandpass noise burst
bandpass noise burst with gap before the startle

Requires: startle2.rco, generated by RPvds (from TDT)
Requires: PySounds.py which requires pyaudio and nidaq
Requires: MPlot.py - a wrapper for Qwt to mimic, sort of, matlab plotting

For PySounds:
Output hardware is either an National Instruments DAC card or a system sound card
If the NI DAC is available, TDT system 3 hardware is assumed as well for the
attenuators (PA5) and an RP2.1 to input the startle response.
Second channel of RP2.1 is collected as well. Use this for a microphone input
to monitor sound in the chamber.

Python 2.5
PyQt4, Qt Designer (for Gui), Qwt (widgets for graphics)
scipy, pylab, numpy, matplotlib (working on phasing out matplotlib)
pyaudio
stack is optional (working on phasing out stack)

Works with Enthought distribution on Mac OS X and Windows.
"""

# November, 2008
# Paul B. Manis, Ph.D.
# UNC Chapel Hill
# Department of Otolaryngology/Head and Neck Surgery
# Supported by NIH Grants DC000425-22 and DC004551-07 to PBM.
# Copyright Paul Manis, 2008, 2009
#

"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
"""
    Additional Terms:
    The author(s) would appreciate that any modifications to this program, or
    corrections of erros, be reported to the principal author, Paul Manis, at
    pmanis@med.unc.edu, with the subject line "PySounds Modifications". 
    
    Note: This program also relies on the TrollTech Qt libraries for the GUI.
    You must obtain these libraries from TrollTech directly, under their license
    to use the program.
"""
################################################################################
import sys, re, os
import datetime, time
from time import strftime

from PyQt4 import Qt, QtCore, QtGui
import PyQt4.Qwt5 as Qwt
from PyQt4.Qwt5.anynumpy import *

from sets import *
from pylab import *
import scipy
import scipy.signal

import numpy
# from numpy.fft import fft
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
# non-standard stuff:
import stack
from random import sample
# our sound handling module (includes hardware detection and signal generation)
import PySounds
import MPlot
import Utility
# our gui:
from PyStartle_gui import Ui_MainWindow

Sounds = PySounds.PySounds() # instance of sound - and connects to the hardware
MPlots = MPlot.MPlot()
Utils = Utility.Utility()

################################################################################
# One class for the program: PyStartle
################################################################################

class PyStartle(QtGui.QMainWindow):
    
    def __init__(self):
        (self.hardware, self.out_sampleFreq, self.in_sampleFreq) = Sounds.getHardware()
        print "PyStartle is running with output hardware: %s" % (self.hardware)
        """ In the constructor get the application
            started byconstructing a basic QApplication with
            its __init__ method, then adding our slot/signal connections
            and finally starting 
            the exec_loop. """""
        QtGui.QDialog.__init__(self)
        self.debugFlag = False # control printing debug statements.
        self.AutoSave = True
        self.maxptsplot = 20000 # limit the number of points plotted
        self.in_sampleFreq = 44100.0
        self.ch1 = []
        self.ch2 = []
        self.response_tb = []# response time base
        self.stim_tb = []# stimuluation time base (not implemented yet..)
        self.PostDuration = 0.35 # seconds after startle  ends to record response
        self.PPGo = False
        self.PP_Notch_F1 = 12000.0 # set defaults for the notch - not in gui yet
        self.PP_Notch_F2 = 14000.0
        self.fileDate = ''
        self.Description = "Acoustic Startle Parameters"
        self.CurrentTab = 0 # set a default current tab - left most entry
        self.stack1 = stack.Stack()	 # init the stack mode used for holding MOUSE event data
        self.stack2 = stack.Stack()	 # init the stack mode used for holding MOUSE event data
        # We pass None since it's the top-level widget, we could in fact leave 
        # that one out, but this way it's easier to add more dialogs or widgets.
        self.ui = Ui_MainWindow() # this is the ONE THING
        self.ui.setupUi(self)
        self.connect(self.ui.QuitButton,QtCore.SIGNAL("clicked()"),
                     self.slotQuit)
        self.connect(self.ui.actionQuit,QtCore.SIGNAL("clicked()"),
             self.slotQuit) 
        self.connect(self.ui.actionOpen,QtCore.SIGNAL("clicked()"),
             self.Analysis_Read) 
#        self.connect(self.ui.CloseDataWindows,QtCore.SIGNAL("clicked()"),
#                     self.slotCloseDataWindows)
        self.connect(self.ui.ToneTest,QtCore.SIGNAL("clicked()"),
                     self.ToneTest)
        self.connect(self.ui.NoiseTest,QtCore.SIGNAL("clicked()"),
                     self.NoiseTest)
        self.connect(self.ui.PrePulse_Run,QtCore.SIGNAL("clicked()"),
                     self.PrePulseStart)
        self.connect(self.ui.PrePulse_Stop,QtCore.SIGNAL("clicked()"),
                     self.PrePulseStop)
        self.connect(self.ui.Save_Params,QtCore.SIGNAL("clicked()"), 
                     self.writeini)
        self.connect(self.ui.Load_Params,QtCore.SIGNAL("clicked()"), self.readini)
        self.connect(self.ui.Write_Data,QtCore.SIGNAL("clicked()"),
                     self.Write_Data)
        self.connect(self.ui.Analysis_Read,QtCore.SIGNAL("clicked()"),
                     self.Analysis_Read)
        self.connect(self.ui.Analysis_Test,QtCore.SIGNAL("clicked()"),
                     self.Analysis_Test)
        self.connect(self.ui.Analysis_Analyze,QtCore.SIGNAL("clicked()"),
                     self.Analyze_Data)
        self.connect(self.ui.Startle_Debug, QtCore.SIGNAL("clicked()"),
                     self.Startle_Debug)
        self.connect(self.ui.Hardware_Debug, QtCore.SIGNAL("clicked()"),
                     self.Hardware_Debug)
        # timer calls NextTrial when timed out
        self.TrialTimer=QtCore.QTimer() # get a Q timer
        self.connect(self.TrialTimer, QtCore.SIGNAL("timeout()"), self.NextTrial);
        MPlots.setXYReport(self.ui.X_Cursor, self.ui.Y_Cursor) # link the cursor to the display
        self.readini("pystartle.ini") # read the initialization file if it is there.
        self.setMainWindow('default')     
        self.statusBar().showMessage("No File" )   
        self.Status('Welcome to PyStartle V2.0alpha')

################################################################################
# utility routines for Gui:
# close the windows and exit
#
 
    def slotQuit(self):
        try:
            if self.hardware == 'nidaq':
                RP21.Halt() # make sure the RP21 is stopped.
        finally:
            pass
#        self.slotCloseDataWindows() # should close the matplotlib windows... 
        QtCore.QCoreApplication.quit()

#
# just close the data plot windows (matplotlib windows)
#
    def slotCloseDataWindows(self):
        for i in range(1,5):
            try:
                plt.close(i)
            except AttributeError:
                pass

    def getCurrentTab(self):
       self.CurrentTab = self.ui.AcquisitionTabs.currentIndex()
       return(self.CurrentTab)

    def setCurrentTab(self, tab = 0):
       self.ui.AcquisitionTabs.setCurrentIndex(tab)
    
    def Startle_Debug(self):
        self.debugFlag = self.ui.Startle_Debug.isChecked()

    def Hardware_Debug(self):
        flag = self.ui.Hardware_Debug.isChecked()
        if flag:
            Sounds.debugOn()
        else:
            Sounds.debugOff()
                    
# update status window
#
#
    def Status(self, text, clear = 0):
        self.ui.Status_Window.insertItem(0, '[' +
                        datetime.datetime.now().ctime() + ']  ' + text)
        item = self.ui.Status_Window.item(0) # get top item object
        self.ui.Status_Window.setCurrentItem(item)
        self.ui.Status_Window.update() # force an update with every line

    def setMainWindow(self, text):
        self.setWindowTitle("PyStartle [%s]" % (text))
        MPlots.setDefaultBkColor('k')
        # set axis titles
        MPlots.PlotReset(self.ui.qwt_Stimulus_Plot, xlabel='Time (sec)', ylabel='Amp (V)')
        MPlots.PlotReset(self.ui.qwt_Response_Plot1, xlabel='Time (sec)', ylabel='Ch1 (mV)')
        MPlots.PlotReset(self.ui.qwt_Response_Plot2, xlabel='Time (sec)', ylabel='Ch2 (mV)')
        MPlots.PlotReset(self.ui.qwt_Expanded_Signal_Plot, xlabel='Time (sec)', ylabel= 'Amp (mV)')
        MPlots.PlotReset(self.ui.qwt_Spectrum_Plot, xlabel='Freq (kHz)', ylabel= 'Power (V^2/Hz)')
        MPlots.PlotReset(self.ui.qwt_RSpectrum_Plot, xlabel='Freq (Hz)', ylabel='Ch1 (mV^2/Hz)')
        MPlots.PlotReset(self.ui.qwt_LSpectrum_Plot, xlabel='Freq (Hz)', ylabel='Ch2 (mV^2/Hz)')
        MPlots.PlotReset(self.ui.qwt_Discrimination_Plot, xlabel='Trial', ylabel= 'd''')

        allplots = MPlots.getPlotList()
        for plot in allplots:
            MPlots.PlotReset(plot, bkcolor='b')
            
# figure title for matplotlib window... 
    def putTitle(self, infotext):
       pa, fname = os.path.split(self.fileName)
       titletext = 'File: %s  R:[' % (fname)
       for i in self.reclist:
           titletext = titletext + '%d ' % (i)
       titletext = titletext + '] B:[ '
       for i in self.blocklist:
           titletext = titletext + '%d ' % (i)
       titletext = titletext + '] ' + infotext
       gcf().text(0.5, 0.95, titletext, horizontalalignment='center',
                  fontproperties=FontProperties(size=12))

################################################################################
# Read the gui data into our local parameters
################################################################################
    def readParameters(self):
        self.AutoSave = self.ui.AutoSave.isChecked()
# from the Levels and  Durations tab:
        self.CN_Level = self.ui.Condition_Level.value()
        self.CN_Dur = self.ui.Condition_Dur.value()
        self.CN_Var = self.ui.Condition_Var.value()
        self.PP_Level = self.ui.PrePulse_Level.value()
        self.PP_OffLevel = self.ui.PrePulse_Off_Level.value()
        self.PP_Dur = self.ui.PrePulse_Dur.value()
        self.PS_Dur = self.ui.PreStartle_Dur.value()
        self.ST_Dur = self.ui.Startle_Dur.value()
        self.ST_Level = self.ui.Startle_Level.value()
        self.StimEnable = self.ui.Stimulus_Enable.isChecked()
        self.WavePlot = self.ui.Waveform_PlotFlag.isChecked()
        self.ShowSpectrum = self.ui.OnlineSpectrum_Flag.isChecked()
        self.OnLineAnalysis = self.ui.OnlineAnalysis_Flag.isChecked()
# from the Waveforms tab:        
        self.PP_Freq = self.ui.PrePulse_Freq.value()
        self.PP_HP = self.ui.PrePulse_HP.value()
        self.PP_LP = self.ui.PrePulse_LP.value()
        self.PP_Mode = self.ui.Waveform_PrePulse.currentIndex()
        self.CN_Mode = self.ui.Waveform_Conditioning.currentIndex()
        self.PP_GapFlag = self.ui.PrePulse_GapFlag.isChecked()
        self.PP_Notch_F1 = self.ui.PrePulse_Notch_F1.value()
        self.PP_Notch_F2 = self.ui.PrePulse_Notch_F2.value()
        self.PP_MultiFreq = str(self.ui.PrePulse_MultiFreq.text())  
# from the Timing and Trials tab:
        self.ITI_Var = self.ui.PrePulse_ITI_Var.value()
        self.ITI = self.ui.PrePulse_ITI.value()
        self.Trials = self.ui.PrePulse_Trials.value()
        self.NHabTrials = self.ui.PrePulse_NHabTrials.value()
# from the analysis tab:
        self.readAnalysisTab()
        
        
    def readAnalysisTab(self): # we call this elsewhere, - define for convenience
        self.Analysis_Start = self.ui.Analysis_Start.value()
        self.Analysis_End = self.ui.Analysis_Duration.value()
        self.Analysis_HPF = self.ui.Analysis_HPF.value()
        self.Analysis_LPF = self.ui.Analysis_LPF.value()
        
    def ToneTest(self):
        self.readParameters()
        w = Sounds.StimulusMaker(mode='tone', freq = (self.PP_Freq, 0),
                               duration = self.PP_Dur, samplefreq = 44100)
        self.plotSignal(w, w, plotResponse=False)

    def NoiseTest(self):
        self.readParameters()
        w = Sounds.StimulusMaker(mode = 'bpnoise', freq=(self.PP_HP, self.PP_LP),
                               duration=self.PP_Dur, samplefreq = 44100)
        self.plotSignal(w, w, plotResponse=False)
        
################################################################################
#
#   PrePulseRun controls the stimulus presentation and timing.
#   It is the main event loop during stimulation/acquisition.
#
# note : we use QTimer for the timing. One instance is generated with the
# main init routine above. We then start this and run it as a separate thread
# Allows gui interaction during data acquisition/stimulation and ability to
# stop the presentation cleanly.
################################################################################

    def PrePulseStart(self):
        if self.PPGo:
            print "already running"
            return;
        self.Status ("Starting Run")
#
# open and build the file
#
        dt = strftime('%Y%m%d%H%M')
        self.fn = dt + "_Startle.txt"
        self.readParameters() # get the parameters for stimulation
        self.TrialCounter = 0
        self.SpecMax = 0
        itil = self.ITI + self.ITI_Var*(rand(1, self.Trials+self.NHabTrials)-0.5)
        self.ITI_List = itil.reshape(max(shape(itil)))
        stimd = self.CN_Dur + self.CN_Var*(rand(1, self.Trials+self.NHabTrials)-0.5)
        self.Dur_List = stimd.reshape(max(shape(stimd)))
        self.Gap_List = int(self.Trials+self.NHabTrials)*[False]
        list = int(self.Trials/2)*[False, True]
        s=sample(list, int(self.Trials))
        self.Gap_List[int(self.NHabTrials):] = s
        if self.AutoSave:
            self.writeDataFileHeader(self.fn) # wait to write header until we have all the values.
        self.Gap_StartleMagnitude = zeros(self.Trials)
        self.Gap_Counter = 0
        self.noGap_StartleMagnitude = zeros(self.Trials)
        self.noGap_Counter = 0
        self.PPGo = True
        if self.debugFlag:
            print "PrePulseStart: timer starting"
        self.TrialTimer.setSingleShot(True)
        self.TrialTimer.start(10) # start right away

# catch the stop button press 
    def PrePulseStop(self):
        if self.debugFlag:
            print "PrePulseStop: hit detected"
        Sounds.setAttens() # attenuators down
        Sounds.HwOff() # turn hardware off
        self.PPGo=False # signal the prepulse while loop that  we are stopping
        self.statusBar().showMessage("Stimulus/Acquisition Events stopped")

# callback routine to stop timer when thread times out.

    def NextTrial(self):
        if self.debugFlag:
            print "NextTrial: entering"
        self.TrialTimer.stop()
        if self.TrialCounter <= self.Trials and self.PPGo:
            self.statusBar().showMessage("Rep: %d of %d, ITI=%7.2f" % (self.TrialCounter+1,
                                                            self.Trials+self.NHabTrials,
                                                            self.ITI_List[self.TrialCounter]))
            DoneTime = self.ITI_List[self.TrialCounter] # get this before we start stimulus so stim time is included
            self.TrialTimer.start(int(1000.0*DoneTime))
            self.Stim_Dur = self.Dur_List[self.TrialCounter] # randomize the durations a bit too
            self.runOnePP()
            if self.WavePlot == True:
                self.plotSignal(self.wave_outL, self.wave_outR, self.out_sampleFreq)
            if self.AutoSave:
                self.AppendData(self.fn)
            self.TrialCounter = self.TrialCounter + 1
        else:
            self.PPGo = False
            self.statusBar().showMessage("Test Complete")
        if self.debugFlag:
            print "NextTrial: exiting"

################################################################################
# runOnePP - "run one prepulse" trial.
# Generate one stimulus set based on the choice. Builds both channels.
# Presents the stimuli if the flag is set.
################################################################################
# the modes parse as follows (same modes apply for CN/PS, and for PP)
# 0 is silence
# 1 is tone
# 2 is bandpass noise
# 3 is notch noise (not implemented yet)
# 4 is multi tones (not implemented yet)
# 5 is AM tones (not implemented yet)
# 6 is AM Noise (not implemented yet)
# 
# The conditioning (CN) and the prepulse (PP) can be any of the above
# the pre-startle (post prepulse) is always the same as the conditioning.
# The conditioning stimulus always runs the whole duration (including through the end of the startle)
# If the conditioning stimulus is not the same as the prepulse, then the conditiioning
# will be interrupted by a gap during the prepulse period, and the prepulse will be calculated,
# shaped, and added during the prepulse period.
#
    def runOnePP(self):
        if self.debugFlag:
            print "runOnePP: Entering"
        (self.hardware, self.out_sampleFreq, self.in_sampleFreq) = Sounds.getHardware()
        if self.CN_Mode == 0:
            cnmode = 'silence'
            cnfreq = (self.PP_Freq, 0) # anything will do
        if self.CN_Mode == 1 or self.CN_Mode == 4 or self.CN_Mode == 5:
            cnmode = 'tone'
            cnfreq = (self.PP_Freq, 0)
        if self.CN_Mode == 2 or self.CN_Mode == 6:
            cnmode = 'bpnoise'
            cnfreq = (self.PP_HP, self.PP_LP)
        if self.CN_Mode == 3:
            cnmode = 'notchnoise' # Note: notch is embedded into a bandpass noise
            cnfreq = (self.PP_HP, self.PP_LP, self.PP_Notch_F1, self.PP_Notch_F2)
        # generate the conditioning stimulus and the post-prepulse stimulus
        self.wave_outL = Sounds.StimulusMaker(mode = cnmode, duration = (self.Stim_Dur+self.PP_Dur+self.PS_Dur+self.ST_Dur),
                                  freq = cnfreq, samplefreq = self.out_sampleFreq, delay=0, level = self.CN_Level)
        # now tailor the conditioning stimulus
        # this is regulated by the current Gap_List value
        w_pp = [] # default with no prepulse
        if self.Gap_List[self.TrialCounter]: # only make a prepulse if it is set
            if self.PP_Mode == 0 or self.PP_GapFlag: # insert a gap
                self.wave_outL = Sounds.insertGap(self.wave_outL, delay = self.Stim_Dur,
                                  duration = self.PP_Dur, samplefreq = self.out_sampleFreq) # inserts the gap
            if self.PP_Mode == 1 or self.PP_Mode ==4 or self.PP_Mode == 5: # now insert a tone
                w_pp = Sounds.StimulusMaker(mode = 'tone', duration = self.PP_Dur, freq = (self.PP_Freq, 0),
                                          delay=self.Stim_Dur, samplefreq = self.out_sampleFreq, level = self.PP_Level)
                w_pp = append(w_pp, numpy.zeros(len(self.wave_outL)-len(w_pp))) # pad
            if self.PP_Mode == 2 or self.PP_Mode == 6:  # 2 is bandpass noise
                w_pp = Sounds.StimulusMaker(mode = 'bpnoise', duration = self.PP_Dur, freq = (self.PP_HP, self.PP_LP),
                                    delay=self.Stim_Dur, samplefreq = self.out_sampleFreq, level = self.PP_Level)
                w_pp = append(w_pp, numpy.zeros(len(self.wave_outL)-len(w_pp))) # pad  
            if self.PP_Mode == 3: # 3 Notched noise
                w_pp = Sounds.StimulusMaker(mode = 'notchnoise', duration = self.Stim_Dur,
                                    freq = (self.PP_HP, self.PP_LP, self.Notch_F1, self.Notch_F2),
                                    samplefreq = self.out_sampleFreq, delay=self.Stim_Dur,
                                    level = self.PP_Level)
                w_pp = append(w_pp, numpy.zeros(len(self.wave_outL)-len(w_pp))) # pad 
        if len(w_pp) > 0:
            self.wave_outL = self.wave_outL + w_pp
        # generate the startle sound. Note that it overlaps the end of the conditioning sound...
        self.wave_outR = Sounds.StimulusMaker(mode = 'bpnoise', delay = (self.Stim_Dur+self.PP_Dur+self.PS_Dur),
                                       duration = self.ST_Dur, samplefreq=self.out_sampleFreq,
                                       freq = (1000.0, 32000.0), level = self.ST_Level,
                                       channel = 1)
        lenL = len(self.wave_outL)
        lenR = len(self.wave_outR)
        if lenR > lenL:
            self.wave_outL = append(self.wave_outL, numpy.zeros(lenR-lenL))
        if lenL > lenR:
            self.wave_outR = append(self.wave_outR, numpy.zeros(lenL-lenR))
        if self.debugFlag:
            print "runOnePP: present stimulus"
        if self.StimEnable == True:
            Sounds.playSound(self.wave_outL, self.wave_outR, self.out_sampleFreq,
                             self.PostDuration)
            (self.ch1, self.ch2) = Sounds.retrieveInputs()
        if self.debugFlag:
            print "runOnePP: exiting"


    
################################################################################
#
# plot the signal and it's power spectrum
#     
    def plotSignal(self, wL, wR, samplefreq=44100, plotResponse = True):
        if self.debugFlag:
            print "plotSignal: entering"
        npts = len(wL)
        t = arange(0,npts)/samplefreq
        skip = int(npts/self.maxptsplot)
        if skip < 1:
            skip = 1

        if self.debugFlag:
            print "plotSignal: stimplot"
        MPlots.PlotReset(self.ui.qwt_Stimulus_Plot)
        MPlots.PlotLine(self.ui.qwt_Stimulus_Plot, t[0::skip], wL[0::skip], color = 'y')
        MPlots.PlotLine(self.ui.qwt_Stimulus_Plot, t[0::skip], wR[0::skip], color = 'c')
        # spectrum of signal
        if self.debugFlag:
            print "plotSignal: specplot start"
        if self.ShowSpectrum:
            if self.debugFlag:
                print "signal pts: %d min: %f max: %f" % (len(wL), min(wL), max(wL))
            (spectrum, freqAzero) = Utils.pSpectrum(wL, samplefreq)
            MPlots.PlotReset(self.ui.qwt_Spectrum_Plot)
            MPlots.PlotLine(self.ui.qwt_Spectrum_Plot, freqAzero/1000.0, 1000.0*spectrum, color = 'r')
        if self.debugFlag:
            print "plotSignal: specplot done"
        if not plotResponse:
            return
        ds = shape(self.ch1)
        self.response_tb=arange(0,len(self.ch1))/self.in_sampleFreq
        MPlots.PlotReset(self.ui.qwt_Response_Plot1)
        MPlots.PlotLine(self.ui.qwt_Response_Plot1, self.response_tb[0::skip],
            1000.0*self.ch1[0::skip], color = 'g')
        MPlots.PlotReset(self.ui.qwt_Response_Plot2)
        MPlots.PlotLine(self.ui.qwt_Response_Plot2, self.response_tb[0::skip], 1000.0*self.ch2[0::skip], color = 'r')
        if self.ShowSpectrum:
            (Lspectrum, Lfreqs) = Utils.pSpectrum(1000.0*self.ch2, samplefreq) # rate  (1/ms) is converted to Hz
            maxFreq = 0.5*samplefreq
            MPlots.PlotReset(self.ui.qwt_LSpectrum_Plot)
            self.ui.qwt_LSpectrum_Plot.setAxisScale(Qwt.QwtPlot.xBottom, 10.0, maxFreq)
            self.ui.qwt_LSpectrum_Plot.setAxisScaleEngine(Qwt.QwtPlot.xBottom, Qwt.QwtLog10ScaleEngine())
            MPlots.PlotLine(self.ui.qwt_LSpectrum_Plot, Lfreqs[1:], 1000.0*Lspectrum[1:], color = 'y')

        if self.debugFlag:
            print "plotSignal: ResponsePlots done"
        tdelay = self.Stim_Dur + self.PP_Dur + self.PS_Dur
        # analyze the response signal
        dprime = self.Response_Analysis(timebase= self.response_tb, signal = self.ch1,
                               samplefreq = samplefreq, delay=tdelay,
                               SpecPlot = self.ui.qwt_RSpectrum_Plot,
                               SignalPlot = self.ui.qwt_Expanded_Signal_Plot,
                               ResponsePlot = self.ui.qwt_Discrimination_Plot,
                               ntrials = self.Trials,
                               trialcounter = self.TrialCounter,
                               gaplist = self.Gap_List)
        if self.debugFlag:
            print "plotSignal: ResponseAnalysis Done"

        if self.TrialCounter > 0:
            self.ui.qwt_Discrimination_Score.setText("Rd: %7.3f" % (dprime))
            self.ui.Rd_Dial.setValue(int(100*dprime))
        
    def getSelectionIndices(self, x, xstart, xend):
        astart = where(x >= xstart)
        aend = where (x <= xend)
        s0 = Set(transpose(astart).flat)
        s1 = Set(transpose(aend).flat)
        xpts = list(s1.intersection(s0))
        return (xpts)
    

    
    def Write_Data(self):
        self.writeDataFileHeader('test.dat')
        
    def writeDataFileHeader(self, filename):
        # make a dictionary of all the parameters
        filedict = {}
        filedict_gap = {}
        filedict_iti = {}
        filedict['CN_Level'] =  self.CN_Level
        filedict['CN_Dur'] = self.CN_Dur
        filedict['CN_Var'] = self.CN_Var
        filedict['PP_Level'] = self.PP_Level
        filedict['PP_OffLevel'] = self.PP_OffLevel 
        filedict['PP_Dur'] = self.PP_Dur
        filedict['PS_Dur'] = self.PS_Dur 
        filedict['ST_Dur'] = self.ST_Dur 
        filedict['ST_Level'] = self.ST_Level
        filedict['StimEnable'] = self.StimEnable
        filedict['WavePlot'] = self.WavePlot
        
        filedict_gap['GapList'] = self.Gap_List # save the sequencing information

# from the Waveforms tab:        
        filedict['PP_Freq'] = self.PP_Freq 
        filedict['PP_HP'] = self.PP_HP
        filedict['PP_LP'] = self.PP_LP
        filedict['PP_Mode'] = self.PP_Mode
        filedict['CN_Mode'] = self.CN_Mode
        filedict['PP_Notch_F1'] = self.PP_Notch_F1
        filedict['PP_Notch_F2'] = self.PP_Notch_F2
        filedict['PP_MultiFreq'] = self.PP_MultiFreq
        filedict['PP_GapFlag'] = self.PP_GapFlag
# from the Timing and Trials tab:
        filedict['ITI_Var'] = self.ITI_Var 
        filedict['ITI'] = self.ITI
        filedict['Trials'] = self.Trials
        filedict['NHabTrials'] = self.NHabTrials
# analysis parameters        
        filedict['Analysis_Start'] = self.Analysis_Start 
        filedict['Analysis_End'] = self.Analysis_End
        filedict['Analysis_HPF'] = self.Analysis_HPF
        filedict['Analysis_LPF'] = self.Analysis_LPF          
        print "Writing File: %s" % (filename)
        hdat = open(filename, 'w')
        hdat.write("%s \n" % (filedict))
        hdat.write("%s \n" % (filedict_gap)) # write in separate lines
        hdat.close()
        
    def AppendData(self, filename):
        hdat = open(filename, 'a')
        datainfo = {}
        datainfo['Points'] = len(self.response_tb)
        datainfo['inSampleFreq'] = self.in_sampleFreq
        datainfo['outSampleFreq'] = self.out_sampleFreq
        datainfo['GapMode'] = self.Gap_List[self.TrialCounter]
        datainfo['ITI'] = self.ITI_List[self.TrialCounter]
        datainfo['CNDur'] = self.Dur_List[self.TrialCounter]
        hdat.write("%s \n" % (datainfo))
        for i in range(0, len(self.response_tb)):
            hdat.write("%f %f %f\n" % (self.response_tb[i], 1000*self.ch1[i],
                                       1000*self.ch2[i]))
        hdat.close()



    def Analysis_Test(self):
        self.readParameters()
        self.readAnalysisTab()
        npts = 10000
        samplefreq = 24410.0
        rate = 1000.0/samplefreq
        signal = normal(0, 1, npts)
        (Rspectrum, Rfreqs) = Utils.pSpectrum(signal, float(rate/1000.0)) # rate  (1/ms) is converted to Hz
        maxFreq = 5000.0
        fa = Utils.SignalFilter(signal, self.Analysis_LPF, self.Analysis_HPF, samplefreq)
        (fRspectrum, fRfreqs) = Utils.pSpectrum(fa, float(rate/1000.0)) # rate  (1/ms) is converted to Hz
        figure(3)
        plt.clf()
        plt.plot(Rfreqs, Rspectrum, 'k-')
        hold(True)
        plt.plot(fRfreqs, fRspectrum, 'r-')
        plt.show()
        
################################################################################
#   Analysis routines
#
################################################################################
    def Analysis_Read(self, filename = None):
        self.readParameters() # to be sure we have "showspectrum"
        self.readAnalysisTab()
        if filename == None:
            fd = QtGui.QFileDialog(self)
            self.inFileName = str(fd.getOpenFileName(self, "Get input file", "",
                                                     "data files (*.txt)"))
        else:
            self.inFileName = filename
        try:
            hstat = open(self.inFileName,"r")
            (p, f)  = os.path.split(self.inFileName)
            self.setMainWindow(f)
        except IOError:
            self.Status( "%s not found" % (self.inFileName))
            return
        lineno = 0
        state = 0 # initial state
                    # states:
                    # 0 - nothing read
                    # 1 = first line read
                    # 2 = record "header" line read
                    # 3 = reading data
        parse =  re.compile("(^([\-0-9.]*) ([\-0-9.]*) ([\-0-9.]*))")
        reccount = 1
        self.a_t = [] 
        self.a_ch1 = [] 
        self.a_ch2 = [] 
        self.gapmode = []
        self.delaylist = []
        header_linecount = 0
        lineno = 0
        for line in hstat:
            lineno = lineno + 1
            if state == 0:
                if header_linecount == 0:
                    self.statusBar().showMessage("Reading Header" )   
                    self.paramdict = Utils.long_Eval(line)
                    header_linecount = 1
                    continue
                if header_linecount == 1:
                    self.paramdict_gap = Utils.long_Eval(line)
                    state = 1
                    header_linecount = 0
                    print ".",
                continue
            if state == 1:
                self.headerdict =  Utils.long_Eval(line)
                self.npts = self.headerdict['Points']
                self.samplefreq = 1.0/float(self.headerdict['SampleRate'])
                if self.headerdict.has_key('GapMode'): # build gap mode array
                    self.gapmode.append(self.headerdict['GapMode'])
                else:
                    self.gapmode.append(False)
                if self.headerdict.has_key('CNDur'): # build duration  (delay to startle) array
                    self.delaylist.append(self.headerdict['CNDur'] +
                                          self.paramdict['PP_Dur'] + self.paramdict['PS_Dur'])                    
                reccount += 1
                self.statusBar().showMessage("Reading Trial %d" % (reccount) )   
                state = 2
                i = 0
                t = numpy.zeros(self.npts)
                ch1 = numpy.zeros(self.npts)
                ch2 = numpy.zeros(self.npts)
                print ".",
                continue
            if state == 2:
                    mo = parse.search(line)
                    t[i] = float(mo.group(2))
                    ch1[i] = float(mo.group(3))
                    ch2[i] = float(mo.group(4))
                    i = i + 1
                    if i >= self.npts:
                        self.a_t.append(numpy.array(t))
                        # filter the data as it comes in
                        self.a_ch1.append(numpy.array(ch1))
                        self.a_ch2.append(numpy.array(ch2))
                        state = 1 # reset the state to read the next points list
        hstat.close()
        self.statusBar().showMessage("Done Reading")   
        self.Analyze_Data()
        
    def Analyze_Data(self):
        self.readParameters() # to be sure we have "showspectrum"
        self.readAnalysisTab()
        ds = len(self.a_t)
        dshape = shape(self.a_t[1])
        srate = 1000.0/float(self.headerdict['SampleRate']) # sample rate is in msec/point.
        sfreq = 1/srate
        # note: must clip to the part of the dataset that we need - e.g., the post-startle section        
        stdur = int(self.Analysis_End/srate) #  points after startle
        plt.figure(2)
        plt.clf()
        rows = int(numpy.sqrt(ds))
        cols = int(ds/rows)
        if rows*cols < ds:
            cols += 1
        # first filter the data set (only the response channel, not the microphonse)
        self.fa_ch1 = []
        for k in range(0, ds):
            fa = Utils.SignalFilter(self.a_ch1[k], self.Analysis_LPF, self.Analysis_HPF, float(self.headerdict['SampleRate']))
            print "fa: %f-%f" % (min(fa), max(fa))
            self.fa_ch1.append((fa).astype('float32'))
  
        k = 0
        for i in range(0, rows):
            for j in range(0, cols):
                if k >= ds:
                    continue
                if self.gapmode[k]:
                    pline = 'r-' # with prepulse, red
                else:
                    pline = 'b-'
                if k < self.paramdict['NHabTrials']: # force green
                    pline = 'g-'
                plt.subplot(rows, cols, k+1)
                ststart = int(self.delaylist[k]/srate) # delay is in msec
                stend = ststart+stdur
                if self.gapmode[k]:       
                    try:
                        plt.plot(self.a_t[k][ststart:stend]-self.a_t[k][ststart],
                        self.fa_ch1[k][ststart:stend], pline, label="PP")
                    except:
                        pass
                else:
#                    print "ststart: %d  stend: %d len (a_t): %d" % (ststart, stend, len(self.a_t[k]))
                    try:
                        plt.plot(self.a_t[k][ststart:stend]-self.a_t[k][ststart],
                        self.fa_ch1[k][ststart:stend], pline, label="NoPP")
                    except:
                        pass
                k = k + 1 
        plt.figure(3)
        plt.clf()
        RAaxes = plt.subplot(2,2,3)
        SAaxes = plt.subplot(2,2,2)
        SPaxes = plt.subplot(2,2,4)
        sum_nogap = numpy.zeros(stdur)
        sum_gap = numpy.zeros(stdur)
        N_gap = 0
        N_nogap = 0
        tb = numpy.arange(0,(stdur/srate),1.0/srate)
        NTrials = int(self.paramdict['Trials'])
        self.Startle_Analyze(ntrials = NTrials) # forces init of variables...
        self.SpecMax = 0
        for i in range(0, ds):
            thislen = shape(self.a_t[i])
            if thislen[0] <= 0:
                break
            ststart = int(self.delaylist[i]/srate) # delay is in msec
            stend = ststart+stdur
            if i < int(self.paramdict['NHabTrials']):
                continue
            self.Response_Analysis(signal=self.fa_ch1[i][ststart:stend],
                                  samplefreq=sfreq,
                                  ResponsePlot=self.ui.qwt_Discrimination_Plot,
                                  SignalPlot = self.ui.qwt_Expanded_Signal_Plot,
                                  SpecPlot = self.ui.qwt_RSpectrum_Plot,
                                  trialcounter=i,
                                  ntrials=NTrials,
                                  gaplist = self.gapmode)
            if self.gapmode[i]:
                try:
                    sum_gap = sum_gap + numpy.array(self.fa_ch1[i][ststart:stend])
                    N_gap += 1
                except:
                    pass
            else:
                try:
                    sum_nogap = sum_nogap + numpy.array(self.fa_ch1[i][ststart:stend])
                    N_nogap += 1
                except:
                    pass
        sum_gap = sum_gap/float(N_gap)
        sum_nogap = sum_nogap/float(N_nogap)
#        print "stdur (int): %d" % (stdur)
#        print "N_Gap: %d  N_Nogap: %d " % (N_gap, N_nogap)
#        print max(sum_gap)
#        print max(sum_nogap)
#        plt.subplot(2,2,1)
#        plt.plot(tb, sum_nogap, 'b-', label="No PrePulse")
#        plt.title('Average Startle')
#        plt.hold(True)
#        plt.plot(tb, sum_gap, 'r-', label="PrePulse")
#        plt.legend()
        plt.draw()
        plt.show()
        

# response analysis for a single trace...

    def Response_Analysis(self, timebase=None, signal=None,
                               samplefreq=44100, delay=0, SpecPlot=None,
                               SignalPlot=None, ResponsePlot=None,
                               trialcounter=0,
                               ntrials=1,
                               gaplist=None):
        if self.debugFlag:
            print "Response_Analysis: entering"
        if gaplist[trialcounter]:
            pline = 'r' # with prepulse, red
        else:
            pline = 'g'
        if self.ShowSpectrum and SpecPlot != None:
            if self.debugFlag:
                print "signal pts: %d min: %f max: %f" % (len(signal), min(signal), max(signal))
            (Rspectrum, Rfreqs) = Utils.pSpectrum(1000.0*signal, samplefreq) # rate  (1/ms) is converted to Hz
            if max(Rspectrum) > self.SpecMax:
                self.SpecMax = max(Rspectrum)
            maxFreq = 1000.0
            MPlots.PlotReset(SpecPlot)
            SpecPlot.setAxisScale(Qwt.QwtPlot.xBottom, 10.0, maxFreq)
            SpecPlot.setAxisScaleEngine(Qwt.QwtPlot.xBottom, Qwt.QwtLog10ScaleEngine())
            MPlots.PlotLine(SpecPlot, Rfreqs[1:], 1000.0*Rspectrum[1:], color = 'y')

        if self.debugFlag:
            print "Response_Analysis: 2"
        if timebase == None:
            timebase = arange(0, len(signal))/samplefreq
        ana_windowstart = (delay + self.Analysis_Start)/1000.0
        ana_windowend = (delay + self.Analysis_End)/1000.0
        if self.debugFlag:
            print "len(timebase): %d max=%f window: %f - %f" % (len(timebase), max(timebase), ana_windowstart, ana_windowend)
        apts = self.getSelectionIndices(timebase, ana_windowstart, ana_windowend)
#        fsignal = self.SignalFilter(signal, self.Analysis_LPF, self.Analysis_HPF, samplefreq)
#        signal = array(self.ch2) # just for monitoring timing
        if self.debugFlag:
            print "apts: %d-%d, l(tb): %d  l(signal): %d" % (min(apts), max(apts), len(timebase), len(signal))
        t0 = timebase[apts[0]] - self.Analysis_Start/1000.0
        if self.debugFlag:
            print "Response_Analysis: 3"
        if SignalPlot != None:
            MPlots.PlotReset(SignalPlot)
            MPlots.PlotLine(SignalPlot, timebase[apts]-t0, 1000.0*signal[apts], color = pline)
        if self.debugFlag:
            print "Response_Analysis: 4"
        dprime = self.Startle_Analyze(timebase=timebase, signal=signal,
                                      startdelay=0.0,
                                      trialcounter=trialcounter,
                                      ntrials=ntrials,
                                      gaplist=gaplist)
        if self.debugFlag:
            print "Response_Analysis: 5"
        if ResponsePlot != None:
            MPlots.PlotReset(ResponsePlot)
            MPlots.PlotLine(ResponsePlot, range(len(self.Gap_StartleMagnitude)),
                                            self.Gap_StartleMagnitude, color =
                                            'r', symbol = 'o')
            MPlots.PlotLine(ResponsePlot, range(len(self.noGap_StartleMagnitude)),
                                            self.noGap_StartleMagnitude, color = 'y',
                                            symbol = '+')
        if self.debugFlag:
            print "Response_Analysis: exiting"
        return(dprime)
        
    def Startle_Analyze(self, timebase=None,
                        signal=None,
                        startdelay=0,
                        trialcounter=0,
                        ntrials=1,
                        gaplist=None):
        self.readAnalysisTab()
        dprime = 0.0
        if trialcounter == 0: # initialize the trials.
            self.Gap_mean = 0.0
            self.Gap_std = 0.0
            self.noGap_mean = 0.0
            self.noGap_std = 0.0
            self.Gap_StartleMagnitude = zeros(ntrials)
            self.Gap_Counter = 0
            self.noGap_StartleMagnitude = zeros(ntrials)
            self.noGap_Counter = 0
            self.SpecMax = 0.0

            return(dprime)
        apts = self.getSelectionIndices(timebase, startdelay/1000.0,
                                        (startdelay+self.Analysis_End)/1000.0)
        if trialcounter > 0 : # once we are past the habituation phase
            if gaplist[trialcounter]:
                try:
                    self.Gap_StartleMagnitude[self.Gap_Counter] = sum(signal[apts]**2)
                    self.Gap_mean = mean(self.Gap_StartleMagnitude[0:self.Gap_Counter])
                    if self.Gap_Counter >= 1 :
                        self.Gap_std = std(self.Gap_StartleMagnitude[0:self.Gap_Counter])
                    self.Gap_Counter = self.Gap_Counter + 1
                except:
                    pass
            else:
                try:
                    self.noGap_StartleMagnitude[self.noGap_Counter] = sum(signal[apts]**2)
                    self.noGap_mean = mean(self.noGap_StartleMagnitude[0:self.noGap_Counter])
                    if self.noGap_Counter >= 1 :
                        self.noGap_std = std(self.noGap_StartleMagnitude[0:self.noGap_Counter])
                    self.noGap_Counter = self.noGap_Counter + 1
                except:
                    pass
# now calculate the d'
            if self.debugFlag:
                print "gap: %f +/- %f,, nogap: %f +/- %f" % (self.Gap_mean, self.Gap_std, self.noGap_mean, self.noGap_std)
            if self.noGap_std != 0 and self.Gap_std != 0 :
                dprime = (self.noGap_mean-self.Gap_mean)/(sqrt(self.noGap_std**2 + self.Gap_std**2))
        return(dprime)
    
################################################################################
#
# Read the initialization file. This is a simple text file with defined fields
# and numeric arguments. 
# The elements of the file can be in any order.
# unrecognized tags are ignored at your peril.
################################################################################

    def readini(self, filename= None):    
        if filename == None:
            fd = QtGui.QFileDialog(self)
            self.fileName = str(fd.getOpenFileName(self, "Get Parameter File", "",
                                                   "Parameter Files (*.ini)"))
        else:
            self.fileName = filename
        rxCurrentTab = re.compile("^\[CurrTab\] ([0-9]*)")
        rxDate = re.compile("^\[Date\] ([0-9a-zA-Z\-\/.,:]*)")
        rxDescription = re.compile("^\[Description\] ([0-9a-zA-Z.,]*)")
        rxStimEnable = re.compile("(^\[StimEnable\]) ([TrueFalse]*)")
        rxWavePlot = re.compile("(^\[WavePlot\]) ([TrueFalse]*)")
        rxSpecPlot = re.compile("(^\[SpecPlot\]) ([TrueFalse]*)")
        rxCN_Level = re.compile("(^\[CN_Level\]) ([0-9.]*)")
        rxCN_Dur = re.compile("(^\[CN_Dur\]) ([0-9.]*)")
        rxCN_Var = re.compile("(^\[CN_Var\]) ([0-9.]*)")
        rxCN_Mode = re.compile("(^\[CN_Mode\]) ([0-9]*)")
        rxPP_Level = re.compile("(^\[PP_Level\]) ([0-9.]*)")
        rxPP_OffLevel= re.compile("(^\[PP_OffLevel\]) ([0-9.]*)")
        rxPP_Dur = re.compile("(^\[PP_Dur\]) ([0-9.]*)")
        rxPP_Freq = re.compile("(^\[PP_Freq\]) ([0-9.]*)")
        rxPP_HP = re.compile("(^\[PP_HP\]) ([0-9.]*)")
        rxPP_LP = re.compile("(^\[PP_LP\]) ([0-9.]*)")
        rxPP_Mode = re.compile("(^\[PP_Mode\]) ([0-9]*)")
        rxPP_NotchF1 = re.compile("(^\[PP_Notch_F1\]) ([0-9.]*)")
        rxPP_NotchF2 = re.compile("(^\[PP_Notch_F2\]) ([0-9.]*)")
        rxPP_GapFlag = re.compile("(^\[PP_GapFlag\]) ([TrueFalse]*)")
        rxPP_MultiF = re.compile("(^\[PP_MultiFreq\]) ([0-9a-zA-Z.,]*)")
        rxST_Dur = re.compile("(^\[ST_Dur\]) ([0-9.]*)")
        rxST_Level = re.compile("(^\[ST_Level\]) ([0-9.]*)")
        rxITI = re.compile("(^\[ITI_mean\]) ([0-9.]*)")
        rxITI_Var = re.compile("(^\[ITI_Var\]) ([0-9.]*)")
        rxTrials = re.compile("(^\[Trials\]) ([0-9.]*)")
        rxNHabTrials = re.compile("(^\[NHabTrials\]) ([0-9.]*)")
        try:
            hstat = open(self.fileName,"r")
            (p, f)  = os.path.split(self.fileName)
            self.setMainWindow(f)
        except IOError:
            self.Status( "%s not found" % (self.fileName))
            return
        try:
            for line in hstat:
                if rxCurrentTab.match(line): # only at start of line
                    mo = rxCurrentTab.search(line)
                    self.CurrentTab = int(mo.group(1))
                    self.setCurrentTab(self.CurrentTab)
                    continue
                if rxDate.match(line): # only at start of line
                    mo = rxDate.search(line)
                    self.fileDate = str(mo.group(1))
                    print ('Date: self.fileDate: %s ' % self.fileDate)
                    self.setMainWindow(f + " " + self.fileDate)
                    continue
                if rxStimEnable.match(line): # at start of line
                    mo = rxStimEnable.search(line) # search whole line to get decimal number
                    self.StimEnable = (mo.group(2) == "True")
                    self.ui.Stimulus_Enable.setChecked(self.StimEnable)
                    continue
                if rxWavePlot.match(line): # at start of line
                    mo = rxWavePlot.search(line) # search whole line to get decimal number
                    self.WavePlot = (mo.group(2) == "True")
                    self.ui.Waveform_PlotFlag.setChecked(self.WavePlot)
                    continue
                if rxSpecPlot.match(line): # at start of line
                    mo = rxSpecPlot.search(line) # search whole line to get decimal number
                    self.ShowSpectrum = (mo.group(2) == "True")
                    self.ui.OnlineSpectrum_Flag.setChecked(self.ShowSpectrum)
                    continue
                if rxCN_Level.match(line):
                    mo = rxCN_Level.search(line)
                    self.ui.Condition_Level.setValue(float(mo.group(2)))
                    continue
                if rxCN_Dur.match(line):
                    mo = rxCN_Dur.search(line)
                    self.ui.Condition_Dur.setValue(float(mo.group(2)))
                    continue
                if rxCN_Var.match(line):
                    mo = rxCN_Var.search(line)
                    self.ui.Condition_Var.setValue(float(mo.group(2)))
                    continue
                if rxCN_Mode.match(line):
                    mo = rxCN_Mode.search(line)
                    self.ui.Waveform_Conditioning.setCurrentIndex(int(mo.group(2)))
                    continue
                if rxPP_Level.match(line):
                    mo = rxPP_Level.search(line)
                    self.ui.PrePulse_Level.setValue(float(mo.group(2)))
                    continue
                if rxPP_Dur.match(line): # at start of line
                    mo = rxPP_Dur.search(line) 
                    self.ui.PrePulse_Dur.setValue(float(mo.group(2)))
                    continue
                if rxPP_Freq.match(line): # at start of line
                    mo = rxPP_Freq.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_Freq.setValue(float(mo.group(2)))
                    continue
                if rxPP_HP.match(line): # at start of line
                    mo = rxPP_HP.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_HP.setValue(float(mo.group(2)))
                    continue
                if rxPP_LP.match(line): # at start of line
                    mo = rxPP_LP.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_LP.setValue(float(mo.group(2)))
                    continue
                if rxPP_NotchF1.match(line): # at start of line
                    mo = rxPP_NotchF1.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_Notch_F1.setValue(float(mo.group(2)))
                    continue
                if rxPP_NotchF2.match(line): # at start of line
                    mo = rxPP_NotchF2.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_Notch_F2.setValue(float(mo.group(2)))
                    continue
                if rxPP_GapFlag.match(line): # at start of line
                    self.PP_GapFlag = (mo.group(2) == "True")
                    self.ui.PrePulse_GapFlag.setChecked(self.PP_GapFlag)
                    continue
                if rxPP_Mode.match(line): # at start of line
                    mo = rxPP_Mode.search(line) # search whole line to get decimal number
                    self.ui.Waveform_PrePulse.setCurrentIndex(int(mo.group(2)))
                    continue
                if rxST_Dur.match(line): # at start of line
                    mo = rxST_Dur.search(line) # search whole line to get decimal number
                    self.ui.Startle_Dur.setValue(float(mo.group(2)))
                    continue
                if rxST_Level.match(line): # at start of line
                    mo = rxST_Level.search(line) # search whole line to get decimal number
                    self.ui.Startle_Level.setValue(float(mo.group(2)))
                    continue
                if rxITI.match(line): # at start of line
                    mo = rxITI.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_ITI.setValue(float(mo.group(2)))
                    continue
                if rxITI_Var.match(line): # at start of line
                    mo = rxITI_Var.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_ITI_Var.setValue(float(mo.group(2)))
                    continue
                if rxTrials.match(line): # at start of line
                    mo = rxTrials.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_Trials.setValue(float(mo.group(2)))
                    continue
                if rxNHabTrials.match(line): # at start of line
                    mo = rxNHabTrials.search(line) # search whole line to get decimal number
                    self.ui.PrePulse_NHabTrials.setValue(float(mo.group(2)))
                    continue
        finally:
            hstat.close()

# write a file that can be read by readini above. 
            
    def writeini(self, filename=None):
                # now save the program status... ;) to reload later
        if filename == None:
            fd = QtGui.QFileDialog(self)
            self.fileName = str(fd.getSaveFileName())
        else:
            self.fileName = filename  
        hstat = open(self.fileName, "w")
        self.readParameters() # get the latest from the gui
# should also save the date in the file.
        hstat.write("[Description] %s\n[Date] %s\n[CurrTab] %d\n" %
                    (self.Description, strftime("%d-%b-%Y"), self.getCurrentTab()))
        hstat.write("[StimEnable] %s\n[WavePlot] %s\n[SpecPlot] %s\n" %
                    (self.StimEnable, self.WavePlot, self.ShowSpectrum))
        hstat.write("[CN_Level] %f\n[CN_Dur] %f\n[CN_Var] %f\n[CN_Mode] %d\n"
                    % (self.CN_Level, self.CN_Dur, self.CN_Var, self.CN_Mode))
        hstat.write("[PP_Level] %f \n[PP_OffLevel] %f \n[PP_Dur] %f\n[PP_Mode] %d\n" %  \
            (self.PP_Level, self.PP_OffLevel, self.PP_Dur, self.PP_Mode))
        hstat.write("[PP_Freq] %f \n[PP_HP] %f\n[PP_LP] %f\n" \
            %  (self.PP_Freq, self.PP_HP, self.PP_LP))
        hstat.write("[ST_Dur] %f \n[ST_Level] %f\n" \
            %  (self.ST_Dur, self.ST_Level))
        hstat.write("[ITI_mean] %f \n[ITI_Var] %f \n[Trials] %f \n[NHabTrials] %f\n" \
            %  (self.ITI, self.ITI_Var, self.Trials, self.NHabTrials))
        hstat.write("[PP_NotchF1] %f \n[PP_NotchF2] %f \n[PP_GapFlag] %s \n[PP_MultiFreq] %s\n" \
                    % (self.PP_Notch_F1, self.PP_Notch_F2, self.PP_GapFlag, self.PP_MultiFreq))
        hstat.close()

################################################################################
#
# main entry
#

if __name__ == "__main__":
# check the hardware first
    app = QtGui.QApplication(sys.argv)
    MainWindow = PyStartle()
    MainWindow.show()
    sys.exit(app.exec_())
    