source: trunk/Puzzlebox/Synapse/Interface.py @ 296

Last change on this file since 296 was 296, checked in by sc, 10 years ago

Interface:

  • "Cancel" button handling for Save and Export fixed
  • Property svn:executable set to *
File size: 34.9 KB
Line 
1# -*- coding: utf-8 -*-
2
3# Copyright Puzzlebox Productions, LLC (2010-2011)
4#
5# This code is released under the GNU Pulic License (GPL) version 2
6# For more information please refer to http://www.gnu.org/copyleft/gpl.html
7
8# Old Class Names:
9#       puzzlebox_synapse_interface = QtUI
10
11__changelog__ = """\
12Last Update: 2011.09.19
13"""
14
15__todo__ = """
16- update configuration.ini file with settings entered into interface
17
18"""
19
20### IMPORTS ###
21import os, sys, time
22import simplejson as json
23
24try:
25        from Interface_Plot import *
26        MATPLOTLIB_AVAILABLE = True
27except:
28        MATPLOTLIB_AVAILABLE = False
29
30MATPLOTLIB_AVAILABLE = False
31
32if (sys.platform != 'win32'):
33        import bluetooth
34        DEFAULT_IMAGE_PATH = '/usr/share/puzzlebox_synapse/images'
35else:
36        import _winreg as winreg
37        import itertools
38        import re
39        import serial
40        DEFAULT_IMAGE_PATH = 'images'
41
42try:
43        import PySide as PyQt4
44        from PySide import QtCore, QtGui
45except:
46        print "Using PyQt4 module"
47        from PyQt4 import QtCore, QtGui
48else:
49        print "Using PySide module"
50
51try:
52        import cPickle as pickle
53except:
54        import pickle
55
56# from puzzlebox_synapse_interface_design import Ui_Form
57from Interface_Design import Ui_Form as Design
58
59import Configuration as configuration
60import Server as synapse_server
61import Client as thinkgear_client
62#import puzzlebox_logger
63
64### GLOBALS ###
65
66DEBUG = 1
67
68THINKGEAR_SERVER_HOST = configuration.THINKGEAR_SERVER_HOST
69THINKGEAR_SERVER_PORT = configuration.THINKGEAR_SERVER_PORT
70
71THINKGEAR_EEG_POWER_BAND_ORDER = configuration.THINKGEAR_EEG_POWER_BAND_ORDER
72
73THINKGEAR_EMULATION_MAX_ESENSE_VALUE = \
74        configuration.THINKGEAR_EMULATION_MAX_ESENSE_VALUE
75THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE = \
76        configuration.THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
77
78PATH_TO_HCITOOL = '/usr/bin/hcitool'
79
80#UPDATE_INTERFACE_VIA_TIMER = True # Alternative is to establish a
81                                  ## ThinkGear Connect client which
82                                  ## updates the interface on demand
83                                  ## as packets are received
84
85UPDATE_INTERFACE_VIA_TIMER = False
86
87#INTERFACE_UPDATE_FREQUENCY = (1 / 512) * 1000 # ms (512 Hz)
88INTERFACE_UPDATE_FREQUENCY = 1000 # ms
89
90INTERFACE_RAW_EEG_UPDATE_FREQUENCY = 512
91
92PACKET_MINIMUM_TIME_DIFFERENCE_THRESHOLD = 0.75
93
94### CLASSES ###
95
96class QtUI(QtGui.QWidget, Design):
97       
98        def __init__(self, log, server=None, DEBUG=DEBUG, parent = None):
99               
100                self.log = log
101                self.DEBUG = DEBUG
102                self.parent=parent
103               
104                if self.parent == None:
105                        QtGui.QWidget.__init__(self, parent)
106                        self.setupUi(self)
107               
108                        self.configureSettings()
109                        self.connectWidgets()
110               
111                self.name = "Synapse Interface"
112               
113                self.thinkGearConnectServer = None
114                self.thinkgearConnectClient = None
115               
116                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
117               
118                self.debug_console_buffer = ''
119               
120                self.packets = {}
121                self.packets['rawEeg'] = []
122                self.packets['signals'] = []
123               
124                self.customDataHeaders = []
125               
126                if UPDATE_INTERFACE_VIA_TIMER:
127                        self.updateInterfaceTimer = QtCore.QTimer()
128                        QtCore.QObject.connect(self.updateInterfaceTimer, \
129                                                    QtCore.SIGNAL("timeout()"), \
130                                                    self.updateInterface)
131               
132                if (sys.platform == 'win32'):
133                        self.homepath = os.path.join( \
134                           os.environ['HOMEDRIVE'], \
135                           os.environ['HOMEPATH'], \
136                           'Desktop')
137                else:
138                        self.homepath = os.environ['HOME']
139               
140                if not os.path.exists(self.homepath):
141                        self.homepath = os.getcwd()
142       
143       
144        ##################################################################
145       
146        def configureSettings(self):
147               
148                # Synapse Interface
149                image_path = "puzzlebox.ico"
150                if not os.path.exists(image_path):
151                        image_path = os.path.join(DEFAULT_IMAGE_PATH, image_path)
152               
153                if os.path.exists(image_path):
154                        icon = QtGui.QIcon()
155                        icon.addPixmap(QtGui.QPixmap(image_path), \
156                                            QtGui.QIcon.Normal, \
157                                            QtGui.QIcon.Off)
158                        self.setWindowIcon(icon)
159               
160                image_path = "puzzlebox_logo.png"
161                if not os.path.exists(image_path):
162                        image_path = os.path.join(DEFAULT_IMAGE_PATH, image_path)
163                if os.path.exists(image_path):
164                        self.labelPuzzleboxIcon.setPixmap(QtGui.QPixmap(image_path))
165               
166               
167                if configuration.INTERFACE_TAB_POSITION == 'South':
168                        self.tabWidget.setTabPosition(QtGui.QTabWidget.South)
169                else:
170                        self.tabWidget.setTabPosition(QtGui.QTabWidget.North)
171               
172               
173                # ThinkGear Device
174                self.updateThinkGearDevices()
175               
176               
177                # ThinkGear Connect Server
178                self.textLabelBluetoothStatus.setText("Status: Disconnected")
179               
180                # Display Host for ThinkGear Connect Socket Server
181                self.lineEditThinkGearHost.setText(THINKGEAR_SERVER_HOST)
182               
183                # Display Port for ThinkGear Connect Socket Server
184                self.lineEditThinkGearPort.setText('%i' % THINKGEAR_SERVER_PORT)
185               
186               
187                # ThinkgGear Progress Bars
188                self.progressBarEEGDelta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
189                self.progressBarEEGTheta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
190                self.progressBarEEGLowAlpha.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
191                self.progressBarEEGHighAlpha.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
192                self.progressBarEEGLowBeta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
193                self.progressBarEEGHighBeta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
194                self.progressBarEEGLowGamma.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
195                self.progressBarEEGMidGamma.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
196               
197                self.progressBarAttention.setMaximum(THINKGEAR_EMULATION_MAX_ESENSE_VALUE)
198                self.progressBarMeditation.setMaximum(THINKGEAR_EMULATION_MAX_ESENSE_VALUE)
199               
200                self.progressBarSignalContactQuality.setMaximum(200)
201               
202               
203                if MATPLOTLIB_AVAILABLE:
204                        self.rawEEGMatplot = rawEEGMatplotlibCanvas( \
205                                                self.tabEEGSignals, \
206                                                width=8, \
207                                                height=4, \
208                                                dpi=100, \
209                                                title='Raw EEG Waves')
210                        self.chartEEGMatplot = chartEEGMatplotlibCanvas( \
211                                                self.tabCharts, \
212                                                width=8, \
213                                                height=4, \
214                                                dpi=100, \
215                                                title='EEG Brain Signals')
216               
217                else:
218                        self.tabWidget.removeTab(self.tabWidget.indexOf(self.tabEEGSignals))
219                        self.tabWidget.removeTab(self.tabWidget.indexOf(self.tabCharts))
220       
221       
222        ##################################################################
223       
224        def connectWidgets(self):
225               
226                self.connect(self.pushButtonBluetoothSearch, \
227                                  QtCore.SIGNAL("clicked()"), \
228                                  self.updateThinkGearDevices)
229               
230                self.connect(self.pushButtonBluetoothConnect, \
231                                  QtCore.SIGNAL("clicked()"), \
232                                  self.connectToThinkGearDevice)
233               
234                self.connect(self.pushButtonThinkGearConnect, \
235                                  QtCore.SIGNAL("clicked()"), \
236                                  self.startThinkGearConnectServer)
237               
238                self.connect(self.pushButtonSave, \
239                                  QtCore.SIGNAL("clicked()"), \
240                                  self.saveData)
241               
242                self.connect(self.pushButtonExport, \
243                                  QtCore.SIGNAL("clicked()"), \
244                                  self.exportData)
245               
246                self.connect(self.pushButtonReset, \
247                                  QtCore.SIGNAL("clicked()"), \
248                                  self.resetData)
249       
250       
251        ##################################################################
252       
253        def connectToThinkGearDevice(self):
254               
255                device_selection = self.comboBoxDeviceSelect.currentText()
256               
257                self.disconnect(self.pushButtonBluetoothConnect, \
258                                     QtCore.SIGNAL("clicked()"), \
259                                     self.connectToThinkGearDevice)
260               
261                self.connect(self.pushButtonBluetoothConnect, \
262                                  QtCore.SIGNAL("clicked()"), \
263                                  self.disconnectFromThinkGearDevice)
264               
265                self.textLabelBluetoothStatus.setText("Status: Connected")
266               
267                self.pushButtonBluetoothSearch.setEnabled(False)
268               
269                self.pushButtonBluetoothConnect.setText('Disconnect')
270                self.pushButtonBluetoothConnect.setChecked(True)
271               
272                self.comboBoxDeviceSelect.setEnabled(False)
273       
274       
275        ##################################################################
276       
277        def disconnectFromThinkGearDevice(self):
278               
279                self.disconnect(self.pushButtonBluetoothConnect, \
280                                     QtCore.SIGNAL("clicked()"), \
281                                     self.disconnectFromThinkGearDevice)
282               
283                self.connect(self.pushButtonBluetoothConnect, \
284                                  QtCore.SIGNAL("clicked()"), \
285                                  self.connectToThinkGearDevice)
286               
287                self.textLabelBluetoothStatus.setText("Status: Disconnected")
288               
289                self.pushButtonBluetoothSearch.setEnabled(True)
290               
291                self.pushButtonBluetoothConnect.setText('Connect')
292                self.pushButtonBluetoothConnect.setChecked(False)
293               
294                self.comboBoxDeviceSelect.setEnabled(True)
295               
296               
297                self.progressBarEEGDelta.setValue(0)
298                self.progressBarEEGTheta.setValue(0)
299                self.progressBarEEGLowAlpha.setValue(0)
300                self.progressBarEEGHighAlpha.setValue(0)
301                self.progressBarEEGLowBeta.setValue(0)
302                self.progressBarEEGHighBeta.setValue(0)
303                self.progressBarEEGLowGamma.setValue(0)
304                self.progressBarEEGMidGamma.setValue(0)
305               
306                self.progressBarAttention.setValue(0)
307                self.progressBarMeditation.setValue(0)
308               
309                self.progressBarSignalContactQuality.setValue(0)
310               
311                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
312               
313                # In case the user connects to a MindSet, then disconnects
314                # and re-connects to a MindSet Emulator,
315                # we need to reset the max power values
316                self.progressBarEEGDelta.setMaximum(self.maxEEGPower)
317                self.progressBarEEGTheta.setMaximum(self.maxEEGPower)
318                self.progressBarEEGLowAlpha.setMaximum(self.maxEEGPower)
319                self.progressBarEEGHighAlpha.setMaximum(self.maxEEGPower)
320                self.progressBarEEGLowBeta.setMaximum(self.maxEEGPower)
321                self.progressBarEEGHighBeta.setMaximum(self.maxEEGPower)
322                self.progressBarEEGLowGamma.setMaximum(self.maxEEGPower)
323                self.progressBarEEGMidGamma.setMaximum(self.maxEEGPower)
324       
325       
326        ##################################################################
327       
328        def startThinkGearConnectServer(self):
329               
330                # Ensure EEG device is connected first
331               
332                if not self.pushButtonBluetoothConnect.isChecked():
333                        self.connectToThinkGearDevice()
334               
335               
336                self.pushButtonBluetoothSearch.setEnabled(False)
337                self.pushButtonBluetoothConnect.setEnabled(False)
338               
339                server_interface = str(self.lineEditThinkGearHost.text())
340                server_port = int(self.lineEditThinkGearPort.text())
341                device_address = str(self.comboBoxDeviceSelect.currentText())
342                emulate_headset_data = (device_address == 'ThinkGear Emulator')
343               
344               
345                self.thinkGearConnectServer = \
346                        synapse_server.ThinkgearServer( \
347                                self.log, \
348                                server_interface=server_interface, \
349                                server_port=server_port, \
350                                device_address=device_address, \
351                                device_model=None, \
352                                emulate_headset_data=emulate_headset_data, \
353                                DEBUG=DEBUG, \
354                                parent=self)
355               
356                self.connect(self.thinkGearConnectServer, \
357                             QtCore.SIGNAL("sendPacket()"), \
358                             self.thinkGearConnectServer.sendPacketQueue)
359               
360                self.thinkGearConnectServer.start()
361               
362               
363                if UPDATE_INTERFACE_VIA_TIMER:
364                        self.updateInterfaceTimer.start(INTERFACE_UPDATE_FREQUENCY)
365               
366                else:
367                        self.thinkgearConnectClient = \
368                                thinkgear_client.QtClient( \
369                                        self.log, \
370                                        server_host=server_interface, \
371                                        server_port=server_port, \
372                                        DEBUG=0, \
373                                        parent=self)
374                       
375                        self.thinkgearConnectClient.start()
376               
377               
378                self.disconnect(self.pushButtonThinkGearConnect, \
379                                     QtCore.SIGNAL("clicked()"), \
380                                     self.startThinkGearConnectServer)
381               
382                self.connect(self.pushButtonThinkGearConnect, \
383                                  QtCore.SIGNAL("clicked()"), \
384                                  self.stopThinkGearConnectServer)
385               
386                self.lineEditThinkGearHost.setEnabled(False)
387                self.lineEditThinkGearPort.setEnabled(False)
388               
389                self.pushButtonThinkGearConnect.setText('Stop')
390       
391       
392        ##################################################################
393       
394        def stopThinkGearConnectServer(self):
395               
396                if UPDATE_INTERFACE_VIA_TIMER:
397                        self.updateInterfaceTimer.stop()
398                else:
399                        try:
400                                self.thinkgearConnectClient.disconnectFromHost()
401                        except Exception, e:
402                                if self.DEBUG:
403                                        print "Call failed to self.thinkgearConnectClient.disconnectFromHost():",
404                                        print e
405                       
406                        try:
407                                self.thinkGearConnectServer.exitThread()
408                        except Exception, e:
409                                if self.DEBUG:
410                                        print "Call failed to self.thinkGearConnectServer.exitThread():",
411                                        print e
412               
413                self.disconnect(self.pushButtonThinkGearConnect, \
414                                QtCore.SIGNAL("clicked()"), \
415                                self.stopThinkGearConnectServer)
416               
417                self.connect(self.pushButtonThinkGearConnect, \
418                                  QtCore.SIGNAL("clicked()"), \
419                                  self.startThinkGearConnectServer)
420               
421                self.lineEditThinkGearHost.setEnabled(True)
422                self.lineEditThinkGearPort.setEnabled(True)
423               
424                self.pushButtonThinkGearConnect.setText('Start')
425               
426                #self.pushButtonBluetoothSearch.setEnabled(True)
427                self.pushButtonBluetoothConnect.setEnabled(True)
428               
429                self.pushButtonThinkGearConnect.setChecked(False)
430       
431       
432        ##################################################################
433       
434        def updateInterface(self):
435               
436                if not self.thinkGearConnectServer.emulate_headset_data:
437                        self.processPacketThinkGear( \
438                                self.thinkGearConnectServer.protocol.data_packet)
439       
440       
441        ##################################################################
442       
443        def processPacketThinkGear(self, packet):
444               
445                if self.DEBUG > 2:
446                        print packet
447               
448               
449                if ('rawEeg' in packet.keys()):
450                        self.packets['rawEeg'].append(packet['rawEeg'])
451                        value = packet['rawEeg']
452                        if MATPLOTLIB_AVAILABLE and \
453                                (self.tabWidget.currentIndex() == \
454                                 self.tabWidget.indexOf(self.tabEEGSignals)):
455                                self.rawEEGMatplot.update_figure(value)
456                else:
457                        self.packets['signals'].append(packet)
458               
459               
460                if ('poorSignalLevel' in packet.keys()):
461                        value = 200 - packet['poorSignalLevel']
462                        self.progressBarSignalContactQuality.setValue(value)
463                        self.textEditDebugConsole.append("")
464                        try:
465                                (date, localtime) = self.parseTimeStamp(packet['timestamp'])
466                                self.textEditDebugConsole.append("Timestamp: %s %s" % (date, localtime))
467                        except:
468                                pass
469                        self.textEditDebugConsole.append("poorSignalLevel: %i" % \
470                                                         packet['poorSignalLevel'])
471               
472               
473                if ('eSense' in packet.keys()):
474                       
475                        if ('attention' in packet['eSense'].keys()):
476                                value = packet['eSense']['attention']
477                                self.progressBarAttention.setValue(value)
478                                self.textEditDebugConsole.append("eSense attention: %i" % value)
479                       
480                        if ('meditation' in packet['eSense'].keys()):
481                                value = packet['eSense']['meditation']
482                                self.progressBarMeditation.setValue(value)
483                                self.textEditDebugConsole.append("eSense meditation: %i" % value)
484                       
485                       
486                        if MATPLOTLIB_AVAILABLE:
487                                self.chartEEGMatplot.update_values('eSense', packet['eSense'])
488                                if (self.tabWidget.currentIndex() == \
489                                    self.tabWidget.indexOf(self.tabCharts)):
490                                        self.chartEEGMatplot.update_figure('eSense', packet['eSense'])
491               
492               
493                if ('eegPower' in packet.keys()):
494                       
495                        # If we are not emulating packets we'll set the maximum EEG Power value
496                        # threshold to the default (or maximum value found within this packet)
497                        if not self.thinkGearConnectServer.emulate_headset_data:
498                                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
499                       
500                        for value in packet['eegPower'].keys():
501                                if packet['eegPower'][value] > self.maxEEGPower:
502                                        self.maxEEGPower = packet['eegPower'][value]
503                       
504                       
505                        if ('delta' in packet['eegPower'].keys()):
506                                value = packet['eegPower']['delta']
507                                self.progressBarEEGDelta.setMaximum(self.maxEEGPower)
508                                self.progressBarEEGDelta.setValue(value)
509                                self.textEditDebugConsole.append("delta: %i" % value)
510                       
511                        if ('theta' in packet['eegPower'].keys()):
512                                value = packet['eegPower']['theta']
513                                self.progressBarEEGTheta.setMaximum(self.maxEEGPower)
514                                self.progressBarEEGTheta.setValue(value)
515                                self.textEditDebugConsole.append("theta: %i" % value)
516                       
517                        if ('lowAlpha' in packet['eegPower'].keys()):
518                                value = packet['eegPower']['lowAlpha']
519                                self.progressBarEEGLowAlpha.setMaximum(self.maxEEGPower)
520                                self.progressBarEEGLowAlpha.setValue(value)
521                                self.textEditDebugConsole.append("lowAlpha: %i" % value)
522                       
523                        if ('highAlpha' in packet['eegPower'].keys()):
524                                value = packet['eegPower']['highAlpha']
525                                self.progressBarEEGHighAlpha.setMaximum(self.maxEEGPower)
526                                self.progressBarEEGHighAlpha.setValue(value)
527                                self.textEditDebugConsole.append("highAlpha: %i" % value)
528                       
529                        if ('lowBeta' in packet['eegPower'].keys()):
530                                value = packet['eegPower']['lowBeta']
531                                self.progressBarEEGLowBeta.setMaximum(self.maxEEGPower)
532                                self.progressBarEEGLowBeta.setValue(value)
533                                self.textEditDebugConsole.append("lowBeta: %i" % value)
534                       
535                        if ('highBeta' in packet['eegPower'].keys()):
536                                value = packet['eegPower']['highBeta']
537                                self.progressBarEEGHighBeta.setMaximum(self.maxEEGPower)
538                                self.progressBarEEGHighBeta.setValue(value)
539                                self.textEditDebugConsole.append("highBeta: %i" % value)
540                       
541                        if ('lowGamma' in packet['eegPower'].keys()):
542                                value = packet['eegPower']['lowGamma']
543                                self.progressBarEEGLowGamma.setMaximum(self.maxEEGPower)
544                                self.progressBarEEGLowGamma.setValue(value)
545                                self.textEditDebugConsole.append("lowGamma: %i" % value)
546                       
547                        if ('highGamma' in packet['eegPower'].keys()):
548                                value = packet['eegPower']['highGamma']
549                                self.progressBarEEGMidGamma.setMaximum(self.maxEEGPower)
550                                self.progressBarEEGMidGamma.setValue(value)
551                                self.textEditDebugConsole.append("highGamma: %i" % value)
552                       
553                       
554                        if MATPLOTLIB_AVAILABLE:
555                                self.chartEEGMatplot.update_values('eegPower', packet['eegPower'])
556                                if (self.tabWidget.currentIndex() == \
557                                    self.tabWidget.indexOf(self.tabCharts)):
558                                        self.chartEEGMatplot.update_figure('eegPower', packet['eegPower'])
559               
560               
561                if ((self.thinkGearConnectServer.protocol != None) and
562                    (self.tabWidget.currentIndex() == \
563                     self.tabWidget.indexOf(self.tabControlPanel))):
564                       
565                        self.updateProfileSessionStatus()
566       
567       
568        ##################################################################
569       
570        def updateProfileSessionStatus(self):
571               
572                session_time = self.calculateSessionTime()
573               
574                if self.parent == None:
575                        target = self
576                else:
577                        target = self.parent
578               
579                target.textLabelSessionTime.setText(session_time)
580               
581                target.textLabelPacketsReceived.setText( "%i" % \
582                        target.thinkGearConnectServer.protocol.packet_count)
583                target.textLabelPacketsDropped.setText( "%i" % \
584                        target.thinkGearConnectServer.protocol.bad_packets)
585       
586       
587        ##################################################################
588       
589        def calculateSessionTime(self):
590               
591                if self.parent == None:
592                        server = self.thinkGearConnectServer
593                else:
594                        server = self.parent.thinkGearConnectServer
595               
596                session_time = time.time() - \
597                        server.protocol.session_start_timestamp
598               
599                session_time = int(session_time)
600               
601                session_time = self.convert_seconds_to_datetime(session_time)
602               
603                return(session_time)
604       
605       
606        ##################################################################
607       
608        def enumerateSerialPorts(self):
609               
610                """ Uses the Win32 registry to return an
611                iterator of serial (COM) ports
612                existing on this computer.
613               
614                from http://eli.thegreenplace.net/2009/07/31/listing-all-serial-ports-on-windows-with-python/
615                """
616         
617                path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
618                try:
619                        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
620                except WindowsError:
621                        #raise IterationError
622                        return
623               
624                for i in itertools.count():
625                        try:
626                                val = winreg.EnumValue(key, i)
627                                yield str(val[1])
628                        except EnvironmentError:
629                                break
630       
631       
632        ##################################################################
633       
634        def fullPortName(self, portname):
635               
636                """ Given a port-name (of the form COM7,
637                COM12, CNCA0, etc.) returns a full
638                name suitable for opening with the
639                Serial class.
640                """
641               
642                m = re.match('^COM(\d+)$', portname)
643                if m and int(m.group(1)) < 10:
644                        return portname
645               
646                return '\\\\.\\' + portname
647       
648       
649        ##################################################################
650       
651        def searchForThinkGearDevices(self):
652               
653                thinkgear_devices = []
654               
655                #self.pushButtonBluetoothSearch.setText('Searching')
656               
657                if (sys.platform == 'win32'):
658                       
659                        # Bluetooth module doesn't compile properly under WindowsError
660                       
661                        for portname in self.enumerateSerialPorts():
662                               
663                                if portname not in thinkgear_devices:
664                                        #portname = self.fullPortName(portname)
665                                        thinkgear_devices.append(portname)
666               
667               
668                else:
669                       
670                        bluetooth_devices = []
671                       
672                        try:
673                                bluetooth_devices = bluetooth.discover_devices( \
674                                                       duration=5, \
675                                                       flush_cache=True, \
676                                                       lookup_names=False)
677                        except:
678                                #command = '%s con' % PATH_TO_HCITOOL
679                                command = '%s scan' % PATH_TO_HCITOOL
680                               
681                                if self.DEBUG:
682                                        print 'Calling "%s":' % command
683                               
684                                output = os.popen(command, 'r')
685                               
686                                for line in output.readlines():
687                                        print line
688                                        try:
689                                                address = line.split(' ')[2]
690                                        except:
691                                                pass
692                                        else:
693                                                bluetooth_devices.append(address)
694                       
695                       
696                        for address in bluetooth_devices:
697                                device_name = bluetooth.lookup_name(address)
698                                if ((device_name == 'MindSet') and \
699                                    (address not in thinkgear_devices)):
700                                        thinkgear_devices.append(address)
701                       
702                       
703                        if self.DEBUG > 2:
704                                print "Bluetooth MindSet devices found:",
705                                print thinkgear_devices
706                       
707                       
708                        if self.parent == None:
709                                self.comboBoxDeviceSelect.clear()
710                        else:
711                                self.parent.comboBoxDeviceSelect.clear()
712                       
713                        #self.comboBoxDeviceSelect.addItem('ThinkGear Emulator')
714                       
715                        if os.path.exists('/dev/ttyUSB0'):
716                                thinkgear_devices.append('/dev/ttyUSB0')
717                        if os.path.exists('/dev/ttyUSB1'):
718                                thinkgear_devices.append('/dev/ttyUSB1')
719                        if os.path.exists('/dev/ttyUSB2'):
720                                thinkgear_devices.append('/dev/ttyUSB2')
721                        if os.path.exists('/dev/ttyUSB3'):
722                                thinkgear_devices.append('/dev/ttyUSB3')
723                        if os.path.exists('/dev/ttyUSB4'):
724                                thinkgear_devices.append('/dev/ttyUSB4')
725                        if os.path.exists('/dev/ttyUSB5'):
726                                thinkgear_devices.append('/dev/ttyUSB5')
727                        if os.path.exists('/dev/ttyUSB6'):
728                                thinkgear_devices.append('/dev/ttyUSB6')
729                        if os.path.exists('/dev/ttyUSB7'):
730                                thinkgear_devices.append('/dev/ttyUSB7')
731                        if os.path.exists('/dev/ttyUSB8'):
732                                thinkgear_devices.append('/dev/ttyUSB8')
733                        if os.path.exists('/dev/ttyUSB9'):
734                                thinkgear_devices.append('/dev/ttyUSB9')
735               
736                        if os.path.exists('/dev/ttyACM0'):
737                                thinkgear_devices.append('/dev/ttyACM0')
738                        if os.path.exists('/dev/ttyACM1'):
739                                thinkgear_devices.append('/dev/ttyACM1')
740                        if os.path.exists('/dev/ttyACM2'):
741                                thinkgear_devices.append('/dev/ttyACM2')
742                        if os.path.exists('/dev/ttyACM3'):
743                                thinkgear_devices.append('/dev/ttyACM3')
744                        if os.path.exists('/dev/ttyACM4'):
745                                thinkgear_devices.append('/dev/ttyACM4')
746               
747                if self.DEBUG:
748                        print "ThinkGear devices found:",
749                        print thinkgear_devices
750               
751               
752                return(thinkgear_devices)
753       
754       
755        ##################################################################
756       
757        def updateThinkGearDevices(self):
758               
759                devices = self.searchForThinkGearDevices()
760               
761                devices.insert(0, 'ThinkGear Emulator')
762               
763                for device in devices:
764                        self.comboBoxDeviceSelect.addItem(device)
765       
766       
767        ##################################################################
768       
769        def collectData(self):
770               
771                if self == None:
772                        target = self
773                else:
774                        target = self.parent
775               
776                data = {}
777               
778                data['rawEeg'] = target.packets['rawEeg']
779                data['signals'] = target.packets['signals']
780               
781                data['sessionTime'] = self.calculateSessionTime()
782               
783                data['profileName'] = str(target.lineEditSessionProfile.text())
784               
785                return(data)
786       
787       
788        ##################################################################
789       
790        def parseTimeStamp(self, timestamp, local_version=False, truncate_time_zone=False):
791               
792                try:
793                        decimal = '%f' % timestamp
794                        decimal = decimal.split('.')[1]
795                except:
796                        decimal = '0'
797               
798                localtime = time.localtime(timestamp)
799               
800                if local_version:
801                        date = time.strftime('%x', localtime)
802                        localtime = time.strftime('%X', localtime)
803               
804                elif truncate_time_zone:
805                        date = time.strftime('%Y-%m-%d', localtime)
806                        localtime = time.strftime('%H:%M:%S', localtime)
807                        localtime = '%s.%s' % (localtime, decimal[:3])
808               
809                else:
810                        date = time.strftime('%Y-%m-%d', localtime)
811                        localtime = time.strftime('%H:%M:%S', localtime)
812                        localtime = '%s.%s %s' % (localtime, decimal, \
813                                       time.strftime('%Z', time.localtime(timestamp)))
814               
815               
816                return(date, localtime)
817       
818       
819        ##################################################################
820       
821        def saveData(self):
822               
823                if self == None:
824                        target = self
825                else:
826                        target = self.parent
827               
828                data = self.collectData()
829               
830                (date, localtime) = self.parseTimeStamp(time.time())
831               
832                default_filename = '%s %s.synapse' % (date, \
833                                      target.lineEditSessionProfile.text())
834                                     
835                default_filename = os.path.join(self.homepath, default_filename)
836               
837                output_file = QtGui.QFileDialog.getSaveFileName(parent=target, \
838                                 caption="Save Session Data to File", \
839                                 dir=default_filename, \
840                                 filter="Puzzlebox Synapse Data File (*.synapse)")
841               
842                try:
843                        output_file = output_file[0]
844                except:
845                        output_file = ''
846               
847                if output_file == '':
848                        return
849               
850                file = open(str(output_file), 'w')
851                pickle.dump(data, file)
852                file.close()
853       
854       
855        ##################################################################
856       
857        def exportData(self):
858               
859                if self.parent == None:
860                        target = self
861                else:
862                        target = self.parent
863               
864                (date, localtime) = self.parseTimeStamp(time.time())
865               
866                default_filename = '%s %s.csv' % (date, \
867                                      target.lineEditSessionProfile.text())
868               
869                default_filename = os.path.join(target.homepath, default_filename)
870               
871                output_file = QtGui.QFileDialog.getSaveFileName(parent=target, \
872                                 caption="Export Session Data to File", \
873                                 dir=default_filename, \
874                                 filter="CSV File (*.csv);;Text File (*.txt)")
875               
876                try:
877                        output_file = output_file[0]
878                except:
879                        output_file = ''
880               
881                if output_file == '':
882                        return
883               
884                if str(output_file).endswith('.csv'):
885                       
886                        outputData = self.exportDataToCSV()
887               
888               
889                else:
890                       
891                        try:
892                                outputData = self.textEditDebugConsole.toPlainText()
893                        except:
894                                outputData = self.exportDataToCSV()
895                       
896                       
897                file = open(str(output_file), 'w')
898                file.write(outputData)
899                file.close()
900       
901       
902        ##################################################################
903       
904        def exportDataToCSV(self):
905               
906                if self == None:
907                        target = self
908                else:
909                        target = self.parent
910               
911                try:
912                        truncate_csv_timezone = target.configuration.EXPORT_CSV_TRUNCATE_TIMEZONE
913                except:
914                        truncate_csv_timezone = False
915               
916                try:
917                        scrub_data = target.configuration.EXPORT_CSV_SCRUB_DATA
918                except:
919                        scrub_data = False
920               
921               
922                headers = 'Date,Time,Delta,Theta,Low Alpha,High Alpha,Low Beta,High Beta,Low Gamma,Mid Gamma,Attention,Meditation,Signal Level'
923               
924                customDataHeaders = []
925                for header in target.customDataHeaders:
926                        customDataHeaders.append(header)
927                for plugin in target.activePlugins:
928                        for header in plugin.customDataHeaders:
929                                customDataHeaders.append(header)
930               
931                for each in customDataHeaders:
932                        headers = headers + ',%s' % each
933               
934                headers = headers + '\n'
935               
936                csv = {}
937               
938                for packet in target.packets['signals']:
939                       
940                        if 'rawEeg' in packet.keys():
941                                continue
942                       
943                        if packet['timestamp'] not in csv.keys():
944                               
945                                #print packet
946                                timestamp = packet['timestamp']
947                                (date, localtime) = self.parseTimeStamp(timestamp, \
948                                                    truncate_time_zone=truncate_csv_timezone)
949                               
950                                csv[timestamp] = {}
951                                csv[timestamp]['Date'] = date
952                                csv[timestamp]['Time'] = localtime
953                                csv[timestamp]['Delta'] = ''
954                                csv[timestamp]['Theta'] = ''
955                                csv[timestamp]['Low Alpha'] = ''
956                                csv[timestamp]['High Alpha'] = ''
957                                csv[timestamp]['Low Beta'] = ''
958                                csv[timestamp]['High Beta'] = ''
959                                csv[timestamp]['Low Gamma'] = ''
960                                csv[timestamp]['Mid Gamma'] = ''
961                                csv[timestamp]['Attention'] = ''
962                                csv[timestamp]['Meditation'] = ''
963                                csv[timestamp]['Signal Level'] = ''
964                               
965                                for header in customDataHeaders:
966                                        csv[timestamp][header] = ''
967                       
968                       
969                        if 'eSense' in packet.keys():
970                                if 'attention' in packet['eSense'].keys():
971                                        csv[timestamp]['Attention'] = packet['eSense']['attention']
972                                if 'meditation' in packet['eSense'].keys():
973                                        csv[timestamp]['Meditation'] = packet['eSense']['meditation']
974                       
975                        if 'eegPower' in packet.keys():
976                                if 'delta' in packet['eegPower'].keys():
977                                        csv[timestamp]['Delta'] = packet['eegPower']['delta']
978                                if 'theta' in packet['eegPower'].keys():
979                                        csv[timestamp]['Theta'] = packet['eegPower']['theta']
980                                if 'lowAlpha' in packet['eegPower'].keys():
981                                        csv[timestamp]['Low Alpha'] = packet['eegPower']['lowAlpha']
982                                if 'highAlpha' in packet['eegPower'].keys():
983                                        csv[timestamp]['High Alpha'] = packet['eegPower']['highAlpha']
984                                if 'lowBeta' in packet['eegPower'].keys():
985                                        csv[timestamp]['Low Beta'] = packet['eegPower']['lowBeta']
986                                if 'highBeta' in packet['eegPower'].keys():
987                                        csv[timestamp]['High Beta'] = packet['eegPower']['highBeta']
988                                if 'lowGamma' in packet['eegPower'].keys():
989                                        csv[timestamp]['Low Gamma'] = packet['eegPower']['lowGamma']
990                                if 'highGamma' in packet['eegPower'].keys():
991                                        csv[timestamp]['Mid Gamma'] = packet['eegPower']['highGamma']
992                       
993                        if 'poorSignalLevel' in packet.keys():
994                                csv[timestamp]['Signal Level'] = packet['poorSignalLevel']
995                       
996                        for header in customDataHeaders:
997                                if 'custom' in packet.keys() and \
998                                   header in packet['custom'].keys():
999                                        csv[timestamp][header] = packet['custom'][header]
1000               
1001               
1002                if scrub_data:
1003                        csv = self.scrubData(csv, truncate_csv_timezone)
1004               
1005               
1006                output = headers
1007               
1008                csv_keys = csv.keys()
1009                csv_keys.sort()
1010               
1011                for key in csv_keys:
1012                       
1013                        row = '%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s' % \
1014                              (csv[key]['Date'], \
1015                               csv[key]['Time'], \
1016                               csv[key]['Delta'], \
1017                               csv[key]['Theta'], \
1018                               csv[key]['Low Alpha'], \
1019                               csv[key]['High Alpha'], \
1020                               csv[key]['Low Beta'], \
1021                               csv[key]['High Beta'], \
1022                               csv[key]['Low Gamma'], \
1023                               csv[key]['Mid Gamma'], \
1024                               csv[key]['Attention'], \
1025                               csv[key]['Meditation'], \
1026                               csv[key]['Signal Level'])
1027                       
1028                        for header in customDataHeaders:
1029                                row = row + ',%s' % csv[key][header]
1030                       
1031                        row = row + '\n'
1032                       
1033                        output = output + row
1034               
1035               
1036                return(output)
1037       
1038       
1039        ##################################################################
1040       
1041        def scrubData(self, csv, truncate_csv_timezone=False):
1042               
1043                # If there are missing packets, repeat a given packet once per missing
1044                # second until there is a gap between 1 and 2 seconds, in which case
1045                # produce a final duplicate packet at the mid-point between the packets
1046
1047                if self.DEBUG:
1048                        print "INFO: Scrubbing Data"
1049               
1050                last_time = None
1051                last_recorded_time = None
1052               
1053                output = {}
1054               
1055                csv_keys = csv.keys()
1056                csv_keys.sort()
1057               
1058                for key in csv_keys:
1059                       
1060                        timestamp = key
1061
1062                        if csv[key]['Attention'] == '':
1063                                continue
1064                       
1065                        if last_time == None:
1066                                # First entry in log
1067                                last_time = timestamp
1068                                last_recorded_time = timestamp
1069                                output[key] = csv[key]
1070                                continue
1071                       
1072                        else:
1073                               
1074                                #time_difference = timestamp - last_time
1075                                time_difference = timestamp - last_recorded_time
1076                               
1077                                if (time_difference <= 1) and \
1078                                   (time_difference >= PACKET_MINIMUM_TIME_DIFFERENCE_THRESHOLD):
1079                                        # Skip packets within the correct time threshold
1080                                        last_time = timestamp
1081                                        last_recorded_time = timestamp
1082                                        output[key] = csv[key]
1083                                        continue
1084                               
1085                                else:
1086
1087                                        if self.DEBUG > 1:
1088                                                print "time_difference:",
1089                                                print time_difference
1090                                                print "timestamp:",
1091                                                print self.parseTimeStamp(timestamp)[-1].split(' ')[0]
1092                                                print "last_time:",
1093                                                print self.parseTimeStamp(last_time)[-1].split(' ')[0]
1094                                                print "last_recorded_time:",
1095                                                print self.parseTimeStamp(last_recorded_time)[-1].split(' ')[0]
1096
1097                                       
1098                                        new_packet = csv[key].copy()
1099                                       
1100                                        if time_difference >= 2:
1101                                               
1102                                                ##new_time = last_time + 1
1103                                                #new_time = last_recorded_time + 1
1104
1105                                                count = int(time_difference)
1106                                                while count >= 1:
1107                                                        new_packet = csv[key].copy()
1108                                                        new_time = last_recorded_time + 1
1109                                                        (date, formatted_new_time) = self.parseTimeStamp(new_time, \
1110                                                         truncate_time_zone=truncate_csv_timezone)
1111                                                        new_packet['Time'] = formatted_new_time
1112                                                        last_recorded_time = new_time
1113                                                        last_time = timestamp
1114                                                        output[new_time] = new_packet
1115                                                        count = count - 1
1116                                                continue
1117                                               
1118                                        elif time_difference < PACKET_MINIMUM_TIME_DIFFERENCE_THRESHOLD:
1119                                                # Spread out "bunched up" packets
1120                                                #new_time = last_time + 1
1121                                                new_time = last_recorded_time + 1
1122                                       
1123                                       
1124                                        elif (time_difference < 2) and (time_difference > 1):
1125                                               
1126                                                #new_time = last_time + ((last_time - timestamp) / 2)
1127                                                #new_time = last_recorded_time + ((last_recorded_time - timestamp) / 2)
1128                                                #new_time = last_time + 1
1129                                                new_time = last_recorded_time + 1
1130                                       
1131                                       
1132                                        (date, formatted_new_time) = self.parseTimeStamp(new_time, \
1133                                           truncate_time_zone=truncate_csv_timezone)
1134                                       
1135                                        new_packet['Time'] = formatted_new_time
1136                                       
1137                                        #last_time = new_time
1138                                        last_recorded_time = new_time
1139                                        last_time = timestamp
1140                                        output[new_time] = new_packet
1141                                       
1142                                        if self.DEBUG > 1:
1143                                                print "WARN: Scrubbing new packet:",
1144                                                print new_packet
1145                                                print
1146               
1147               
1148                return(output)
1149       
1150       
1151        ##################################################################
1152
1153        #def processTimeDifference(self,
1154
1155        ##################################################################
1156       
1157        def resetData(self):
1158               
1159                if self == None:
1160                        target = self
1161                else:
1162                        target = self.parent
1163               
1164                target.packets['rawEeg'] = []
1165                target.packets['signals'] = []
1166               
1167                target.thinkGearConnectServer.protocol.session_start_timestamp = \
1168                        time.time()
1169               
1170                target.thinkGearConnectServer.protocol.packet_count = 0
1171                target.thinkGearConnectServer.protocol.bad_packets = 0
1172               
1173                self.updateProfileSessionStatus()
1174               
1175                try:
1176                        target.textEditDebugConsole.setText("")
1177                except:
1178                        pass
1179       
1180       
1181        #####################################################################
1182       
1183        def convert_seconds_to_datetime(self, duration):
1184               
1185                duration_hours = duration / (60 * 60)
1186                duration_minutes = (duration - (duration_hours * (60 * 60))) / 60
1187                duration_seconds = (duration - (duration_hours * (60 * 60)) - (duration_minutes * 60))
1188               
1189                duration_hours = '%i' % duration_hours
1190                if (len(duration_hours) == 1):
1191                        duration_hours = "0%s" % duration_hours
1192               
1193                duration_minutes = '%i' % duration_minutes
1194                if (len(duration_minutes) == 1):
1195                        duration_minutes = "0%s" % duration_minutes
1196               
1197                duration_seconds = '%i' % duration_seconds
1198                if (len(duration_seconds) == 1):
1199                        duration_seconds = "0%s" % duration_seconds
1200               
1201                datetime = '%s:%s:%s' % (duration_hours, duration_minutes, duration_seconds)
1202               
1203                return(datetime)
1204       
1205       
1206        ##################################################################
1207       
1208        def stop(self):
1209               
1210                if UPDATE_INTERFACE_VIA_TIMER:
1211                        self.updateInterfaceTimer.stop()
1212                else:
1213                        if self.thinkgearConnectClient != None:
1214                                self.thinkgearConnectClient.disconnectFromHost()
1215               
1216                if self.thinkGearConnectServer != None:
1217                        self.thinkGearConnectServer.exitThread()
1218       
1219       
1220        ##################################################################
1221       
1222        def closeEvent(self, event):
1223               
1224                quit_message = "Are you sure you want to exit the program?"
1225               
1226                reply = QtGui.QMessageBox.question( \
1227                           self, \
1228                          'Message', \
1229                           quit_message, \
1230                           QtGui.QMessageBox.Yes, \
1231                           QtGui.QMessageBox.No)
1232               
1233                if reply == QtGui.QMessageBox.Yes:
1234                       
1235                        self.stop()
1236                       
1237                        event.accept()
1238               
1239                else:
1240                        event.ignore()
Note: See TracBrowser for help on using the repository browser.