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

Last change on this file since 291 was 291, checked in by sc, 10 years ago
  • Updated for new Puzzlebox Jigsaw plug-in model
  • Property svn:executable set to *
File size: 31.0 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                if self.device != None:
768                        self.parseStream()
769       
770       
771        ##################################################################
772       
773        def exitThread(self, callThreadQuit=True):
774               
775                try:
776                        self.device.stop()
777                except:
778                        pass
779               
780                #self.wait()
781                if callThreadQuit:
782                        QtCore.QThread.quit(self)
783
784
785#####################################################################
786#####################################################################
787
788class SerialDevice(QtCore.QThread):
789       
790        def __init__(self, log, \
791                               device_address=THINKGEAR_DEVICE_SERIAL_PORT, \
792                               DEBUG=DEBUG, \
793                               parent=None):
794               
795                QtCore.QThread.__init__(self, parent)
796               
797                self.log = log
798                self.DEBUG = DEBUG
799                self.parent = parent
800               
801                self.device_address = device_address
802                self.device = None
803                self.buffer = ''
804               
805                if (self.device_address.count(':') == 5):
806                        # Device address is a Bluetooth MAC address
807                        if self.DEBUG:
808                                print "Initializing Bluetooth Device",
809                                print self.device_address
810                        self.device = self.initializeBluetoothDevice()
811                else:
812                        # Device address is a serial port address
813                        if self.DEBUG:
814                                print "Initializing Serial Device",
815                                print self.device_address
816                        self.device = self.initializeSerialDevice()
817               
818                self.buffer_check_timer = QtCore.QTimer()
819                QtCore.QObject.connect(self.buffer_check_timer, \
820                                       QtCore.SIGNAL("timeout()"), \
821                                       self.checkBuffer)
822                self.buffer_check_timer.start(DEVICE_BUFFER_CHECK_TIMER)
823               
824                self.read_buffer_check_timer = QtCore.QTimer()
825                QtCore.QObject.connect(self.read_buffer_check_timer, \
826                                       QtCore.SIGNAL("timeout()"), \
827                                       self.checkReadBuffer)
828#               self.read_buffer_check_timer.start(DEVICE_READ_BUFFER_CHECK_TIMER)
829               
830                self.keep_running = True
831       
832       
833        ##################################################################
834       
835        def initializeBluetoothDevice(self):
836               
837                socket = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
838               
839                try:
840                        socket.connect((self.device_address, THINKGEAR_DEVICE_BLUETOOTH_CHANNEL))
841               
842                except Exception, e:
843                        if self.DEBUG:
844                                print "ERROR:",
845                                print e
846                                sys.exit()
847               
848               
849                return socket
850       
851       
852        ##################################################################
853       
854        def initializeSerialDevice(self):
855               
856                baudrate = DEFAULT_SERIAL_BAUDRATE
857                bytesize = 8
858                parity = 'NONE'
859                stopbits = 1
860                software_flow_control = 'f'
861                rts_cts_flow_control = 'f'
862                #timeout = 15
863                timeout = 5
864               
865                # convert bytesize
866                if (bytesize == 5):
867                        init_byte_size = serial.FIVEBITS
868                elif (bytesize == 6):
869                        init_byte_size = serial.SIXBITS
870                elif (bytesize == 7):
871                        init_byte_size = serial.SEVENBITS
872                elif (bytesize == 8):
873                        init_byte_size = serial.EIGHTBITS
874                else:
875                        #self.log.perror("Invalid value for %s modem byte size! Using default (8)" % modem_type)
876                        init_byte_size = serial.EIGHTBITS
877               
878                # convert parity
879                if (parity == 'NONE'):
880                        init_parity = serial.PARITY_NONE
881                elif (parity == 'EVEN'):
882                        init_parity = serial.PARITY_EVEN
883                elif (parity == 'ODD'):
884                        init_parity = serial.PARITY_ODD
885                else:
886                        #self.log.perror("Invalid value for %s modem parity! Using default (NONE)" % modem_type)
887                        init_parity = serial.PARITY_NONE
888               
889                # convert stopbits
890                if (stopbits == 1):
891                        init_stopbits = serial.STOPBITS_ONE
892                elif (stopbits == 2):
893                        init_stopbits = serial.STOPBITS_TWO
894                else:
895                        #self.log.perror("Invalid value for %s modem stopbits! Using default (8)" % modem_type)
896                        init_byte_size = serial.STOPBITS_ONE
897               
898                # convert software flow control
899                if (software_flow_control == 't'):
900                        init_software_flow_control = 1
901                else:
902                        init_software_flow_control = 0
903               
904                # convert rts cts flow control
905                if (rts_cts_flow_control == 't'):
906                        init_rts_cts_flow_control = 1
907                else:
908                        init_rts_cts_flow_control = 0
909               
910               
911                try:
912##                      device = serial.Serial(port = self.device_address, \
913##                                                  baudrate = baudrate, \
914##                                                  bytesize = init_byte_size, \
915##                                                  parity = init_parity, \
916##                                                  stopbits = init_stopbits, \
917##                                                  xonxoff = init_software_flow_control, \
918##                                                  rtscts = init_rts_cts_flow_control, \
919##                                                  timeout = timeout)
920                       
921                        device = serialWrapper(port = self.device_address, \
922                                                    baudrate = baudrate, \
923                                                    bytesize = init_byte_size, \
924                                                    parity = init_parity, \
925                                                    stopbits = init_stopbits, \
926                                                    xonxoff = init_software_flow_control, \
927                                                    rtscts = init_rts_cts_flow_control, \
928                                                    timeout = timeout)
929               
930                except Exception, e:
931                        if self.DEBUG:
932                                print "ERROR:",
933                                print e,
934                                print self.device_address
935                                #sys.exit()
936                                return(None)
937               
938               
939                device.flushInput()
940                #device.flushOutput()
941               
942               
943                #if self.DEBUG:
944                        #print "Writing device connect packet"
945                #device.write('\xc2')
946               
947               
948                return(device)
949       
950       
951        ##################################################################
952       
953        def checkBuffer(self):
954               
955                if self.DEBUG > 1:
956                        print "INFO: Buffer size check:",
957                        print len(self.buffer),
958                        print "(maximum before reset is %i)" % DEVICE_BUFFER_MAX_SIZE
959               
960                if (DEVICE_BUFFER_MAX_SIZE <= len(self.buffer)):
961                       
962                        if self.DEBUG:
963                                print "ERROR: Buffer size has grown too large, resetting"
964                       
965                        self.reset()
966       
967       
968        ##################################################################
969       
970        def checkReadBuffer(self):
971               
972                if self.DEBUG > 1:
973                        print "INFO: Read buffer timer check"
974               
975                current_time = time.time()
976               
977                if ((self.parent != None) and \
978                    (self.parent.protocol != None)):
979                       
980                        if (current_time - self.parent.protocol.payload_timestamp > \
981                                 DEVICE_BUFFER_CHECK_TIMER):
982                               
983                                if self.DEBUG:
984                                        print "ERROR: Read buffer timer has expired, resetting connection"
985                               
986                        self.parent.resetDevice()
987       
988       
989        ##################################################################
990       
991        def reset(self):
992               
993                self.buffer = ''
994       
995       
996        ##################################################################
997       
998        def read(self, length=1):
999               
1000                # Sleep for 2 ms if buffer is empty
1001                # Based on 512 Hz refresh rate of NeuroSky MindSet device
1002                # (1/512) * 1000 = 1.9531250
1003                while len(self.buffer) < length:
1004                        try:
1005                                QtCore.QThread.msleep(2)
1006                        except Exception, e:
1007                                #if self.DEBUG:
1008                                        #print "ERROR: Protocol failed to call QtCore.QThread.msleep(2) in read():",
1009                                        #print e
1010                                pass
1011                       
1012                bytes = self.buffer[:length]
1013               
1014                self.buffer = self.buffer[length:]
1015               
1016                return(bytes)
1017       
1018       
1019        ##################################################################
1020       
1021        def stop(self):
1022               
1023                self.keep_running = False
1024                self.buffer_check_timer.stop()
1025                self.read_buffer_check_timer.stop()
1026                self.buffer = ''
1027       
1028       
1029        ##################################################################
1030       
1031        def exitThread(self, callThreadQuit=True):
1032               
1033                self.stop()
1034                self.close()
1035               
1036                if callThreadQuit:
1037                        QtCore.QThread.quit(self)
1038       
1039       
1040        ##################################################################
1041       
1042        def close(self):
1043               
1044                if self.device != None:
1045                        self.device.close()
1046       
1047       
1048        ##################################################################
1049       
1050        def run(self):
1051               
1052                if self.device == None:
1053                        self.keep_running = False
1054               
1055                self.buffer = ''
1056               
1057                while self.keep_running:
1058                       
1059                        try:
1060                                #byte = self.device.read()
1061                                byte = self.device.recv(1)
1062                               
1063                                if (len(byte) != 0):
1064                                        if self.DEBUG > 2:
1065                                                print "Device read:",
1066                                                print byte
1067                                               
1068                                        self.buffer += byte
1069                       
1070                        except:
1071                                if self.DEBUG:
1072                                        print "ERROR: failed to read from serial device"
1073                                break
1074               
1075               
1076                self.exitThread()
1077
1078
1079#####################################################################
1080#####################################################################
1081
1082class serialWrapper(serial.Serial):
1083       
1084        #__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)
1085       
1086        def recv(self, size=1):
1087               
1088                return(self.read(size))
1089
Note: See TracBrowser for help on using the repository browser.