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

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