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..4003312 100644 --- a/openbci/utils/parse.py +++ b/openbci/utils/parse.py @@ -156,7 +156,63 @@ 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] + + 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) + + 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 +426,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..f6066ee 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" @@ -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 diff --git a/tests/test_parse.py b/tests/test_parse.py index c7c17e5..944174d 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -178,6 +178,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..dfc2983 100644 --- a/tests/test_wifi.py +++ b/tests/test_wifi.py @@ -17,6 +17,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 +27,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 +39,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)