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

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

Interface:

  • device_model support for handling NeuroSky? MindWave? auto-connect packets
  • 'Emit Signal' mode for Server communication support
  • fix for selecting correct default user path

Server:

  • device_model support for handling NeuroSky? MindWave? auto-connect packets
  • 'Emit Signal' mode for Server communication support

Protocol:

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