source: trunk/synapse/puzzlebox_synapse_protocol_thinkgear.py @ 161

Last change on this file since 161 was 159, checked in by sc, 11 years ago

puzzlebox_synapse_configuration.py:

  • THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE set to 16384

puzzlebox_synapse_interface.py:

  • automatic searching for COM ports under Windows added
  • enumerate_serial_ports added
  • full_port_name added
  • maximum EEG power values reset to default per-packet when not in emulation mode

puzzlebox_synapse_protocol_thinkgear.py:

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