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

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