source: trunk/Puzzlebox/Synapse/ThinkGear/Protocol.py @ 411

Last change on this file since 411 was 411, checked in by sc, 3 years ago
  • exportTrainingDataToCSV() added
  • collectProcessedData() added
  • Training debug output added to Protocol
File size: 37.7 KB
Line 
1# -*- coding: utf-8 -*-
2
3# Copyright Puzzlebox Productions, LLC (2010-2012)
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__changelog__ = """
9Last Update: 2012.05.06
10"""
11
12__todo__ = """
13
14 - When a "MindSet" is configured as "MindWave" hardware:
15   File "/home/sc/development/jigsaw/trunk/Puzzlebox/Synapse/Protocol.py", line 868, in run
16   self.device.device.write('\xc1')
17
18 - needs to handle:
19   serial.serialutil.SerialException:
20   could not open port /dev/rfcomm0:
21   [Errno 16] Device or resource busy: '/dev/rfcomm0'
22"""
23
24__doc__ = """\
25Puzzlebox.Synapse.Protocol
26
27usage:
28  from Puzzlebox.Synapse import Protocol
29
30Modules:
31  Protocol.ProtocolHandler()
32  Protocol.SerialDevice()
33
34SPEC:
35
36 CODE Definitions Table
37 Single-Byte CODEs
38 Extended             (Byte)
39 Code Level   [CODE] [LENGTH] Data Value Meaning
40 ----------   ------ -------- ------------------
41           0    0x02        - POOR_SIGNAL Quality (0-255)
42           0    0x04        - ATTENTION eSense (0 to 100)
43           0    0x05        - MEDITATION eSense (0 to 100)
44           0    0x16        - Blink Strength. (0-255) Sent only
45                              when Blink event occurs.
46 Multi-Byte CODEs
47 Extended             (Byte)
48 Code Level   [CODE] [LENGTH] Data Value Meaning
49 ----------   ------ -------- ------------------
50           0    0x80        2 RAW Wave Value: a single big-endian
51                                16-bit two's-compliment signed value
52                                (high-order byte followed by
53                                low-order byte) (-32768 to 32767)
54           0    0x83       24 ASIC_EEG_POWER: eight big-endian
55                                3-byte unsigned integer values
56                                representing delta, theta, low-alpha
57                                high-alpha, low-beta, high-beta,
58                                low-gamma, and mid-gamma EEG band
59                                power values
60         Any    0x55        - NEVER USED (reserved for [EXCODE])
61         Any    0xAA        - NEVER USED (reserved for [SYNC])
62
63MindWave Connection CODEs
64[CODE] [DATA]    Data Value Meaning
65------ --------- ------------
66 0xC0  0xAB 0xCD Connect to headset with ID "ABCD"
67 0xC1          - Disconnect
68 0xC2          - Connect to first available headset
69
70MindWave Response CODEs
71Extended             (Byte)
72Code Level   [CODE] [LENGTH] Data Value Meaning
73----------   ------ -------- ------------------
74         0    0xD0        3 Headset Connect Success
75         0    0xD1        2 Headset Not Found
76         0    0xD2        3 Headset Disconnected
77         0    0xD3        0 Request Denied
78         0    0xD4        1 Standby/Scan Mode
79
80Linux Bluetooth serial protocol profile example:
81    rfcomm connect rfcomm0 00:13:EF:00:1B:FE 3
82"""
83
84### IMPORTS ###
85import sys, time
86import signal
87import serial
88import copy
89
90if ((sys.platform != 'win32') and \
91    (sys.platform != 'darwin')):
92        import bluetooth
93
94
95import Puzzlebox.Synapse.Configuration as configuration
96
97if configuration.ENABLE_PYSIDE:
98        try:
99                import PySide as PyQt4
100                from PySide import QtCore
101        except Exception, e:
102                print "ERROR: Exception importing PySide:",
103                print e
104                configuration.ENABLE_PYSIDE = False
105        else:
106                print "INFO: [Synapse:ThinkGear:Protocol] Using PySide module"
107
108if not configuration.ENABLE_PYSIDE:
109        print "INFO: [Synapse:ThinkGear:Protocol] Using PyQt4 module"
110        from PyQt4 import QtCore
111
112
113#####################################################################
114# Globals
115#####################################################################
116
117DEBUG = configuration.DEBUG
118
119THINKGEAR_DEVICE_SERIAL_PORT = configuration.THINKGEAR_DEVICE_SERIAL_PORT
120#THINKGEAR_DEVICE_BLUETOOTH_ADDRESS = \
121        #configuration.THINKGEAR_DEVICE_BLUETOOTH_ADDRESS
122
123#DEFAULT_SERIAL_BAUDRATE = 57600
124DEFAULT_SERIAL_BAUDRATE = 115200
125
126#THINKGEAR_DEVICE_BLUETOOTH_ADDRESS = '00:13:EF:00:1B:FE'
127THINKGEAR_DEVICE_BLUETOOTH_CHANNEL = 3
128
129THINKGEAR_DEVICE_AUTOCONNECT_INTERVAL = 4 # seconds between attempting
130                                          # to send auto-connect packets
131#THINKGEAR_DEVICE_ID = configuration.THINKGEAR_DEVICE_ID
132#THINKGEAR_DEFAULT_DEVICE_ID = '\x7d\x68'
133#THINKGEAR_DEFAULT_DEVICE_ID = '\xe4\x68'
134
135PROTOCOL_SYNC = '\xAA'
136PROTOCOL_EXCODE = '\x55'
137
138EEG_POWER_BAND_ORDER = configuration.THINKGEAR_EEG_POWER_BAND_ORDER
139
140DEVICE_BUFFER_CHECK_TIMER = 60 * 1000 # Check buffer size once every minute
141DEVICE_READ_BUFFER_CHECK_TIMER = 10 * 1000 # Check buffer size once x seconds
142DEVICE_BUFFER_MAX_SIZE = 180 # Reset buffer if it grow this large
143                             # as this would indicate the processing
144                             # algorithm is not keeping up with the device
145                             # According to protocol specification,
146                             # "...a complete, valid Packet is ... a maximum
147                             # of 173 bytes long (possible if the Data Payload
148                             # is the maximum 169 bytes long)."
149                             # Therefore we reset if our buffer has grown longer
150                             # than the maximum packet length as this means
151                             # the processing algorthim is at least one full
152                             # packet behind.
153
154DEBUG_BYTE_COUNT = 819200
155DEBUG_PACKET_COUNT = 1024
156
157
158#CURRENT_SIGNAL = 0
159#CURRENT_ATTENTION = 0
160#CURRENT_MEDITATION = 0
161
162#####################################################################
163# Classes
164#####################################################################
165
166class puzzlebox_synapse_protocol_thinkgear(QtCore.QThread):
167       
168        def __init__(self, log, \
169                               serial_device, \
170                               device_model=None, \
171                               DEBUG=DEBUG, \
172                               parent=None):
173               
174                QtCore.QThread.__init__(self,parent)
175               
176                self.log = log
177                self.DEBUG = DEBUG
178                self.parent = parent
179               
180                #self.device_id = device_id
181                self.device_model = device_model
182               
183                self.device = None
184                self.buffer = ''
185                self.payload_timestamp = time.time()
186               
187                self.device = serial_device
188                self.auto_connect_timestamp = time.time()
189               
190                self.data_packet = {}
191                self.data_packet['eegPower'] = {}
192                self.data_packet['eSense'] = {}
193               
194                self.current_signal = 200
195                self.current_attention = 0
196                self.current_meditaiton = 0
197                self.detection_threshold = 70
198                self.current_detection = 0
199               
200                #self.packet_count = 0
201                #self.bad_packets = 0
202                #self.session_start_timestamp = time.time()
203               
204                self.keep_running = True
205       
206       
207        ##################################################################
208       
209        def communicateWithHandsfreeProfile(self):
210               
211                #"AT+CKPD=200" - Indicates a Bluetooth button press
212                #"AT+VGM=" - Indicates a microphone volume change
213                #"AT+VGS=" - Indicates a speakerphone volume change
214                #"AT+BRSF=" - The Headset is asking what features are supported
215                #"AT+CIND?" - The Headset is asking about the indicators that are signaled
216                #"AT+CIND=?" - The Headset is asking about the test indicators
217                #"AT+CMER=" - The Headset is asking which indicates are registered for updates
218                #"ATA" - When an incoming call has been answered, usually a Bluetooth button press
219                #"AT+CHUP" - When a call has been hung up, usually a Bluetooth button press
220                #"ATD>" - The Headset is requesting the local device to perform a memory dial
221                #"ATD" - The Headset is requesting to dial the number
222                #"AT+BLDN" - The Headset is requesting to perform last number dialed
223                #"AT+CCWA=" - The Headset has enabled call waiting
224                #"AT+CLIP=" - The Headset has enabled CLI (Calling Line Identification)
225                #"AT+VTS=" - The Headset is asking to send DTMF digits
226                #"AT+CHLD=" - The Headset is asking to put the call on Hold
227                #"AT+BVRA=" - The Headset is requesting voice recognition
228                #"ATH" - Call hang-up
229               
230                #self.device.write('\x29')
231                #self.device.write('AT+BRSF=24\r\n')
232               
233                buffer = ''
234               
235                while True:
236                        reply = self.device.read()
237                       
238                        if (len(reply) != 0):
239                                if DEBUG > 1:
240                                        print reply
241                                buffer += reply
242                       
243                        if buffer == "AT+BRSF=24\r":
244                                print "--> Received:",
245                                print buffer
246                                response = '\r\nOK\r\n'
247                                print "<-- Sending:",
248                                print response.replace('\r\n', '')
249                                self.device.write(response)
250                                buffer = ''
251                       
252                        elif buffer == 'AT+CIND=?\r':
253                                print "--> Received:",
254                                print buffer
255                                # first field indicates that we have cellular service [0-1]
256                                # second field indicates that we're in a call (0 for false) [0-1]
257                                # third field indicates the current call setup (0 for idle) [0-3]
258                                response = '\r\n+CIND: 1,0,0\r\n'
259                                print "<-- Sending:",
260                                print response.replace('\r\n', '')
261                                self.device.write(response)
262                                response = '\r\nOK\r\n'
263                                print "<-- Sending:",
264                                print response.replace('\r\n', '')
265                                self.device.write(response)
266                                buffer = ''
267                       
268                        elif buffer == 'AT+CMER=3, 0, 0, 1\r':
269                                print "--> Received:",
270                                print buffer
271                                response = '\r\nOK\r\n'
272                                print "<-- Sending:",
273                                print response.replace('\r\n', '')
274                                self.device.write(response)
275                                response = '\r\n+CIEV:2,1\r\n'
276                                print "<-- Sending:",
277                                print response.replace('\r\n', '')
278                                self.device.write(response)
279                                response = '\r\n+CIEV:3,0\r\n'
280                                print "<-- Sending:",
281                                print response.replace('\r\n', '')
282                                self.device.write(response)
283                                buffer = ''
284                       
285                        elif buffer == 'AT+VGS=15\r':
286                                print "--> Received:",
287                                print buffer
288                                response = '\r\nOK\r\n'
289                                print "<-- Sending:",
290                                print response.replace('\r\n', '')
291                                self.device.write(response)
292                                buffer = ''
293                       
294                        elif buffer == 'AT+VGM=08\r':
295                                print "--> Received:",
296                                print buffer
297                                response = '\r\nOK\r\n'
298                                print "<-- Sending:",
299                                print response.replace('\r\n', '')
300                                self.device.write(response)
301                                buffer = ''
302                               
303                               
304                                self.exitThread()
305                                #self.keep_running = False
306                                #self.device.stop()
307                                #QtCore.QThread.quit(self)
308                                #sys.exit()
309       
310       
311        ##################################################################
312       
313        def hexStringEndianSwap(self, theString):
314                """Rearranges character-couples in a little endian hex string to
315                convert it into a big endian hex string and vice-versa. i.e. 'A3F2'
316                is converted to 'F2A3'
317               
318                @param theString: The string to swap character-couples in
319                @return: A hex string with swapped character-couples. -1 on error.
320               
321                Taken from http://bytes.com/topic/python/answers/652429-convert-little-endian-hex-string-number#post2588668"""
322               
323                # We can't swap character couples in a string that has an odd number
324                # of characters.
325                if len(theString)%2 != 0:
326                        return -1
327               
328                # Swap the couples
329                swapList = []
330                for i in range(0, len(theString), 2):
331                        swapList.insert(0, theString[i:i+2])
332               
333                # Combine everything into one string. Don't use a delimeter.
334                return ''.join(swapList)
335       
336       
337        ##################################################################
338       
339        def processRawEEGValue(self, data_values):
340               
341                '''SPEC: This Data Value consists of two bytes, and represents a
342                single raw wave sample. Its value is a signed 16-bit integer that
343                ranges from -32768 to 32767. The first byte of the Value represents
344                the high-order bits of the twos-compliment value, while the second
345                byte represents the low-order bits. To reconstruct the full raw
346                wave value, simply shift the rst byte left by 8 bits, and
347                bitwise-or with the second byte:
348               
349                short raw = (Value[0]<<8) | Value[1];
350               
351                where Value[0] is the high-order byte, and Value[1] is the
352                low-order byte. In systems or languages where bit operations are
353                inconvenient, the following arithmetic operations may be
354                substituted instead:
355               
356                raw = Value[0]*256 + Value[1];
357                if( raw >= 32768 ) raw = raw - 65536;
358               
359                where raw is of any signed number type in the language that can
360                represent all the numbers from -32768 to 32767.
361               
362                Each ThinkGear model reports its raw wave information in only
363                certain areas of the full -32768 to 32767 range. For example,
364                MindSet reports raw waves that fall between approximately -2048 to
365                2047. By default, output of this Data Value is enabled, and is
366                outputed 512 times a second, or approximately once every 2ms.'''
367               
368                high_order = data_values[0:2]
369                low_order = data_values[2:4]
370               
371                #high_order = high_order.encode("hex")
372                high_order = int(high_order, 16)
373               
374                #low_order = low_order.encode("hex")
375                low_order = int(low_order, 16)
376
377                raw = high_order * 256 + low_order
378               
379                if (raw >= 32768):
380                        raw = raw - 65536
381               
382               
383               
384                #print "%s,%s,%s,%s,%s" % (time.time(),
385                        #CURRENT_SIGNAL,
386                        #CURRENT_ATTENTION,
387                        #CURRENT_MEDITATION,
388                        #raw)
389               
390                if (self.current_signal == 0):
391                        print "%s,%i,%i,%i,%s,%i" % (time.time(),
392                                                self.current_signal,
393                                                self.current_attention,
394                                                self.current_meditaiton,
395                                                raw,
396                                                self.current_detection)
397               
398               
399                return (raw)
400       
401       
402        ##################################################################
403       
404        def processAsicEegPower(self, data_values):
405               
406                '''SPEC: This Data Value represents the current magnitude of 8
407                commonly-recognized types of EEG (brain-waves). This Data Value
408                is output as a series of eight 3-byte unsigned integers in
409                little-endian format.
410                The eight EEG powers are output in the following order:
411                delta (0.5 - 2.75Hz),
412                theta (3.5 - 6.75Hz),
413                low-alpha (7.5 - 9.25Hz),
414                high-alpha (10 - 11.75Hz),
415                low-beta (13 - 16.75Hz),
416                high-beta (18 - 29.75Hz),
417                low-gamma (31 - 39.75Hz), and
418                mid-gamma (41 - 49.75Hz).
419                These values have no units and therefore are only meaningful compared
420                to each other and to themselves, to consider relative quantity and
421                temporal uctuations. By default, output of this Data Value is enabled,
422                and is typically output once a second.'''
423               
424                eegPower = {}
425               
426                eegPower['delta'] = data_values[0:6]
427                eegPower['theta'] = data_values[6:12]
428                eegPower['lowAlpha'] = data_values[12:18]
429                eegPower['highAlpha'] = data_values[18:24]
430                eegPower['lowBeta'] = data_values[24:30]
431                eegPower['highBeta'] = data_values[30:36]
432                eegPower['lowGamma'] = data_values[36:42]
433                eegPower['highGamma'] = data_values[42:48]
434               
435                for key in eegPower.keys():
436                        eegPower[key] = self.hexStringEndianSwap(eegPower[key])
437                        #eegPower[key] = eegPower[key].encode("hex")
438                        eegPower[key] = int(eegPower[key], 16)
439               
440               
441                return(eegPower)
442       
443       
444        ##################################################################
445       
446        def processDataRow(self, \
447                           extended_code_level, \
448                           code, \
449                           length, \
450                           data_values, \
451                           timestamp):
452               
453                '''CODE Definitions Table
454                   Single-Byte CODEs
455                   Extended             (Byte)
456                   Code Level   [CODE] [LENGTH] Data Value Meaning
457                   ----------   ------ -------- ------------------
458                             0    0x02        - POOR_SIGNAL Quality (0-255)
459                             0    0x04        - ATTENTION eSense (0 to 100)
460                             0    0x05        - MEDITATION eSense (0 to 100)
461                             0    0x16        - Blink Strength. (0-255) Sent only
462                                                when Blink event occurs.
463                   Multi-Byte CODEs
464                   Extended             (Byte)
465                   Code Level   [CODE] [LENGTH] Data Value Meaning
466                   ----------   ------ -------- ------------------
467                             0    0x80        2 RAW Wave Value: a single big-endian
468                                                  16-bit two's-compliment signed value
469                                                  (high-order byte followed by
470                                                  low-order byte) (-32768 to 32767)
471                             0    0x83       24 ASIC_EEG_POWER: eight big-endian
472                                                  3-byte unsigned integer values
473                                                  representing delta, theta, low-alpha
474                                                  high-alpha, low-beta, high-beta,
475                                                  low-gamma, and mid-gamma EEG band
476                                                  power values
477                           Any    0x55        - NEVER USED (reserved for [EXCODE])
478                           Any    0xAA        - NEVER USED (reserved for [SYNC])
479               
480                   MindWave CODEs
481                   Extended             (Byte)
482                   Code Level   [CODE] [LENGTH] Data Value Meaning
483                   ----------   ------ -------- ------------------
484                             0    0xD0        3 Headset Connect Success
485                             0    0xD1        2 Headset Not Found
486                             0    0xD2        3 Headset Disconnected
487                             0    0xD3        0 Request Denied
488                             0    0xD4        1 Standby/Scan Mode'''
489               
490                packet_update = {}
491               
492                packet_update['timestamp'] = timestamp
493               
494                #self.packet_count += 1
495                #self.parent.plugin_session.packet_count += 1
496                self.parent.incrementPacketCount()
497               
498                if extended_code_level == 0:
499                       
500                        if code == '02':
501                                poor_signal_quality = int(data_values, 16)
502                                if self.DEBUG > 1:
503                                        print # Empty line at the beginning of most packets
504                                        print "poorSignalLevel:",
505                                        print poor_signal_quality
506                               
507                                self.current_signal = copy.copy(poor_signal_quality)
508                               
509                                packet_update['poorSignalLevel'] = poor_signal_quality
510                               
511                                self.current_signal = poor_signal_quality
512                       
513                       
514                        elif code == '04':
515                                attention = int(data_values, 16)
516                                if self.DEBUG > 1:
517                                        print "attention:",
518                                        print attention
519                               
520                                self.current_attention = copy.copy(attention)
521                               
522                                if (attention > self.detection_threshold):
523                                        self.current_detection = 1
524                                else:
525                                        self.current_detection = 0
526                               
527                                packet_update['eSense'] = {}
528                                packet_update['eSense']['attention'] = attention
529                       
530                       
531                        elif code == '05':
532                                meditation = int(data_values, 16)
533                                if self.DEBUG > 1:
534                                        print "meditation:",
535                                        print meditation
536                               
537                                self.current_meditaiton = copy.copy(meditation)
538                               
539                                packet_update['eSense'] = {}
540                                packet_update['eSense']['meditation'] = meditation
541                       
542                       
543                        elif code == '16':
544                                blink_strength = int(data_values, 16)
545                                if self.DEBUG > 1:
546                                        print "blinkStrength:",
547                                        print blink_strength
548                               
549                                packet_update['blinkStrength'] = blink_strength
550                       
551                       
552                        elif code == '80':
553                                #self.packet_count -= 1 # We don't count raw EEG packets for Interface
554                                raw_wave_value = data_values
555                                if self.DEBUG > 3:
556                                        print "Raw EEG:",
557                                        print raw_wave_value
558                                raw_eeg_value = self.processRawEEGValue(data_values)
559                                if self.DEBUG > 2:
560                                        print "Raw EEG Value:",
561                                        print raw_eeg_value
562                               
563                                packet_update['rawEeg'] = raw_eeg_value
564                               
565                                #print "%s,%i,%i,%i,%s" % (time.time(),
566                                                                                #self.current_signal,
567                                                                                #self.current_attention,
568                                                                                #self.current_meditaiton,
569                                                                                #raw_eeg_value)
570                       
571                       
572                        elif code == '83':
573                                asic_eeg_power = data_values
574                                if self.DEBUG > 2:
575                                        print "ASIC_EEG_POWER:",
576                                        print asic_eeg_power
577                                eegPower = self.processAsicEegPower(data_values)
578                                if self.DEBUG > 1:
579                                        for key in EEG_POWER_BAND_ORDER:
580                                                print "%s: %i" % (key, eegPower[key])
581                               
582                                packet_update['eegPower'] = {}
583                                for key in eegPower.keys():
584                                        packet_update['eegPower'][key] = eegPower[key]
585                       
586                       
587                        elif code == 'd0':
588                                if self.DEBUG:
589                                        print "INFO: ThinkGear Headset Connect Success"
590                                #self.session_start_timestamp = time.time()
591                                self.parent.resetSessionStartTime()
592                                #print "okay"
593                                #self.packet_count = 0
594                                #self.parent.plugin_session.packet_count = 0
595                                self.parent.setPacketCount(0)
596                                #self.bad_packets = 0
597                                #self.parent.plugin_session.bad_packets = 0
598                                self.parent.setBadPackets(0)
599                       
600                       
601                        elif code == 'd1':
602                                current_time = time.time()
603                                if current_time - self.auto_connect_timestamp > \
604                                        THINKGEAR_DEVICE_AUTOCONNECT_INTERVAL:
605                                        if self.DEBUG:
606                                                print "INFO: ThinkGear device not found. Writing auto-connect packet."
607                                        self.auto_connect_timestamp = current_time
608                                        self.device.device.write('\xc2')
609                                        #self.device.device.write('\xc0\xe4\x68')
610                       
611                       
612                        elif code == 'd2':
613                                current_time = time.time()
614                                if current_time - self.auto_connect_timestamp > \
615                                        THINKGEAR_DEVICE_AUTOCONNECT_INTERVAL:
616                                        if self.DEBUG:
617                                                print "INFO: ThinkGear device disconnected. Writing auto-connect packet."
618                                        self.auto_connect_timestamp = current_time
619                                        self.device.device.write('\xc2')
620                                        #self.device.device.write('\xc0\xe4\x68')
621                       
622                       
623                        elif code == 'd3':
624                                current_time = time.time()
625                                if current_time - self.auto_connect_timestamp > \
626                                        THINKGEAR_DEVICE_AUTOCONNECT_INTERVAL:
627                                        if self.DEBUG:
628                                                print "INFO: ThinkGear device request denied. Writing auto-connect packet."
629                                        self.auto_connect_timestamp = current_time
630                                        self.device.device.write('\xc2')
631                                        #self.device.device.write('\xc0\xe4\x68')
632                       
633                       
634                        elif code == 'd4':
635                                current_time = time.time()
636                                if current_time - self.auto_connect_timestamp > \
637                                        THINKGEAR_DEVICE_AUTOCONNECT_INTERVAL:
638                                        if self.DEBUG:
639                                                print "INFO: ThinkGear device in standby/scan mode. Writing auto-connect packet."
640                                        self.auto_connect_timestamp = current_time
641                                        self.device.device.write('\xc2')
642                                        #self.device.device.write('\xc0\xe4\x68')
643                       
644                       
645                        else:
646                                #self.bad_packets += 1
647                                #self.parent.plugin_session.bad_packets += 1
648                                self.parent.incrementBadPackets()
649                                if self.DEBUG:
650                                        print "ERROR: data payload row code not matched:",
651                                        print code
652               
653               
654                return(packet_update)
655       
656       
657        ##################################################################
658       
659        def processDataPayload(self, data_payload, timestamp):
660               
661                '''A DataRow consists of bytes in the following format:
662                ([EXCODE]...) [CODE] ([VLENGTH])   [VALUE...]
663                ____________________ ____________ ___________
664                ^^^^(Value Type)^^^^ ^^(length)^^ ^^(value)^^'''
665               
666               
667                if self.DEBUG > 3:
668                        print "data payload:",
669                        for byte in data_payload:
670                                print byte.encode("hex"),
671                        print
672               
673                byte_index = 0
674               
675                # Parse the extended_code_level, code, and length
676                while (byte_index < len(data_payload)):
677                        extended_code_level = 0
678                       
679                        # 1. Parse and count the number of [EXCODE] (0x55)
680                        #    bytes that may be at the beginning of the
681                        #    current DataRow.
682                        while (data_payload[byte_index] == PROTOCOL_EXCODE):
683                                extended_code_level += 1
684                                byte_index += 1
685                       
686                        # 2. Parse the [CODE] byte for the current DataRow.
687                        code = data_payload[byte_index]
688                        byte_index += 1
689                        code = code.encode("hex")
690                       
691                        # 3. If [CODE] >= 0x80, parse the next byte as the
692                        #    [VLENGTH] byte for the current DataRow.
693                        if (code > '\x7f'.encode("hex")):
694                                length = data_payload[byte_index]
695                                byte_index += 1
696                                length = length.encode("hex")
697                                length = int(length, 16)
698                        else:
699                                length = 1
700                       
701                       
702                        if self.DEBUG > 3:
703                                print "EXCODE level:",
704                                print extended_code_level,
705                                print " CODE:",
706                                print code,
707                                print " length:",
708                                print length
709                                #print type(code)
710                       
711                        data_values = ''
712                        value_index = 0
713                       
714                        # 4. Parse and handle the [VALUE...] byte(s) of the current
715                        #    DataRow, based on the DataRow's [EXCODE] level, [CODE],
716                        #    and [VLENGTH] (refer to the Code De nitions Table).
717                        while value_index < length:
718                                # Uh-oh more C mojo
719                                try:
720                                        value = data_payload[(byte_index + value_index)] # & 0xFF
721                                except:
722                                        if self.DEBUG:
723                                                print "ERROR: failed to parse and handle the [VALUE...] bytes of the current DataRow"
724                                        break
725                                data_values += value.encode("hex")
726                                value_index += 1
727                       
728                        if self.DEBUG > 3:
729                                print "Data Values:",
730                                print data_values
731                                print
732                       
733                        packet_update = self.processDataRow(extended_code_level, \
734                                                            code, \
735                                                            length, \
736                                                            data_values, \
737                                                            timestamp)
738                       
739                        self.updateDataPacket(packet_update)
740                       
741                       
742                        byte_index += length
743                       
744                        # 5. If not all bytes have been parsed from the payload[] array,
745                        # return to step 1. to continue parsing the next DataRow.
746       
747       
748        ##################################################################
749       
750        def parseStream(self):
751               
752                '''Each Packet begins with its Header, followed by its Data Payload,
753                and ends with the Payload's Check-sum Byte, as follows:
754                [SYNC] [SYNC] [PLENGTH]      [PAYLOAD...]         [CHKSUM]
755                _______________________      _____________     ____________
756                ^^^^^^^^(Header)^^^^^^^      ^^(Payload)^^     ^(Checksum)^'''
757               
758                # Loop forever, parsing one Packet per loop...
759                #packet_count = 0
760                self.parent.setPacketCount(0)
761               
762                while self.keep_running:
763                       
764                        # Synchronize on [SYNC] bytes
765                        # Read from stream until two consecutive [SYNC] bytes are found
766                        byte = self.device.read()
767                        if (byte != PROTOCOL_SYNC):
768                                continue
769                       
770                        byte = self.device.read()
771                        if (byte != PROTOCOL_SYNC):
772                                continue
773                       
774                        self.payload_timestamp = time.time()
775                       
776                        # Parse [PLENGTH] byte
777                       
778                        # SPEC: [PLENGTH] byte indicates the length, in bytes, of the
779                        # Packet's Data Payload [PAYLOAD...] section, and may be any value
780                        # from 0 up to 169. Any higher value indicates an error
781                        # (PLENGTH TOO LARGE). Be sure to note that [PLENGTH] is the length
782                        # of the Packet's Data Payload, NOT of the entire Packet.
783                        # The Packet's complete length will always be [PLENGTH] + 4.
784                       
785                        byte = self.device.read()
786                        packet_length = byte.encode("hex")
787                        packet_length = int(packet_length, 16)
788                       
789                        if (packet_length > 170):
790                                #self.bad_packets += 1
791                                #self.parent.plugin_session.bad_packets += 1
792                                self.parent.incrementBadPackets()
793                                if self.DEBUG:
794                                        print "ERROR: packet length bad"
795                                        continue
796                       
797                       
798                        # Collect [PAYLOAD...] bytes
799                        data_payload = self.device.read(packet_length)
800                       
801                       
802                        # Calculate [PAYLOAD...] checksum
803                       
804                        # SPEC: The [CHKSUM] Byte must be used to verify the integrity of the
805                        # Packet's Data Payload. The Payload's Checksum is defined as:
806                        #  1. summing all the bytes of the Packet's Data Payload
807                        #  2. taking the lowest 8 bits of the sum
808                        #  3. performing the bit inverse (one's compliment inverse)
809                        #     on those lowest 8 bits
810                       
811                        payload_checksum = 0
812                        for byte in data_payload:
813                                value = byte.encode("hex")
814                                value = int(value, 16)
815                                payload_checksum += value
816                       
817                       
818                        # Take the lowest 8 bits of the calculated payload_checksum
819                        # and invert them. Serious C code mojo follows.
820                        payload_checksum &= 0xff
821                        payload_checksum = ~payload_checksum & 0xff
822                       
823                       
824                        # Parse [CKSUM] byte
825                        packet_checksum = self.device.read()
826                        packet_checksum = packet_checksum.encode("hex")
827                        packet_checksum = int(packet_checksum, 16)
828                       
829                       
830                        # Verify [CKSUM] byte against calculated [PAYLOAD...] checksum
831                        if packet_checksum != payload_checksum:
832                                #self.bad_packets += 1
833                                #self.parent.plugin_session.bad_packets += 1
834                                self.parent.incrementBadPackets()
835                               
836                                if self.DEBUG > 1:
837                                        print "ERROR: packet checksum does not match"
838                                        print "       packet_checksum:",
839                                        print packet_checksum
840                                        print "       payload_checksum:",
841                                        print payload_checksum
842                                       
843                                        #self.device.checkBuffer()
844                               
845                                continue
846                       
847                       
848                        else:
849                                # Since [CKSUM] is OK, parse the Data Payload
850                                if self.DEBUG > 3:
851                                        print "packet checksum correct"
852                               
853                               
854                                self.processDataPayload(data_payload, self.payload_timestamp)
855                               
856                               
857                                #if self.DEBUG > 1:
858                                        #packet_count += 1
859                                        #if packet_count >= DEBUG_PACKET_COUNT:
860                                                #print "max debugging count reached, disconnecting"
861                                                #self.keep_running = False
862                                                #self.device.stop()
863                                                #QtCore.QThread.quit(self)
864                                                ##sys.exit()
865       
866       
867        ##################################################################
868       
869        def updateDataPacket(self, packet_update):
870               
871                if 'eSense' in packet_update.keys():
872                        process_packet = {'eSense': {}}
873                        for key in packet_update['eSense'].keys():
874                                self.data_packet['eSense'][key] = packet_update['eSense'][key]
875                                process_packet['eSense'][key] = packet_update['eSense'][key]
876               
877                else:
878                        self.data_packet.update(packet_update)
879                        process_packet = packet_update
880               
881               
882                process_packet['timestamp'] = packet_update['timestamp']
883               
884               
885                if self.DEBUG > 3:
886                        print self.data_packet
887               
888               
889                if (self.parent != None):
890                       
891                        # NOTE: is it possible this call is blocking the Protocol
892                        #       thread from continuing to parse data?
893                       
894                        self.parent.processPacketThinkGear(process_packet)
895       
896       
897        ##################################################################
898       
899        def disconnectHardware(self):
900               
901                if self.device != None and self.device.device != None:
902                        if self.device_model == 'NeuroSky MindWave':
903                                if self.DEBUG:
904                                        print "INFO: ThinkGear device model MindWave selected. Writing disconnect packet."
905                                try:
906                                        self.device.device.write('\xc1')
907                                except Exception, e:
908                                        if self.DEBUG:
909                                                print "ERROR: failed to write disconnect packet: ",
910                                                print e
911       
912       
913        ##################################################################
914       
915        def resetSessionStartTime(self):
916               
917                #self.session_start_timestamp = time.time()
918               
919                if (self.parent != None):
920                       
921                        #self.parent.updateSessionStartTime(self.session_start_timestamp)
922                        self.parent.resetSessionStartTime()
923       
924       
925        ##################################################################
926       
927        def resetSession(self):
928               
929                self.parent.setPacketCount(0)
930                self.parent.setBadPackets(0)
931                #self.session_start_timestamp = time.time()
932                #self.parent.setSessionTimestamp( time.time() )
933                #self.parent.resetSessionTimestamp()
934                self.parent.resetSessionStartTime()
935       
936       
937        ##################################################################
938       
939        def run(self):
940               
941                try:
942                        self.resetSession()
943                except Exception, e:
944                        if self.DEBUG:
945                                print "ERROR: self.resetSession():",
946                                print e
947               
948                if self.device != None and self.device.device != None:
949                        if self.device_model == 'NeuroSky MindWave':
950                                if self.DEBUG:
951                                        print "INFO: ThinkGear device model MindWave selected. Writing disconnect packet."
952                                self.device.device.write('\xc1')
953                                if self.DEBUG:
954                                        print "INFO: ThinkGear device model MindWave selected. Writing auto-connect packet."
955                                self.device.device.write('\xc2')
956                        else:
957                                if self.device_model != None and self.DEBUG:
958                                        print "INFO: %s device model selected" % self.device_model
959                       
960                        self.parseStream()
961       
962       
963        ##################################################################
964       
965        def exitThread(self, callThreadQuit=True):
966               
967                self.disconnectHardware()
968               
969                try:
970                        self.device.stop()
971                except:
972                        pass
973               
974               
975                if callThreadQuit:
976                        QtCore.QThread.quit(self)
977
978
979#####################################################################
980#####################################################################
981
982class SerialDevice(QtCore.QThread):
983       
984        def __init__(self, log, \
985                               device_address=THINKGEAR_DEVICE_SERIAL_PORT, \
986                               DEBUG=DEBUG, \
987                               parent=None):
988               
989                QtCore.QThread.__init__(self, parent)
990               
991                self.log = log
992                self.DEBUG = DEBUG
993                self.parent = parent
994               
995                self.device_address = device_address
996                self.device = None
997                self.buffer = ''
998               
999                if (self.device_address.count(':') == 5):
1000                        # Device address is a Bluetooth MAC address
1001                        if self.DEBUG:
1002                                print "Initializing Bluetooth Device",
1003                                print self.device_address
1004                        self.device = self.initializeBluetoothDevice()
1005                else:
1006                        # Device address is a serial port address
1007                        if self.DEBUG:
1008                                print "Initializing Serial Device",
1009                                print self.device_address
1010                        self.device = self.initializeSerialDevice()
1011               
1012                self.buffer_check_timer = QtCore.QTimer()
1013                QtCore.QObject.connect(self.buffer_check_timer, \
1014                                       QtCore.SIGNAL("timeout()"), \
1015                                       self.checkBuffer)
1016                self.buffer_check_timer.start(DEVICE_BUFFER_CHECK_TIMER)
1017               
1018                self.read_buffer_check_timer = QtCore.QTimer()
1019                QtCore.QObject.connect(self.read_buffer_check_timer, \
1020                                       QtCore.SIGNAL("timeout()"), \
1021                                       self.checkReadBuffer)
1022#               self.read_buffer_check_timer.start(DEVICE_READ_BUFFER_CHECK_TIMER)
1023               
1024                self.keep_running = True
1025       
1026       
1027        ##################################################################
1028       
1029        def initializeBluetoothDevice(self):
1030               
1031                socket = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
1032               
1033                try:
1034                        socket.connect((self.device_address, THINKGEAR_DEVICE_BLUETOOTH_CHANNEL))
1035               
1036                except Exception, e:
1037                        if self.DEBUG:
1038                                print "ERROR:",
1039                                print e
1040                                sys.exit()
1041               
1042               
1043                return socket
1044       
1045       
1046        ##################################################################
1047       
1048        def initializeSerialDevice(self):
1049               
1050                baudrate = DEFAULT_SERIAL_BAUDRATE
1051                bytesize = 8
1052                parity = 'NONE'
1053                stopbits = 1
1054                software_flow_control = 'f'
1055                rts_cts_flow_control = 'f'
1056                #timeout = 15
1057                timeout = 5
1058               
1059                # convert bytesize
1060                if (bytesize == 5):
1061                        init_byte_size = serial.FIVEBITS
1062                elif (bytesize == 6):
1063                        init_byte_size = serial.SIXBITS
1064                elif (bytesize == 7):
1065                        init_byte_size = serial.SEVENBITS
1066                elif (bytesize == 8):
1067                        init_byte_size = serial.EIGHTBITS
1068                else:
1069                        #self.log.perror("Invalid value for %s modem byte size! Using default (8)" % modem_type)
1070                        init_byte_size = serial.EIGHTBITS
1071               
1072                # convert parity
1073                if (parity == 'NONE'):
1074                        init_parity = serial.PARITY_NONE
1075                elif (parity == 'EVEN'):
1076                        init_parity = serial.PARITY_EVEN
1077                elif (parity == 'ODD'):
1078                        init_parity = serial.PARITY_ODD
1079                else:
1080                        #self.log.perror("Invalid value for %s modem parity! Using default (NONE)" % modem_type)
1081                        init_parity = serial.PARITY_NONE
1082               
1083                # convert stopbits
1084                if (stopbits == 1):
1085                        init_stopbits = serial.STOPBITS_ONE
1086                elif (stopbits == 2):
1087                        init_stopbits = serial.STOPBITS_TWO
1088                else:
1089                        #self.log.perror("Invalid value for %s modem stopbits! Using default (8)" % modem_type)
1090                        init_byte_size = serial.STOPBITS_ONE
1091               
1092                # convert software flow control
1093                if (software_flow_control == 't'):
1094                        init_software_flow_control = 1
1095                else:
1096                        init_software_flow_control = 0
1097               
1098                # convert rts cts flow control
1099                if (rts_cts_flow_control == 't'):
1100                        init_rts_cts_flow_control = 1
1101                else:
1102                        init_rts_cts_flow_control = 0
1103               
1104               
1105                try:
1106##                      device = serial.Serial(port = self.device_address, \
1107##                                                  baudrate = baudrate, \
1108##                                                  bytesize = init_byte_size, \
1109##                                                  parity = init_parity, \
1110##                                                  stopbits = init_stopbits, \
1111##                                                  xonxoff = init_software_flow_control, \
1112##                                                  rtscts = init_rts_cts_flow_control, \
1113##                                                  timeout = timeout)
1114                       
1115                        device = serialWrapper(port = self.device_address, \
1116                                                    baudrate = baudrate, \
1117                                                    bytesize = init_byte_size, \
1118                                                    parity = init_parity, \
1119                                                    stopbits = init_stopbits, \
1120                                                    xonxoff = init_software_flow_control, \
1121                                                    rtscts = init_rts_cts_flow_control, \
1122                                                    timeout = timeout)
1123               
1124                except Exception, e:
1125                        if self.DEBUG:
1126                                print "ERROR:",
1127                                print e,
1128                                print self.device_address
1129                                #sys.exit()
1130                                return(None)
1131               
1132               
1133                device.flushInput()
1134                #device.flushOutput()
1135               
1136               
1137                #if self.DEBUG:
1138                        #print "Writing device connect packet"
1139                #device.write('\xc2')
1140               
1141               
1142                return(device)
1143       
1144       
1145        ##################################################################
1146       
1147        def checkBuffer(self):
1148               
1149                if self.DEBUG > 1:
1150                        print "INFO: Buffer size check:",
1151                        print len(self.buffer),
1152                        print "(maximum before reset is %i)" % DEVICE_BUFFER_MAX_SIZE
1153               
1154                if (DEVICE_BUFFER_MAX_SIZE <= len(self.buffer)):
1155                       
1156                        if self.DEBUG:
1157                                print "ERROR: Buffer size has grown too large, resetting"
1158                       
1159                        self.reset()
1160       
1161       
1162        ##################################################################
1163       
1164        def checkReadBuffer(self):
1165               
1166                if self.DEBUG > 1:
1167                        print "INFO: Read buffer timer check"
1168               
1169                current_time = time.time()
1170               
1171                if ((self.parent != None) and \
1172                    (self.parent.protocol != None)):
1173                       
1174                        if (current_time - self.parent.protocol.payload_timestamp > \
1175                                 DEVICE_BUFFER_CHECK_TIMER):
1176                               
1177                                if self.DEBUG:
1178                                        print "ERROR: Read buffer timer has expired, resetting connection"
1179                               
1180                        self.parent.resetDevice()
1181       
1182       
1183        ##################################################################
1184       
1185        def reset(self):
1186               
1187                self.buffer = ''
1188       
1189       
1190        ##################################################################
1191       
1192        def read(self, length=1):
1193               
1194                # Sleep for 2 ms if buffer is empty
1195                # Based on 512 Hz refresh rate of NeuroSky MindSet device
1196                # (1/512) * 1000 = 1.9531250
1197                while len(self.buffer) < length:
1198                        try:
1199                                QtCore.QThread.msleep(2)
1200                        except Exception, e:
1201                                #if self.DEBUG:
1202                                        #print "ERROR: Protocol failed to call QtCore.QThread.msleep(2) in read():",
1203                                        #print e
1204                                pass
1205                       
1206                bytes = self.buffer[:length]
1207               
1208                self.buffer = self.buffer[length:]
1209               
1210                return(bytes)
1211       
1212       
1213        ##################################################################
1214       
1215        def stop(self):
1216               
1217                self.keep_running = False
1218                try:
1219                        self.buffer_check_timer.stop()
1220                except Exception, e:
1221                        if self.DEBUG:
1222                                print "ERROR: Protocol failed to call self.buffer_check_timer.stop() in stop():",
1223                                print e
1224               
1225                try:
1226                        self.read_buffer_check_timer.stop()
1227                except Exception, e:
1228                        if self.DEBUG:
1229                                print "ERROR: Protocol failed to call self.read_buffer_check_timer.stop() in stop():",
1230                                print e
1231               
1232                self.buffer = ''
1233       
1234       
1235        ##################################################################
1236       
1237        def exitThread(self, callThreadQuit=True):
1238               
1239                self.stop()
1240                self.close()
1241               
1242                if callThreadQuit:
1243                        try:
1244                                QtCore.QThread.quit(self)
1245                        except Exception, e:
1246                                if self.DEBUG:
1247                                        print "ERROR: Protocol failed to call QtCore.QThread.quit(self) in exitThread():",
1248                                        print e
1249       
1250       
1251        ##################################################################
1252       
1253        def close(self):
1254               
1255                if self.device != None:
1256                       
1257                        try:
1258                                self.device.close()
1259                        except Exception, e:
1260                                #if self.DEBUG:
1261                                        #print "ERROR: Protocol failed to close device in close():",
1262                                        #print e
1263                                pass
1264       
1265       
1266        ##################################################################
1267       
1268        def run(self):
1269               
1270                if self.device == None:
1271                        self.keep_running = False
1272               
1273                self.buffer = ''
1274               
1275                while self.keep_running:
1276                       
1277                        try:
1278                                #byte = self.device.read()
1279                                byte = self.device.recv(1)
1280                               
1281                                if (len(byte) != 0):
1282                                        if self.DEBUG > 2:
1283                                                print "Device read:",
1284                                                print byte
1285                                               
1286                                        self.buffer += byte
1287                       
1288                        except Exception, e:
1289                                if self.DEBUG:
1290                                        print "ERROR: failed to read from serial device:",
1291                                        print e
1292                                break
1293               
1294               
1295                self.exitThread()
1296
1297
1298#####################################################################
1299#####################################################################
1300
1301class serialWrapper(serial.Serial):
1302       
1303        #__init__(port=None, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False, rtscts=False, writeTimeout=None, dsrdtr=False, interCharTimeout=None)
1304       
1305        def recv(self, size=1):
1306               
1307                return(self.read(size))
1308
Note: See TracBrowser for help on using the repository browser.