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

Last change on this file since 292 was 292, checked in by sc, 10 years ago
  • Update to emit signals between Server and Interface instead of calling parent methods directly
  • Property svn:executable set to *
File size: 34.6 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                                emulate_headset_data=emulate_headset_data, \
352                                DEBUG=DEBUG, \
353                                parent=self)
354               
355                self.thinkGearConnectServer.start()
356               
357               
358                if UPDATE_INTERFACE_VIA_TIMER:
359                        self.updateInterfaceTimer.start(INTERFACE_UPDATE_FREQUENCY)
360               
361                else:
362                        self.thinkgearConnectClient = \
363                                thinkgear_client.QtClient( \
364                                        self.log, \
365                                        server_host=server_interface, \
366                                        server_port=server_port, \
367                                        DEBUG=0, \
368                                        parent=self)
369                       
370                        self.thinkgearConnectClient.start()
371               
372               
373                self.disconnect(self.pushButtonThinkGearConnect, \
374                                     QtCore.SIGNAL("clicked()"), \
375                                     self.startThinkGearConnectServer)
376               
377                self.connect(self.pushButtonThinkGearConnect, \
378                                  QtCore.SIGNAL("clicked()"), \
379                                  self.stopThinkGearConnectServer)
380               
381                self.lineEditThinkGearHost.setEnabled(False)
382                self.lineEditThinkGearPort.setEnabled(False)
383               
384                self.pushButtonThinkGearConnect.setText('Stop')
385       
386       
387        ##################################################################
388       
389        def stopThinkGearConnectServer(self):
390               
391                if UPDATE_INTERFACE_VIA_TIMER:
392                        self.updateInterfaceTimer.stop()
393                else:
394                        try:
395                                self.thinkgearConnectClient.disconnectFromHost()
396                        except Exception, e:
397                                if self.DEBUG:
398                                        print "Call failed to self.thinkgearConnectClient.disconnectFromHost():",
399                                        print e
400                       
401                        try:
402                                self.thinkGearConnectServer.exitThread()
403                        except Exception, e:
404                                if self.DEBUG:
405                                        print "Call failed to self.thinkGearConnectServer.exitThread():",
406                                        print e
407               
408                self.disconnect(self.pushButtonThinkGearConnect, \
409                                QtCore.SIGNAL("clicked()"), \
410                                self.stopThinkGearConnectServer)
411               
412                self.connect(self.pushButtonThinkGearConnect, \
413                                  QtCore.SIGNAL("clicked()"), \
414                                  self.startThinkGearConnectServer)
415               
416                self.lineEditThinkGearHost.setEnabled(True)
417                self.lineEditThinkGearPort.setEnabled(True)
418               
419                self.pushButtonThinkGearConnect.setText('Start')
420               
421                #self.pushButtonBluetoothSearch.setEnabled(True)
422                self.pushButtonBluetoothConnect.setEnabled(True)
423               
424                self.pushButtonThinkGearConnect.setChecked(False)
425       
426       
427        ##################################################################
428       
429        def updateInterface(self):
430               
431                if not self.thinkGearConnectServer.emulate_headset_data:
432                        self.processPacketThinkGear( \
433                                self.thinkGearConnectServer.protocol.data_packet)
434       
435       
436        ##################################################################
437       
438        def processPacketThinkGear(self, packet):
439               
440                if self.DEBUG > 2:
441                        print packet
442               
443               
444                if ('rawEeg' in packet.keys()):
445                        self.packets['rawEeg'].append(packet['rawEeg'])
446                        value = packet['rawEeg']
447                        if MATPLOTLIB_AVAILABLE and \
448                                (self.tabWidget.currentIndex() == \
449                                 self.tabWidget.indexOf(self.tabEEGSignals)):
450                                self.rawEEGMatplot.update_figure(value)
451                else:
452                        self.packets['signals'].append(packet)
453               
454               
455                if ('poorSignalLevel' in packet.keys()):
456                        value = 200 - packet['poorSignalLevel']
457                        self.progressBarSignalContactQuality.setValue(value)
458                        self.textEditDebugConsole.append("")
459                        try:
460                                (date, localtime) = self.parseTimeStamp(packet['timestamp'])
461                                self.textEditDebugConsole.append("Timestamp: %s %s" % (date, localtime))
462                        except:
463                                pass
464                        self.textEditDebugConsole.append("poorSignalLevel: %i" % \
465                                                         packet['poorSignalLevel'])
466               
467               
468                if ('eSense' in packet.keys()):
469                       
470                        if ('attention' in packet['eSense'].keys()):
471                                value = packet['eSense']['attention']
472                                self.progressBarAttention.setValue(value)
473                                self.textEditDebugConsole.append("eSense attention: %i" % value)
474                       
475                        if ('meditation' in packet['eSense'].keys()):
476                                value = packet['eSense']['meditation']
477                                self.progressBarMeditation.setValue(value)
478                                self.textEditDebugConsole.append("eSense meditation: %i" % value)
479                       
480                       
481                        if MATPLOTLIB_AVAILABLE:
482                                self.chartEEGMatplot.update_values('eSense', packet['eSense'])
483                                if (self.tabWidget.currentIndex() == \
484                                    self.tabWidget.indexOf(self.tabCharts)):
485                                        self.chartEEGMatplot.update_figure('eSense', packet['eSense'])
486               
487               
488                if ('eegPower' in packet.keys()):
489                       
490                        # If we are not emulating packets we'll set the maximum EEG Power value
491                        # threshold to the default (or maximum value found within this packet)
492                        if not self.thinkGearConnectServer.emulate_headset_data:
493                                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
494                       
495                        for value in packet['eegPower'].keys():
496                                if packet['eegPower'][value] > self.maxEEGPower:
497                                        self.maxEEGPower = packet['eegPower'][value]
498                       
499                       
500                        if ('delta' in packet['eegPower'].keys()):
501                                value = packet['eegPower']['delta']
502                                self.progressBarEEGDelta.setMaximum(self.maxEEGPower)
503                                self.progressBarEEGDelta.setValue(value)
504                                self.textEditDebugConsole.append("delta: %i" % value)
505                       
506                        if ('theta' in packet['eegPower'].keys()):
507                                value = packet['eegPower']['theta']
508                                self.progressBarEEGTheta.setMaximum(self.maxEEGPower)
509                                self.progressBarEEGTheta.setValue(value)
510                                self.textEditDebugConsole.append("theta: %i" % value)
511                       
512                        if ('lowAlpha' in packet['eegPower'].keys()):
513                                value = packet['eegPower']['lowAlpha']
514                                self.progressBarEEGLowAlpha.setMaximum(self.maxEEGPower)
515                                self.progressBarEEGLowAlpha.setValue(value)
516                                self.textEditDebugConsole.append("lowAlpha: %i" % value)
517                       
518                        if ('highAlpha' in packet['eegPower'].keys()):
519                                value = packet['eegPower']['highAlpha']
520                                self.progressBarEEGHighAlpha.setMaximum(self.maxEEGPower)
521                                self.progressBarEEGHighAlpha.setValue(value)
522                                self.textEditDebugConsole.append("highAlpha: %i" % value)
523                       
524                        if ('lowBeta' in packet['eegPower'].keys()):
525                                value = packet['eegPower']['lowBeta']
526                                self.progressBarEEGLowBeta.setMaximum(self.maxEEGPower)
527                                self.progressBarEEGLowBeta.setValue(value)
528                                self.textEditDebugConsole.append("lowBeta: %i" % value)
529                       
530                        if ('highBeta' in packet['eegPower'].keys()):
531                                value = packet['eegPower']['highBeta']
532                                self.progressBarEEGHighBeta.setMaximum(self.maxEEGPower)
533                                self.progressBarEEGHighBeta.setValue(value)
534                                self.textEditDebugConsole.append("highBeta: %i" % value)
535                       
536                        if ('lowGamma' in packet['eegPower'].keys()):
537                                value = packet['eegPower']['lowGamma']
538                                self.progressBarEEGLowGamma.setMaximum(self.maxEEGPower)
539                                self.progressBarEEGLowGamma.setValue(value)
540                                self.textEditDebugConsole.append("lowGamma: %i" % value)
541                       
542                        if ('highGamma' in packet['eegPower'].keys()):
543                                value = packet['eegPower']['highGamma']
544                                self.progressBarEEGMidGamma.setMaximum(self.maxEEGPower)
545                                self.progressBarEEGMidGamma.setValue(value)
546                                self.textEditDebugConsole.append("highGamma: %i" % value)
547                       
548                       
549                        if MATPLOTLIB_AVAILABLE:
550                                self.chartEEGMatplot.update_values('eegPower', packet['eegPower'])
551                                if (self.tabWidget.currentIndex() == \
552                                    self.tabWidget.indexOf(self.tabCharts)):
553                                        self.chartEEGMatplot.update_figure('eegPower', packet['eegPower'])
554               
555               
556                if ((self.thinkGearConnectServer.protocol != None) and
557                    (self.tabWidget.currentIndex() == \
558                     self.tabWidget.indexOf(self.tabControlPanel))):
559                       
560                        self.updateProfileSessionStatus()
561       
562       
563        ##################################################################
564       
565        def updateProfileSessionStatus(self):
566               
567                session_time = self.calculateSessionTime()
568               
569                if self.parent == None:
570                        target = self
571                else:
572                        target = self.parent
573               
574                target.textLabelSessionTime.setText(session_time)
575               
576                target.textLabelPacketsReceived.setText( "%i" % \
577                        target.thinkGearConnectServer.protocol.packet_count)
578                target.textLabelPacketsDropped.setText( "%i" % \
579                        target.thinkGearConnectServer.protocol.bad_packets)
580       
581       
582        ##################################################################
583       
584        def calculateSessionTime(self):
585               
586                if self.parent == None:
587                        server = self.thinkGearConnectServer
588                else:
589                        server = self.parent.thinkGearConnectServer
590               
591                session_time = time.time() - \
592                        server.protocol.session_start_timestamp
593               
594                session_time = int(session_time)
595               
596                session_time = self.convert_seconds_to_datetime(session_time)
597               
598                return(session_time)
599       
600       
601        ##################################################################
602       
603        def enumerateSerialPorts(self):
604               
605                """ Uses the Win32 registry to return an
606                iterator of serial (COM) ports
607                existing on this computer.
608               
609                from http://eli.thegreenplace.net/2009/07/31/listing-all-serial-ports-on-windows-with-python/
610                """
611         
612                path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
613                try:
614                        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
615                except WindowsError:
616                        #raise IterationError
617                        return
618               
619                for i in itertools.count():
620                        try:
621                                val = winreg.EnumValue(key, i)
622                                yield str(val[1])
623                        except EnvironmentError:
624                                break
625       
626       
627        ##################################################################
628       
629        def fullPortName(self, portname):
630               
631                """ Given a port-name (of the form COM7,
632                COM12, CNCA0, etc.) returns a full
633                name suitable for opening with the
634                Serial class.
635                """
636               
637                m = re.match('^COM(\d+)$', portname)
638                if m and int(m.group(1)) < 10:
639                        return portname
640               
641                return '\\\\.\\' + portname
642       
643       
644        ##################################################################
645       
646        def searchForThinkGearDevices(self):
647               
648                thinkgear_devices = []
649               
650                #self.pushButtonBluetoothSearch.setText('Searching')
651               
652                if (sys.platform == 'win32'):
653                       
654                        # Bluetooth module doesn't compile properly under WindowsError
655                       
656                        for portname in self.enumerateSerialPorts():
657                               
658                                if portname not in thinkgear_devices:
659                                        #portname = self.fullPortName(portname)
660                                        thinkgear_devices.append(portname)
661               
662               
663                else:
664                       
665                        bluetooth_devices = []
666                       
667                        try:
668                                bluetooth_devices = bluetooth.discover_devices( \
669                                                       duration=5, \
670                                                       flush_cache=True, \
671                                                       lookup_names=False)
672                        except:
673                                #command = '%s con' % PATH_TO_HCITOOL
674                                command = '%s scan' % PATH_TO_HCITOOL
675                               
676                                if self.DEBUG:
677                                        print 'Calling "%s":' % command
678                               
679                                output = os.popen(command, 'r')
680                               
681                                for line in output.readlines():
682                                        print line
683                                        try:
684                                                address = line.split(' ')[2]
685                                        except:
686                                                pass
687                                        else:
688                                                bluetooth_devices.append(address)
689                       
690                       
691                        for address in bluetooth_devices:
692                                device_name = bluetooth.lookup_name(address)
693                                if ((device_name == 'MindSet') and \
694                                    (address not in thinkgear_devices)):
695                                        thinkgear_devices.append(address)
696                       
697                       
698                        if self.DEBUG > 2:
699                                print "Bluetooth MindSet devices found:",
700                                print thinkgear_devices
701                       
702                       
703                        if self.parent == None:
704                                self.comboBoxDeviceSelect.clear()
705                        else:
706                                self.parent.comboBoxDeviceSelect.clear()
707                       
708                        #self.comboBoxDeviceSelect.addItem('ThinkGear Emulator')
709                       
710                        if os.path.exists('/dev/ttyUSB0'):
711                                thinkgear_devices.append('/dev/ttyUSB0')
712                        if os.path.exists('/dev/ttyUSB1'):
713                                thinkgear_devices.append('/dev/ttyUSB1')
714                        if os.path.exists('/dev/ttyUSB2'):
715                                thinkgear_devices.append('/dev/ttyUSB2')
716                        if os.path.exists('/dev/ttyUSB3'):
717                                thinkgear_devices.append('/dev/ttyUSB3')
718                        if os.path.exists('/dev/ttyUSB4'):
719                                thinkgear_devices.append('/dev/ttyUSB4')
720                        if os.path.exists('/dev/ttyUSB5'):
721                                thinkgear_devices.append('/dev/ttyUSB5')
722                        if os.path.exists('/dev/ttyUSB6'):
723                                thinkgear_devices.append('/dev/ttyUSB6')
724                        if os.path.exists('/dev/ttyUSB7'):
725                                thinkgear_devices.append('/dev/ttyUSB7')
726                        if os.path.exists('/dev/ttyUSB8'):
727                                thinkgear_devices.append('/dev/ttyUSB8')
728                        if os.path.exists('/dev/ttyUSB9'):
729                                thinkgear_devices.append('/dev/ttyUSB9')
730               
731                        if os.path.exists('/dev/ttyACM0'):
732                                thinkgear_devices.append('/dev/ttyACM0')
733                        if os.path.exists('/dev/ttyACM1'):
734                                thinkgear_devices.append('/dev/ttyACM1')
735                        if os.path.exists('/dev/ttyACM2'):
736                                thinkgear_devices.append('/dev/ttyACM2')
737                        if os.path.exists('/dev/ttyACM3'):
738                                thinkgear_devices.append('/dev/ttyACM3')
739                        if os.path.exists('/dev/ttyACM4'):
740                                thinkgear_devices.append('/dev/ttyACM4')
741               
742                if self.DEBUG:
743                        print "ThinkGear devices found:",
744                        print thinkgear_devices
745               
746               
747                return(thinkgear_devices)
748       
749       
750        ##################################################################
751       
752        def updateThinkGearDevices(self):
753               
754                devices = self.searchForThinkGearDevices()
755               
756                devices.insert(0, 'ThinkGear Emulator')
757               
758                for device in devices:
759                        self.comboBoxDeviceSelect.addItem(device)
760       
761       
762        ##################################################################
763       
764        def collectData(self):
765               
766                if self == None:
767                        target = self
768                else:
769                        target = self.parent
770               
771                data = {}
772               
773                data['rawEeg'] = target.packets['rawEeg']
774                data['signals'] = target.packets['signals']
775               
776                data['sessionTime'] = self.calculateSessionTime()
777               
778                data['profileName'] = str(target.lineEditSessionProfile.text())
779               
780                return(data)
781       
782       
783        ##################################################################
784       
785        def parseTimeStamp(self, timestamp, local_version=False, truncate_time_zone=False):
786               
787                try:
788                        decimal = '%f' % timestamp
789                        decimal = decimal.split('.')[1]
790                except:
791                        decimal = '0'
792               
793                localtime = time.localtime(timestamp)
794               
795                if local_version:
796                        date = time.strftime('%x', localtime)
797                        localtime = time.strftime('%X', localtime)
798               
799                elif truncate_time_zone:
800                        date = time.strftime('%Y-%m-%d', localtime)
801                        localtime = time.strftime('%H:%M:%S', localtime)
802                        localtime = '%s.%s' % (localtime, decimal[:3])
803               
804                else:
805                        date = time.strftime('%Y-%m-%d', localtime)
806                        localtime = time.strftime('%H:%M:%S', localtime)
807                        localtime = '%s.%s %s' % (localtime, decimal, \
808                                       time.strftime('%Z', time.localtime(timestamp)))
809               
810               
811                return(date, localtime)
812       
813       
814        ##################################################################
815       
816        def saveData(self):
817               
818                if self == None:
819                        target = self
820                else:
821                        target = self.parent
822               
823                data = self.collectData()
824               
825                (date, localtime) = self.parseTimeStamp(time.time())
826               
827                default_filename = '%s %s.synapse' % (date, \
828                                      target.lineEditSessionProfile.text())
829                                     
830                default_filename = os.path.join(self.homepath, default_filename)
831               
832                output_file = QtGui.QFileDialog.getSaveFileName(parent=target, \
833                                 caption="Save Session Data to File", \
834                                 dir=default_filename, \
835                                 filter="Puzzlebox Synapse Data File (*.synapse)")
836               
837                try:
838                        output_file = output_file[0]
839                except:
840                        pass
841               
842                file = open(str(output_file), 'w')
843                pickle.dump(data, file)
844                file.close()
845       
846       
847        ##################################################################
848       
849        def exportData(self):
850               
851                if self.parent == None:
852                        target = self
853                else:
854                        target = self.parent
855               
856                (date, localtime) = self.parseTimeStamp(time.time())
857               
858                default_filename = '%s %s.csv' % (date, \
859                                      target.lineEditSessionProfile.text())
860               
861                default_filename = os.path.join(self.homepath, default_filename)
862               
863                output_file = QtGui.QFileDialog.getSaveFileName(parent=target, \
864                                 caption="Export Session Data to File", \
865                                 dir=default_filename, \
866                                 filter="CSV File (*.csv);;Text File (*.txt)")
867               
868                try:
869                        output_file = output_file[0]
870                except:
871                        pass
872               
873                if str(output_file).endswith('.csv'):
874                       
875                        outputData = self.exportDataToCSV()
876               
877               
878                else:
879                       
880                        try:
881                                outputData = self.textEditDebugConsole.toPlainText()
882                        except:
883                                outputData = self.exportDataToCSV()
884                       
885                       
886                file = open(str(output_file), 'w')
887                file.write(outputData)
888                file.close()
889       
890       
891        ##################################################################
892       
893        def exportDataToCSV(self):
894               
895                if self == None:
896                        target = self
897                else:
898                        target = self.parent
899               
900                try:
901                        truncate_csv_timezone = target.configuration.EXPORT_CSV_TRUNCATE_TIMEZONE
902                except:
903                        truncate_csv_timezone = False
904               
905                try:
906                        scrub_data = target.configuration.EXPORT_CSV_SCRUB_DATA
907                except:
908                        scrub_data = False
909               
910               
911                headers = 'Date,Time,Delta,Theta,Low Alpha,High Alpha,Low Beta,High Beta,Low Gamma,Mid Gamma,Attention,Meditation,Signal Level'
912               
913                customDataHeaders = []
914                for header in target.customDataHeaders:
915                        customDataHeaders.append(header)
916                for plugin in target.activePlugins:
917                        for header in plugin.customDataHeaders:
918                                customDataHeaders.append(header)
919               
920                for each in customDataHeaders:
921                        headers = headers + ',%s' % each
922               
923                headers = headers + '\n'
924               
925                csv = {}
926               
927                for packet in target.packets['signals']:
928                       
929                        if 'rawEeg' in packet.keys():
930                                continue
931                       
932                        if packet['timestamp'] not in csv.keys():
933                               
934                                #print packet
935                                timestamp = packet['timestamp']
936                                (date, localtime) = self.parseTimeStamp(timestamp, \
937                                                    truncate_time_zone=truncate_csv_timezone)
938                               
939                                csv[timestamp] = {}
940                                csv[timestamp]['Date'] = date
941                                csv[timestamp]['Time'] = localtime
942                                csv[timestamp]['Delta'] = ''
943                                csv[timestamp]['Theta'] = ''
944                                csv[timestamp]['Low Alpha'] = ''
945                                csv[timestamp]['High Alpha'] = ''
946                                csv[timestamp]['Low Beta'] = ''
947                                csv[timestamp]['High Beta'] = ''
948                                csv[timestamp]['Low Gamma'] = ''
949                                csv[timestamp]['Mid Gamma'] = ''
950                                csv[timestamp]['Attention'] = ''
951                                csv[timestamp]['Meditation'] = ''
952                                csv[timestamp]['Signal Level'] = ''
953                               
954                                for header in customDataHeaders:
955                                        csv[timestamp][header] = ''
956                       
957                       
958                        if 'eSense' in packet.keys():
959                                if 'attention' in packet['eSense'].keys():
960                                        csv[timestamp]['Attention'] = packet['eSense']['attention']
961                                if 'meditation' in packet['eSense'].keys():
962                                        csv[timestamp]['Meditation'] = packet['eSense']['meditation']
963                       
964                        if 'eegPower' in packet.keys():
965                                if 'delta' in packet['eegPower'].keys():
966                                        csv[timestamp]['Delta'] = packet['eegPower']['delta']
967                                if 'theta' in packet['eegPower'].keys():
968                                        csv[timestamp]['Theta'] = packet['eegPower']['theta']
969                                if 'lowAlpha' in packet['eegPower'].keys():
970                                        csv[timestamp]['Low Alpha'] = packet['eegPower']['lowAlpha']
971                                if 'highAlpha' in packet['eegPower'].keys():
972                                        csv[timestamp]['High Alpha'] = packet['eegPower']['highAlpha']
973                                if 'lowBeta' in packet['eegPower'].keys():
974                                        csv[timestamp]['Low Beta'] = packet['eegPower']['lowBeta']
975                                if 'highBeta' in packet['eegPower'].keys():
976                                        csv[timestamp]['High Beta'] = packet['eegPower']['highBeta']
977                                if 'lowGamma' in packet['eegPower'].keys():
978                                        csv[timestamp]['Low Gamma'] = packet['eegPower']['lowGamma']
979                                if 'highGamma' in packet['eegPower'].keys():
980                                        csv[timestamp]['Mid Gamma'] = packet['eegPower']['highGamma']
981                       
982                        if 'poorSignalLevel' in packet.keys():
983                                csv[timestamp]['Signal Level'] = packet['poorSignalLevel']
984                       
985                        for header in customDataHeaders:
986                                if 'custom' in packet.keys() and \
987                                   header in packet['custom'].keys():
988                                        csv[timestamp][header] = packet['custom'][header]
989               
990               
991                if scrub_data:
992                        csv = self.scrubData(csv, truncate_csv_timezone)
993               
994               
995                output = headers
996               
997                csv_keys = csv.keys()
998                csv_keys.sort()
999               
1000                for key in csv_keys:
1001                       
1002                        row = '%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s' % \
1003                              (csv[key]['Date'], \
1004                               csv[key]['Time'], \
1005                               csv[key]['Delta'], \
1006                               csv[key]['Theta'], \
1007                               csv[key]['Low Alpha'], \
1008                               csv[key]['High Alpha'], \
1009                               csv[key]['Low Beta'], \
1010                               csv[key]['High Beta'], \
1011                               csv[key]['Low Gamma'], \
1012                               csv[key]['Mid Gamma'], \
1013                               csv[key]['Attention'], \
1014                               csv[key]['Meditation'], \
1015                               csv[key]['Signal Level'])
1016                       
1017                        for header in customDataHeaders:
1018                                row = row + ',%s' % csv[key][header]
1019                       
1020                        row = row + '\n'
1021                       
1022                        output = output + row
1023               
1024               
1025                return(output)
1026       
1027       
1028        ##################################################################
1029       
1030        def scrubData(self, csv, truncate_csv_timezone=False):
1031               
1032                # If there are missing packets, repeat a given packet once per missing
1033                # second until there is a gap between 1 and 2 seconds, in which case
1034                # produce a final duplicate packet at the mid-point between the packets
1035
1036                if self.DEBUG:
1037                        print "INFO: Scrubbing Data"
1038               
1039                last_time = None
1040                last_recorded_time = None
1041               
1042                output = {}
1043               
1044                csv_keys = csv.keys()
1045                csv_keys.sort()
1046               
1047                for key in csv_keys:
1048                       
1049                        timestamp = key
1050
1051                        if csv[key]['Attention'] == '':
1052                                continue
1053                       
1054                        if last_time == None:
1055                                # First entry in log
1056                                last_time = timestamp
1057                                last_recorded_time = timestamp
1058                                output[key] = csv[key]
1059                                continue
1060                       
1061                        else:
1062                               
1063                                #time_difference = timestamp - last_time
1064                                time_difference = timestamp - last_recorded_time
1065                               
1066                                if (time_difference <= 1) and \
1067                                   (time_difference >= PACKET_MINIMUM_TIME_DIFFERENCE_THRESHOLD):
1068                                        # Skip packets within the correct time threshold
1069                                        last_time = timestamp
1070                                        last_recorded_time = timestamp
1071                                        output[key] = csv[key]
1072                                        continue
1073                               
1074                                else:
1075
1076                                        if self.DEBUG > 1:
1077                                                print "time_difference:",
1078                                                print time_difference
1079                                                print "timestamp:",
1080                                                print self.parseTimeStamp(timestamp)[-1].split(' ')[0]
1081                                                print "last_time:",
1082                                                print self.parseTimeStamp(last_time)[-1].split(' ')[0]
1083                                                print "last_recorded_time:",
1084                                                print self.parseTimeStamp(last_recorded_time)[-1].split(' ')[0]
1085
1086                                       
1087                                        new_packet = csv[key].copy()
1088                                       
1089                                        if time_difference >= 2:
1090                                               
1091                                                ##new_time = last_time + 1
1092                                                #new_time = last_recorded_time + 1
1093
1094                                                count = int(time_difference)
1095                                                while count >= 1:
1096                                                        new_packet = csv[key].copy()
1097                                                        new_time = last_recorded_time + 1
1098                                                        (date, formatted_new_time) = self.parseTimeStamp(new_time, \
1099                                                         truncate_time_zone=truncate_csv_timezone)
1100                                                        new_packet['Time'] = formatted_new_time
1101                                                        last_recorded_time = new_time
1102                                                        last_time = timestamp
1103                                                        output[new_time] = new_packet
1104                                                        count = count - 1
1105                                                continue
1106                                               
1107                                        elif time_difference < PACKET_MINIMUM_TIME_DIFFERENCE_THRESHOLD:
1108                                                # Spread out "bunched up" packets
1109                                                #new_time = last_time + 1
1110                                                new_time = last_recorded_time + 1
1111                                       
1112                                       
1113                                        elif (time_difference < 2) and (time_difference > 1):
1114                                               
1115                                                #new_time = last_time + ((last_time - timestamp) / 2)
1116                                                #new_time = last_recorded_time + ((last_recorded_time - timestamp) / 2)
1117                                                #new_time = last_time + 1
1118                                                new_time = last_recorded_time + 1
1119                                       
1120                                       
1121                                        (date, formatted_new_time) = self.parseTimeStamp(new_time, \
1122                                           truncate_time_zone=truncate_csv_timezone)
1123                                       
1124                                        new_packet['Time'] = formatted_new_time
1125                                       
1126                                        #last_time = new_time
1127                                        last_recorded_time = new_time
1128                                        last_time = timestamp
1129                                        output[new_time] = new_packet
1130                                       
1131                                        if self.DEBUG > 1:
1132                                                print "WARN: Scrubbing new packet:",
1133                                                print new_packet
1134                                                print
1135               
1136               
1137                return(output)
1138       
1139       
1140        ##################################################################
1141
1142        #def processTimeDifference(self,
1143
1144        ##################################################################
1145       
1146        def resetData(self):
1147               
1148                if self == None:
1149                        target = self
1150                else:
1151                        target = self.parent
1152               
1153                target.packets['rawEeg'] = []
1154                target.packets['signals'] = []
1155               
1156                target.thinkGearConnectServer.protocol.session_start_timestamp = \
1157                        time.time()
1158               
1159                target.thinkGearConnectServer.protocol.packet_count = 0
1160                target.thinkGearConnectServer.protocol.bad_packets = 0
1161               
1162                self.updateProfileSessionStatus()
1163               
1164                try:
1165                        target.textEditDebugConsole.setText("")
1166                except:
1167                        pass
1168       
1169       
1170        #####################################################################
1171       
1172        def convert_seconds_to_datetime(self, duration):
1173               
1174                duration_hours = duration / (60 * 60)
1175                duration_minutes = (duration - (duration_hours * (60 * 60))) / 60
1176                duration_seconds = (duration - (duration_hours * (60 * 60)) - (duration_minutes * 60))
1177               
1178                duration_hours = '%i' % duration_hours
1179                if (len(duration_hours) == 1):
1180                        duration_hours = "0%s" % duration_hours
1181               
1182                duration_minutes = '%i' % duration_minutes
1183                if (len(duration_minutes) == 1):
1184                        duration_minutes = "0%s" % duration_minutes
1185               
1186                duration_seconds = '%i' % duration_seconds
1187                if (len(duration_seconds) == 1):
1188                        duration_seconds = "0%s" % duration_seconds
1189               
1190                datetime = '%s:%s:%s' % (duration_hours, duration_minutes, duration_seconds)
1191               
1192                return(datetime)
1193       
1194       
1195        ##################################################################
1196       
1197        def stop(self):
1198               
1199                if UPDATE_INTERFACE_VIA_TIMER:
1200                        self.updateInterfaceTimer.stop()
1201                else:
1202                        if self.thinkgearConnectClient != None:
1203                                self.thinkgearConnectClient.disconnectFromHost()
1204               
1205                if self.thinkGearConnectServer != None:
1206                        self.thinkGearConnectServer.exitThread()
1207       
1208       
1209        ##################################################################
1210       
1211        def closeEvent(self, event):
1212               
1213                quit_message = "Are you sure you want to exit the program?"
1214               
1215                reply = QtGui.QMessageBox.question( \
1216                           self, \
1217                          'Message', \
1218                           quit_message, \
1219                           QtGui.QMessageBox.Yes, \
1220                           QtGui.QMessageBox.No)
1221               
1222                if reply == QtGui.QMessageBox.Yes:
1223                       
1224                        self.stop()
1225                       
1226                        event.accept()
1227               
1228                else:
1229                        event.ignore()
Note: See TracBrowser for help on using the repository browser.