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

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