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

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