From d65cfbebb8730960211284856ac3ab7ba93a98fe Mon Sep 17 00:00:00 2001 From: Daniel Lasry Date: Sun, 17 Feb 2019 13:01:46 -0800 Subject: [PATCH 1/4] ADD: Aux to fix wifi --- CHANGELOG.md | 6 +++++ openbci/utils/constants.py | 5 ++++ openbci/utils/parse.py | 48 +++++++++++++++++++++++++++++++++- openbci/wifi.py | 53 +++++++++++++++++++++++++++++++++----- tests/test_parse.py | 25 ++++++++++++++++++ tests/test_wifi.py | 8 +++++- 6 files changed, 137 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3cae41..85cd07f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.1.0 + +### New Features + +* Now supporting digital and analog reading on cyton wifi + # v1.0.2 ### Bug Fixes diff --git a/openbci/utils/constants.py b/openbci/utils/constants.py index dc0f238..5cbee17 100644 --- a/openbci/utils/constants.py +++ b/openbci/utils/constants.py @@ -92,3 +92,8 @@ class Constants: SAMPLE_RATE_6400 = 6400 SAMPLE_RATE_800 = 800 SAMPLE_RATE_8000 = 8000 + + """Different Aux Modes""" + AUX_MODE_ANALOG = 'analog' + AUX_MODE_DEFAULT = 'default' + AUX_MODE_DIGITAL = 'digital' diff --git a/openbci/utils/parse.py b/openbci/utils/parse.py index 424df38..56dd8db 100644 --- a/openbci/utils/parse.py +++ b/openbci/utils/parse.py @@ -156,7 +156,51 @@ def parse_packet_standard_accel(self, raw_data_to_sample): return sample_object def parse_packet_standard_raw_aux(self, raw_data_to_sample): - pass + """ + + :param raw_data_to_sample: RawDataToSample + :return: + """ + # Check to make sure data is not null. + if raw_data_to_sample is None: + raise RuntimeError(k.ERROR_UNDEFINED_OR_NULL_INPUT) + if raw_data_to_sample.raw_data_packet is None: + raise RuntimeError(k.ERROR_UNDEFINED_OR_NULL_INPUT) + + # Check to make sure the buffer is the right size. + if len(raw_data_to_sample.raw_data_packet) != k.RAW_PACKET_SIZE: + raise RuntimeError(k.ERROR_INVALID_BYTE_LENGTH) + + # Verify the correct stop byte. + if raw_data_to_sample.raw_data_packet[0] != k.RAW_BYTE_START: + raise RuntimeError(k.ERROR_INVALID_BYTE_START) + + sample_object = OpenBCISample() + + sample_object.aux_data = raw_data_to_sample.raw_data_packet[k.RAW_PACKET_POSITION_START_AUX:k.RAW_PACKET_POSITION_STOP_AUX+1] + + sample_object.packet_type = k.RAW_PACKET_TYPE_STANDARD_RAW_AUX + + sample_object.channel_data = self.get_channel_data_array(raw_data_to_sample) + + sample_object.sample_number = raw_data_to_sample.raw_data_packet[ + k.RAW_PACKET_POSITION_SAMPLE_NUMBER + ] + sample_object.start_byte = raw_data_to_sample.raw_data_packet[ + k.RAW_PACKET_POSITION_START_BYTE + ] + sample_object.stop_byte = raw_data_to_sample.raw_data_packet[ + k.RAW_PACKET_POSITION_STOP_BYTE + ] + + sample_object.valid = True + + now_ms = int(round(time.time() * 1000)) + + sample_object.timestamp = now_ms + sample_object.boardTime = 0 + + return sample_object def parse_packet_time_synced_accel(self, raw_data_to_sample): pass @@ -370,3 +414,5 @@ def __init__(self, self._timestamps = {} self.valid = valid self.accel_data = accel_data if accel_data is not None else [] + self.analog_data = {} + self.digital_data = {} diff --git a/openbci/wifi.py b/openbci/wifi.py index 5f2c7a4..fe70079 100755 --- a/openbci/wifi.py +++ b/openbci/wifi.py @@ -65,7 +65,7 @@ class OpenBCIWiFi(object): def __init__(self, ip_address=None, shield_name=None, sample_rate=None, log=True, timeout=3, max_packets_to_skip=20, latency=10000, high_speed=True, ssdp_attempts=5, - num_channels=8, local_ip_address=None): + num_channels=8, local_ip_address=None, aux_mode=Constants.AUX_MODE_DEFAULT): # these one are used self.daisy = False self.gains = None @@ -81,6 +81,7 @@ def __init__(self, ip_address=None, shield_name=None, sample_rate=None, log=True self.ssdp_attempts = ssdp_attempts self.streaming = False self.timeout = timeout + self.aux_mode = aux_mode # might be handy to know API self.board_type = "none" @@ -99,7 +100,7 @@ def __init__(self, ip_address=None, shield_name=None, sample_rate=None, log=True self.local_ip_address = self._get_local_ip_address() # Intentionally bind to port 0 - self.local_wifi_server = WiFiShieldServer(self.local_ip_address, 0) + self.local_wifi_server = WiFiShieldServer(self.local_ip_address, 0, aux_mode=self.aux_mode) self.local_wifi_server_port = self.local_wifi_server.socket.getsockname()[1] if self.log: print("Opened socket on %s:%d" % @@ -208,6 +209,10 @@ def connect(self): raise RuntimeWarning("WiFi Shield is not able to connect to local server." "Please open an issue.") + if self.board_type != Constants.BOARD_GANGLION: + self.aux_mode_set(self.aux_mode) + + def init_streaming(self): """ Tell the board to record like crazy. """ res_stream_start = requests.get( @@ -288,6 +293,25 @@ def wifi_write(self, output): raise RuntimeError("Error code: %d %s" % ( res_command_post.status_code, res_command_post.text)) + def aux_mode_set(self, aux_mode): + """ + Used to configure the cyton to enter digital or analog read mode. + NOTE: Does not work on Ganglion + :param aux_mode: str + Can be either 'default', 'digital', or 'analog' + :return: None + """ + if self.board_type == Constants.BOARD_GANGLION: + raise RuntimeError("Aux mode not implemented on ganglion") + elif aux_mode == Constants.AUX_MODE_DEFAULT: + self.wifi_write('/0') + elif aux_mode == Constants.AUX_MODE_ANALOG: + self.wifi_write('/2') + elif aux_mode == Constants.AUX_MODE_DIGITAL: + self.wifi_write('/3') + else: + raise RuntimeError("Invalid aux mode choice") + def getSampleRate(self): return self.sample_rate @@ -617,7 +641,7 @@ def reconnect(self): class WiFiShieldHandler(asyncore.dispatcher_with_send): def __init__(self, sock, callback=None, high_speed=True, - parser=None, daisy=False): + parser=None, daisy=False, aux_mode=Constants.AUX_MODE_DEFAULT): asyncore.dispatcher_with_send.__init__(self, sock) self.callback = callback @@ -626,6 +650,7 @@ def __init__(self, sock, callback=None, high_speed=True, self.last_odd_sample = OpenBCISample() self.parser = parser if parser is not None else ParseRaw( gains=[24, 24, 24, 24, 24, 24, 24, 24]) + self.aux_mode = aux_mode def handle_read(self): # 3000 is the max data the WiFi shield is allowed to send over TCP @@ -657,7 +682,22 @@ def handle_read(self): self.last_odd_sample, sample) if self.callback is not None: self.callback(daisy_sample) - else: + elif len(sample.aux_data) != 0: + if self.aux_mode is Constants.AUX_MODE_ANALOG: + # extract value sample.aux_data + analog_val_A5 = sample.aux_data[0] << 8 | sample.aux_data[1] + analog_val_A6 = sample.aux_data[2] << 8 | sample.aux_data[3] + sample.analog_data = { + 'A5': analog_val_A5, + 'A6': analog_val_A6 + } + elif self.aux_mode is Constants.AUX_MODE_DIGITAL: + sample.digital_data = { + 'D11': ((sample.aux_data[0] & 0xFF00) >> 8), + 'D12': sample.aux_data[0] & 0xFF, + 'D17': sample.aux_data[1] & 0xFF + } + if self.callback is not None: self.callback(sample) @@ -684,12 +724,13 @@ def handle_read(self): class WiFiShieldServer(asyncore.dispatcher): - def __init__(self, host, port, callback=None, gains=None, high_speed=True, daisy=False): + def __init__(self, host, port, callback=None, gains=None, high_speed=True, daisy=False, aux_mode=Constants.AUX_MODE_DEFAULT): asyncore.dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((host, port)) self.daisy = daisy + self.aux_mode = aux_mode self.listen(5) self.callback = None self.handler = None @@ -702,7 +743,7 @@ def handle_accept(self): sock, addr = pair print('Incoming connection from %s' % repr(addr)) self.handler = WiFiShieldHandler(sock, self.callback, high_speed=self.high_speed, - parser=self.parser, daisy=self.daisy) + parser=self.parser, daisy=self.daisy, aux_mode=self.aux_mode) def set_callback(self, callback): self.callback = callback diff --git a/tests/test_parse.py b/tests/test_parse.py index c7c17e5..9c12f97 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -1,6 +1,9 @@ from unittest import TestCase, main, skip import mock +import sys, os +sys.path.insert(1, os.path.join(sys.path[0], '..')) + from openbci.utils import (Constants, OpenBCISample, ParseRaw, @@ -178,6 +181,28 @@ def test_parse_packet_standard_accel(self): self.assertEqual(sample.stop_byte, 0xC0) self.assertTrue(sample.valid) + def test_parse_packet_standard_raw_aux(self): + sample_number = 0x45 + data = sample_packet_standard_raw_aux(sample_number) + + expected_scale_factor = 4.5 / 24 / (pow(2, 23) - 1) + + parser = ParseRaw(gains=[24, 24, 24, 24, 24, 24, 24, 24], scaled_output=True) + + parser.raw_data_to_sample.raw_data_packet = data + + sample = parser.parse_packet_standard_raw_aux(parser.raw_data_to_sample) + + self.assertIsNotNone(sample) + for i in range(len(sample.channel_data)): + self.assertEqual(sample.channel_data[i], expected_scale_factor * (i + 1)) + self.assertEqual(sample.aux_data, bytearray([0, 1, 2, 3, 4, 5]), "Ensure 6 bytes were extracted") + self.assertEqual(sample.packet_type, Constants.RAW_PACKET_TYPE_STANDARD_RAW_AUX) + self.assertEqual(sample.sample_number, 0x45) + self.assertEqual(sample.start_byte, 0xA0) + self.assertEqual(sample.stop_byte, 0xC1) + self.assertTrue(sample.valid) + @mock.patch.object(ParseRaw, 'parse_packet_standard_accel') def test_transform_raw_data_packet_to_sample_accel(self, mock_parse_packet_standard_accel): data = sample_packet(0) diff --git a/tests/test_wifi.py b/tests/test_wifi.py index 1797d24..b42c8de 100644 --- a/tests/test_wifi.py +++ b/tests/test_wifi.py @@ -1,6 +1,9 @@ from unittest import TestCase, main, skip import mock +import sys, os +sys.path.insert(1, os.path.join(sys.path[0], '..')) + from openbci import OpenBCIWiFi @@ -17,6 +20,7 @@ def test_wifi_init(self, mock_on_shield_found): expected_latency = 5000 expected_high_speed = False expected_ssdp_attempts = 2 + expected_aux_mode = 'analog' wifi = OpenBCIWiFi(ip_address=expected_ip_address, shield_name=expected_shield_name, @@ -26,7 +30,8 @@ def test_wifi_init(self, mock_on_shield_found): max_packets_to_skip=expected_max_packets_to_skip, latency=expected_latency, high_speed=expected_high_speed, - ssdp_attempts=expected_ssdp_attempts) + ssdp_attempts=expected_ssdp_attempts, + aux_mode=expected_aux_mode) self.assertEqual(wifi.ip_address, expected_ip_address) self.assertEqual(wifi.shield_name, expected_shield_name) @@ -37,6 +42,7 @@ def test_wifi_init(self, mock_on_shield_found): self.assertEqual(wifi.latency, expected_latency) self.assertEqual(wifi.high_speed, expected_high_speed) self.assertEqual(wifi.ssdp_attempts, expected_ssdp_attempts) + self.assertEqual(wifi.aux_mode, expected_aux_mode) mock_on_shield_found.assert_called_with(expected_ip_address) From daa3e12f35138cffab5febb7ffdd586585f477c3 Mon Sep 17 00:00:00 2001 From: Daniel Lasry Date: Mon, 18 Feb 2019 11:06:14 -0800 Subject: [PATCH 2/4] remove path insertion mistake --- tests/test_parse.py | 3 --- tests/test_wifi.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/tests/test_parse.py b/tests/test_parse.py index 9c12f97..944174d 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -1,9 +1,6 @@ from unittest import TestCase, main, skip import mock -import sys, os -sys.path.insert(1, os.path.join(sys.path[0], '..')) - from openbci.utils import (Constants, OpenBCISample, ParseRaw, diff --git a/tests/test_wifi.py b/tests/test_wifi.py index b42c8de..dfc2983 100644 --- a/tests/test_wifi.py +++ b/tests/test_wifi.py @@ -1,9 +1,6 @@ from unittest import TestCase, main, skip import mock -import sys, os -sys.path.insert(1, os.path.join(sys.path[0], '..')) - from openbci import OpenBCIWiFi From e517f628b7eba3d5d0369d98c72d3bed469eb6cd Mon Sep 17 00:00:00 2001 From: Daniel Lasry Date: Wed, 6 Mar 2019 09:21:13 -0800 Subject: [PATCH 3/4] Moved unpacking logic to parse.py --- openbci/utils/parse.py | 12 ++++++++++++ openbci/wifi.py | 26 ++++---------------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/openbci/utils/parse.py b/openbci/utils/parse.py index 56dd8db..4003312 100644 --- a/openbci/utils/parse.py +++ b/openbci/utils/parse.py @@ -179,6 +179,18 @@ def parse_packet_standard_raw_aux(self, raw_data_to_sample): sample_object.aux_data = raw_data_to_sample.raw_data_packet[k.RAW_PACKET_POSITION_START_AUX:k.RAW_PACKET_POSITION_STOP_AUX+1] + if(len(sample_object.aux_data) != 0): + # extract value sample_object.aux_data + sample_object.analog_data = { + 'A5': sample_object.aux_data[0] << 8 | sample_object.aux_data[1], + 'A6': sample_object.aux_data[2] << 8 | sample_object.aux_data[3] + } + sample_object.digital_data = { + 'D11': ((sample_object.aux_data[0] & 0xFF00) >> 8), + 'D12': sample_object.aux_data[0] & 0xFF, + 'D17': sample_object.aux_data[1] & 0xFF + } + sample_object.packet_type = k.RAW_PACKET_TYPE_STANDARD_RAW_AUX sample_object.channel_data = self.get_channel_data_array(raw_data_to_sample) diff --git a/openbci/wifi.py b/openbci/wifi.py index fe70079..b0e676d 100755 --- a/openbci/wifi.py +++ b/openbci/wifi.py @@ -100,7 +100,7 @@ def __init__(self, ip_address=None, shield_name=None, sample_rate=None, log=True self.local_ip_address = self._get_local_ip_address() # Intentionally bind to port 0 - self.local_wifi_server = WiFiShieldServer(self.local_ip_address, 0, aux_mode=self.aux_mode) + self.local_wifi_server = WiFiShieldServer(self.local_ip_address, 0) self.local_wifi_server_port = self.local_wifi_server.socket.getsockname()[1] if self.log: print("Opened socket on %s:%d" % @@ -641,7 +641,7 @@ def reconnect(self): class WiFiShieldHandler(asyncore.dispatcher_with_send): def __init__(self, sock, callback=None, high_speed=True, - parser=None, daisy=False, aux_mode=Constants.AUX_MODE_DEFAULT): + parser=None, daisy=False): asyncore.dispatcher_with_send.__init__(self, sock) self.callback = callback @@ -650,7 +650,6 @@ def __init__(self, sock, callback=None, high_speed=True, self.last_odd_sample = OpenBCISample() self.parser = parser if parser is not None else ParseRaw( gains=[24, 24, 24, 24, 24, 24, 24, 24]) - self.aux_mode = aux_mode def handle_read(self): # 3000 is the max data the WiFi shield is allowed to send over TCP @@ -682,25 +681,9 @@ def handle_read(self): self.last_odd_sample, sample) if self.callback is not None: self.callback(daisy_sample) - elif len(sample.aux_data) != 0: - if self.aux_mode is Constants.AUX_MODE_ANALOG: - # extract value sample.aux_data - analog_val_A5 = sample.aux_data[0] << 8 | sample.aux_data[1] - analog_val_A6 = sample.aux_data[2] << 8 | sample.aux_data[3] - sample.analog_data = { - 'A5': analog_val_A5, - 'A6': analog_val_A6 - } - elif self.aux_mode is Constants.AUX_MODE_DIGITAL: - sample.digital_data = { - 'D11': ((sample.aux_data[0] & 0xFF00) >> 8), - 'D12': sample.aux_data[0] & 0xFF, - 'D17': sample.aux_data[1] & 0xFF - } if self.callback is not None: self.callback(sample) - else: try: possible_chunks = data.split('\r\n') @@ -724,13 +707,12 @@ def handle_read(self): class WiFiShieldServer(asyncore.dispatcher): - def __init__(self, host, port, callback=None, gains=None, high_speed=True, daisy=False, aux_mode=Constants.AUX_MODE_DEFAULT): + def __init__(self, host, port, callback=None, gains=None, high_speed=True, daisy=False): asyncore.dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((host, port)) self.daisy = daisy - self.aux_mode = aux_mode self.listen(5) self.callback = None self.handler = None @@ -743,7 +725,7 @@ def handle_accept(self): sock, addr = pair print('Incoming connection from %s' % repr(addr)) self.handler = WiFiShieldHandler(sock, self.callback, high_speed=self.high_speed, - parser=self.parser, daisy=self.daisy, aux_mode=self.aux_mode) + parser=self.parser, daisy=self.daisy) def set_callback(self, callback): self.callback = callback From 4a074889f0412ae2c1ca94b29b1169d91f4e45f7 Mon Sep 17 00:00:00 2001 From: Daniel Lasry Date: Wed, 6 Mar 2019 09:24:19 -0800 Subject: [PATCH 4/4] fixed accidental edit --- openbci/wifi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openbci/wifi.py b/openbci/wifi.py index b0e676d..f6066ee 100755 --- a/openbci/wifi.py +++ b/openbci/wifi.py @@ -681,9 +681,10 @@ def handle_read(self): self.last_odd_sample, sample) if self.callback is not None: self.callback(daisy_sample) - + else: if self.callback is not None: self.callback(sample) + else: try: possible_chunks = data.split('\r\n')