source: trunk/Puzzlebox/Synapse/Protocol.py @ 286

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

Interface:

  • scrubData added

Protocol:

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