explorepy 4.2.0__tar.gz → 4.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. {explorepy-4.2.0 → explorepy-4.3.0}/.bumpversion.cfg +1 -1
  2. {explorepy-4.2.0 → explorepy-4.3.0}/CHANGELOG.rst +7 -0
  3. {explorepy-4.2.0 → explorepy-4.3.0}/PKG-INFO +3 -3
  4. {explorepy-4.2.0 → explorepy-4.3.0}/README.rst +2 -2
  5. {explorepy-4.2.0 → explorepy-4.3.0}/docs/conf.py +1 -1
  6. {explorepy-4.2.0 → explorepy-4.3.0}/docs/usage.rst +88 -17
  7. {explorepy-4.2.0 → explorepy-4.3.0}/pyproject.toml +1 -1
  8. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/BLEClient.py +12 -6
  9. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/BTClient.py +0 -9
  10. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/__init__.py +1 -1
  11. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/cli.py +12 -19
  12. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/command.py +0 -37
  13. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/explore.py +162 -40
  14. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/packet.py +42 -37
  15. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/parser.py +44 -30
  16. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/serial_client.py +2 -3
  17. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/stream_processor.py +7 -17
  18. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/tools.py +25 -239
  19. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy.egg-info/PKG-INFO +3 -3
  20. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy.egg-info/SOURCES.txt +1 -0
  21. explorepy-4.3.0/tests/integration_test_ble.py +115 -0
  22. {explorepy-4.2.0 → explorepy-4.3.0}/.cookiecutterrc +0 -0
  23. {explorepy-4.2.0 → explorepy-4.3.0}/.coveragerc +0 -0
  24. {explorepy-4.2.0 → explorepy-4.3.0}/.editorconfig +0 -0
  25. {explorepy-4.2.0 → explorepy-4.3.0}/AUTHORS.rst +0 -0
  26. {explorepy-4.2.0 → explorepy-4.3.0}/CONTRIBUTING.rst +0 -0
  27. {explorepy-4.2.0 → explorepy-4.3.0}/LICENSE +0 -0
  28. {explorepy-4.2.0 → explorepy-4.3.0}/MANIFEST.in +0 -0
  29. {explorepy-4.2.0 → explorepy-4.3.0}/docs/authors.rst +0 -0
  30. {explorepy-4.2.0 → explorepy-4.3.0}/docs/changelog.rst +0 -0
  31. {explorepy-4.2.0 → explorepy-4.3.0}/docs/contributing.rst +0 -0
  32. {explorepy-4.2.0 → explorepy-4.3.0}/docs/explore_legacy_devices.rst +0 -0
  33. {explorepy-4.2.0 → explorepy-4.3.0}/docs/index.rst +0 -0
  34. {explorepy-4.2.0 → explorepy-4.3.0}/docs/installation.rst +0 -0
  35. {explorepy-4.2.0 → explorepy-4.3.0}/docs/logo.jpg +0 -0
  36. {explorepy-4.2.0 → explorepy-4.3.0}/docs/readme.rst +0 -0
  37. {explorepy-4.2.0 → explorepy-4.3.0}/docs/reference/explorepy.rst +0 -0
  38. {explorepy-4.2.0 → explorepy-4.3.0}/docs/reference/index.rst +0 -0
  39. {explorepy-4.2.0 → explorepy-4.3.0}/docs/requirements.txt +0 -0
  40. {explorepy-4.2.0 → explorepy-4.3.0}/docs/spelling_wordlist.txt +0 -0
  41. {explorepy-4.2.0 → explorepy-4.3.0}/setup.cfg +0 -0
  42. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/_exceptions.py +0 -0
  43. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/bt_mock_client.py +0 -0
  44. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/bt_mock_server.py +0 -0
  45. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/debug.py +0 -0
  46. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/filters.py +0 -0
  47. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/log_config.py +0 -0
  48. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy/settings_manager.py +0 -0
  49. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy.egg-info/dependency_links.txt +0 -0
  50. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy.egg-info/entry_points.txt +0 -0
  51. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy.egg-info/requires.txt +0 -0
  52. {explorepy-4.2.0 → explorepy-4.3.0}/src/explorepy.egg-info/top_level.txt +0 -0
  53. {explorepy-4.2.0 → explorepy-4.3.0}/tests/README.md +0 -0
  54. {explorepy-4.2.0 → explorepy-4.3.0}/tests/__init__.py +0 -0
  55. {explorepy-4.2.0 → explorepy-4.3.0}/tests/conftest.py +0 -0
  56. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/calibration_info +0 -0
  57. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/calibration_info_usbc +0 -0
  58. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/cmd_rcv +0 -0
  59. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/cmd_stat +0 -0
  60. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/device_info +0 -0
  61. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/device_info_ble +0 -0
  62. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/device_info_v2 +0 -0
  63. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/device_info_v2_2 +0 -0
  64. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/disconnect +0 -0
  65. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/eeg16_ble +0 -0
  66. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/eeg32 +0 -0
  67. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/eeg94 +0 -0
  68. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/eeg98 +0 -0
  69. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/eeg98_ble +0 -0
  70. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/eeg98_usbc +0 -0
  71. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/eeg98_usbc_2 +0 -0
  72. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/env +0 -0
  73. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/orn +0 -0
  74. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/orn_2 +0 -0
  75. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/orn_matrix.txt +0 -0
  76. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/push_marker +0 -0
  77. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/in/trigger_in +0 -0
  78. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/axis_and_angle.txt +0 -0
  79. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/calibration_info_out.txt +0 -0
  80. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/calibration_info_usbc_out.txt +0 -0
  81. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/cmd_rcv_out.txt +0 -0
  82. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/cmd_stat_out.txt +0 -0
  83. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/device_info_ble_out.txt +0 -0
  84. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/device_info_out.txt +0 -0
  85. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/device_info_v2_2_out.txt +0 -0
  86. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/device_info_v2_out.txt +0 -0
  87. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/disconnect_out.txt +0 -0
  88. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/eeg16_ble_out.txt +0 -0
  89. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/eeg32_out.txt +0 -0
  90. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/eeg94_out.txt +0 -0
  91. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/eeg98_ble_out.txt +0 -0
  92. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/eeg98_out.txt +0 -0
  93. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/eeg98_out_fake.txt +0 -0
  94. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/eeg98_usbc_out.txt +0 -0
  95. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/eeg98_usbc_out_2.txt +0 -0
  96. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/env_out.txt +0 -0
  97. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/orn_2_out.txt +0 -0
  98. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/orn_out.txt +0 -0
  99. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/push_marker_out.txt +0 -0
  100. {explorepy-4.2.0 → explorepy-4.3.0}/tests/res/out/trigger_in_out.txt +0 -0
  101. {explorepy-4.2.0 → explorepy-4.3.0}/tests/test_packet.py +0 -0
  102. {explorepy-4.2.0 → explorepy-4.3.0}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 4.2.0
2
+ current_version = 4.3.0
3
3
  commit = False
4
4
  tag = False
5
5
 
@@ -2,6 +2,13 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ 4.3.0 (10.9.2025)
6
+ ------------------
7
+ * Live impedance acquisition and recording
8
+ * Improve parser efficiency
9
+ * Bugfix for Linux release
10
+ * Refactor codebase
11
+
5
12
  4.2.0 (15.7.2025)
6
13
  ------------------
7
14
  * Add ExplorePy and Arduino interfacing example
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: explorepy
3
- Version: 4.2.0
3
+ Version: 4.3.0
4
4
  Author-email: MentaLab Hub <support@mentab.org>
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/Mentalab-hub/explorepy
@@ -62,9 +62,9 @@ Dynamic: license-file
62
62
  :target: https://pypi.org/project/explorepy
63
63
 
64
64
 
65
- .. |commits-since| image:: https://img.shields.io/github/commits-since/Mentalab-hub/explorepy/v4.2.0.svg
65
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/Mentalab-hub/explorepy/v4.3.0.svg
66
66
  :alt: Commits since latest release
67
- :target: https://github.com/Mentalab-hub/explorepy/compare/v4.2.0...master
67
+ :target: https://github.com/Mentalab-hub/explorepy/compare/v4.3.0...master
68
68
 
69
69
 
70
70
  .. |wheel| image:: https://img.shields.io/pypi/wheel/explorepy.svg
@@ -17,9 +17,9 @@
17
17
  :target: https://pypi.org/project/explorepy
18
18
 
19
19
 
20
- .. |commits-since| image:: https://img.shields.io/github/commits-since/Mentalab-hub/explorepy/v4.2.0.svg
20
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/Mentalab-hub/explorepy/v4.3.0.svg
21
21
  :alt: Commits since latest release
22
- :target: https://github.com/Mentalab-hub/explorepy/compare/v4.2.0...master
22
+ :target: https://github.com/Mentalab-hub/explorepy/compare/v4.3.0...master
23
23
 
24
24
 
25
25
  .. |wheel| image:: https://img.shields.io/pypi/wheel/explorepy.svg
@@ -28,7 +28,7 @@ project = 'explorepy'
28
28
  year = '2018-2025'
29
29
  author = 'Mentalab GmbH.'
30
30
  copyright = '{0}, {1}'.format(year, author)
31
- version = release = '4.2.0'
31
+ version = release = '4.3.0'
32
32
  pygments_style = 'trac'
33
33
  templates_path = ['.']
34
34
  extlinks = {
@@ -55,6 +55,7 @@ Connects to a device and records ExG and orientation data into two separate file
55
55
  -d, --duration <integer> Recording duration in seconds
56
56
  --edf Write in EDF file
57
57
  --csv Write in csv file (default type)
58
+ --imp-mode Enable impedance mode with live monitoring
58
59
  -h, --help Show this message and exit.
59
60
 
60
61
 
@@ -222,6 +223,10 @@ You can record data in realtime to EDF (BDF+) or CSV files using:
222
223
  ::
223
224
  explore.record_data(file_name='test', duration=120, file_type='csv')
224
225
 
226
+ To also record impedance data, use:
227
+ ::
228
+ explore.record_data(file_name='test', duration=120, file_type='csv', imp_mode=True)
229
+
225
230
  This will record data in three separate files: "``test_ExG.csv``", "``test_ORN.csv``" and "``test_marker.csv``", which contain ExG data, orientation data (accelerometer, gyroscope, magnetometer) and event markers respectively. Add command arguments to overwrite files and set the duration of the recording (in seconds).
226
231
  ::
227
232
  explore.record_data(file_name='test', do_overwrite=True, file_type='csv', duration=120)
@@ -376,34 +381,100 @@ Impedance data acquisition in real-time
376
381
  """""""""""""""""""""""""""""""""""""""
377
382
  ::
378
383
 
379
- # An example code for impedance data acquisition from Explore device
380
-
384
+ import argparse
385
+ import csv
381
386
  import time
387
+
388
+ import numpy as np
389
+ import pandas as pd
390
+ import matplotlib.pyplot as plt
391
+ from scipy import signal
392
+
382
393
  import explorepy
383
394
  from explorepy.stream_processor import TOPICS
384
395
 
385
396
 
386
- def handle_imp(packet):
387
- """A function that receives impedance packet values"""
388
- imp_values = packet.get_impedances()
389
- print(imp_values)
397
+ # ----------------------------- Argument Parsing ----------------------------- #
398
+ parser = argparse.ArgumentParser(description="Acquire and filter impedance-mode ExG data from an Explore device.")
399
+ parser.add_argument("--device-name", required=True, help="Name of the Explore device (e.g., Explore_AAXX)")
400
+ args = parser.parse_args()
401
+
402
+
403
+ # ----------------------------- Configuration ----------------------------- #
404
+ FS = 250 # Sampling rate in Hz
405
+ CHANNEL_LABELS = [f"ch{i}" for i in range(1, 33)]
406
+ OUTPUT_FILENAME = "exg_data_imp_mode.csv"
407
+ RECORD_SECONDS = 40
408
+
409
+
410
+ # ----------------------------- CSV Setup ----------------------------- #
411
+ csv_file = open(OUTPUT_FILENAME, 'w', newline='\n')
412
+ csv_writer = csv.writer(csv_file, delimiter=",")
413
+ csv_writer.writerow(['Timestamp'] + CHANNEL_LABELS[:8]) # Log only first 8 channels
414
+
390
415
 
416
+ # ----------------------------- Packet Handlers ----------------------------- #
417
+ def handle_exg_packet(packet):
418
+ """Callback to handle incoming ExG data packets."""
419
+ timestamps, signals = packet.get_data(FS)
420
+ data = np.concatenate((np.array(timestamps)[:, np.newaxis].T, np.array(signals)), axis=0)
421
+ np.savetxt(csv_file, np.round(data.T, 4), fmt='%4f', delimiter=',')
391
422
 
392
- exp_device = explorepy.Explore()
393
423
 
394
- # Connect to the Explore device using device bluetooth name or mac address
395
- exp_device.connect('Explore_XXXX')
424
+ def handle_impedance_packet(packet):
425
+ """Callback to handle incoming impedance packets."""
426
+ impedance_values = packet.get_impedances()
427
+ print("Impedance:", impedance_values)
396
428
 
397
- exp_device.stream_processor.subscribe(callback=handle_imp, topic=TOPICS.imp)
398
429
 
399
- # enable impedance mode
400
- exp_device.stream_processor.imp_initialize(notch_freq=50)
430
+ # ----------------------------- Device Initialization ----------------------------- #
431
+ device = explorepy.Explore()
432
+ device.connect(args.device_name)
433
+ device.stream_processor.subscribe(callback=handle_impedance_packet, topic=TOPICS.imp)
434
+ device.stream_processor.subscribe(callback=handle_exg_packet, topic=TOPICS.raw_ExG)
435
+ device.stream_processor.imp_initialize(notch_freq=50)
401
436
 
402
- count = 0
403
- while count < 15:
437
+
438
+ # ----------------------------- Data Acquisition Loop ----------------------------- #
439
+ for _ in range(RECORD_SECONDS):
404
440
  time.sleep(1)
405
- count += 1
406
441
 
407
- exp_device.stream_processor.disable_imp()
408
- exp_device.stream_processor.unsubscribe(callback=handle_imp, topic=TOPICS.imp)
442
+
443
+ # ----------------------------- Cleanup ----------------------------- #
444
+ device.stream_processor.disable_imp()
445
+ device.stream_processor.unsubscribe(callback=handle_impedance_packet, topic=TOPICS.imp)
446
+ device.stream_processor.unsubscribe(callback=handle_exg_packet, topic=TOPICS.raw_ExG)
447
+ csv_file.close()
448
+
449
+
450
+ # ----------------------------- Signal Processing ----------------------------- #
451
+ def apply_bandpass_filter(signal_data, low_freq, high_freq, fs, order=3):
452
+ """Apply a Butterworth bandpass filter to the signal."""
453
+ b, a = signal.butter(order, [low_freq / fs, high_freq / fs], btype='bandpass')
454
+ return signal.filtfilt(b, a, signal_data)
455
+
456
+
457
+ def apply_notch_filter(signal_data, fs, freq=50, quality_factor=30.0):
458
+ """Apply a notch filter at the specified frequency."""
459
+ b, a = signal.iirnotch(freq, quality_factor, fs)
460
+ return signal.filtfilt(b, a, signal_data)
461
+
462
+
463
+ # ----------------------------- Load and Filter Data ----------------------------- #
464
+ df = pd.read_csv(OUTPUT_FILENAME, delimiter=',', dtype=np.float64)
465
+ raw_ch1 = df['ch1']
466
+ filtered = apply_notch_filter(raw_ch1, FS, freq=62.5)
467
+ filtered = apply_notch_filter(filtered, FS, freq=50)
468
+ filtered = apply_bandpass_filter(filtered, low_freq=0.5, high_freq=30, fs=FS)
469
+
470
+
471
+ # ----------------------------- Plot Filtered Signal ----------------------------- #
472
+ plt.figure(figsize=(10, 4))
473
+ plt.plot(df['Timestamp'], filtered, label='Filtered ch1')
474
+ plt.xlabel("Time (s)")
475
+ plt.ylabel("Amplitude (µV)")
476
+ plt.title("Filtered EEG Signal - Channel 1")
477
+ plt.grid(True)
478
+ plt.tight_layout()
479
+ plt.show()
409
480
 
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'explorepy'
7
- version = "4.2.0"
7
+ version = "4.3.0"
8
8
  license = { text = "MIT" }
9
9
  readme = { file = "README.rst", content-type = "text/markdown" }
10
10
  authors = [
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import atexit
3
3
  import logging
4
+ import os
4
5
  import threading
5
6
  import time
6
7
  from queue import (
@@ -189,16 +190,20 @@ class BLEClient(BTClient):
189
190
  def disconnect(self):
190
191
  """Disconnect from the device"""
191
192
  self.is_connected = False
192
- self.notify_task.cancel()
193
+ if os.name != 'nt':
194
+ if self.client and self.client.is_connected:
195
+ try:
196
+ asyncio.run(self.client.stop_notify(self.eeg_tx_char_uuid))
197
+ except RuntimeError:
198
+ # nothing to do here, this works even though there is an exception
199
+ pass
200
+ if self.notify_task:
201
+ self.notify_task.cancel()
193
202
  self.read_event.set()
194
203
  time.sleep(1)
195
204
  self.stop_read_loop()
196
205
  self.ble_device = None
197
206
  self.buffer = Queue()
198
- logger.info('ExplorePy disconnecting from device')
199
-
200
- def _find_mac_address(self):
201
- raise NotImplementedError
202
207
 
203
208
  def read(self, n_bytes):
204
209
  """Read n_bytes from the socket
@@ -221,7 +226,8 @@ class BLEClient(BTClient):
221
226
  raise ConnectionAbortedError('Error reading data from BLE stream, too many bytes requested')
222
227
  return ret
223
228
  except Empty:
224
- raise ConnectionAbortedError
229
+ if self.is_connected:
230
+ raise ConnectionAbortedError
225
231
  except Exception as error:
226
232
  logger.error('Unknown error reading data from BLE stream, error is {}'.format(error))
227
233
  raise ConnectionAbortedError(str(error))
@@ -12,7 +12,6 @@ class BTClient(abc.ABC):
12
12
  self.mac_address = mac_address
13
13
  self.device_name = device_name
14
14
  self.bt_serial_port_manager = None
15
- self.device_manager = None
16
15
 
17
16
  @abc.abstractmethod
18
17
  def connect(self):
@@ -34,10 +33,6 @@ class BTClient(abc.ABC):
34
33
  def disconnect(self):
35
34
  """Disconnect from the device"""
36
35
 
37
- @abc.abstractmethod
38
- def _find_mac_address(self):
39
- pass
40
-
41
36
  @abc.abstractmethod
42
37
  def read(self, n_bytes):
43
38
  """Read n_bytes from the socket
@@ -56,7 +51,3 @@ class BTClient(abc.ABC):
56
51
  Args:
57
52
  data (bytearray): Data to be sent
58
53
  """
59
-
60
- @staticmethod
61
- def _check_mac_address(device_name, mac_address):
62
- return (device_name[-4:-2] == mac_address[-5:-3]) and (device_name[-2:] == mac_address[-2:])
@@ -20,7 +20,7 @@ from .explore import Explore # noqa
20
20
 
21
21
 
22
22
  __all__ = ["Explore", "command", "tools", "log_config"]
23
- __version__ = '4.2.0'
23
+ __version__ = '4.3.0'
24
24
 
25
25
  this = sys.modules[__name__]
26
26
  # TODO appropriate library
@@ -12,8 +12,6 @@ import explorepy
12
12
  CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
- default_bt_backend = explorepy.get_bt_interface()
16
-
17
15
 
18
16
  @click.group(context_settings=CONTEXT_SETTINGS, invoke_without_command=True)
19
17
  @click.option("--version", "-V", help="Print explorepy version", is_flag=True)
@@ -77,13 +75,22 @@ def acquire(name, address, duration):
77
75
  @click.option("-d", "--duration", type=int, help="Recording duration in seconds", metavar="<integer>")
78
76
  @click.option("--edf", 'file_type', flag_value='edf', help="Write in EDF file")
79
77
  @click.option("--csv", 'file_type', flag_value='csv', help="Write in csv file (default type)", default=True)
78
+ @click.option("--imp-mode", is_flag=True, help="Enable impedance mode with live monitoring")
79
+ @click.option("-nf", "--notch-freq", help="Notch frequency for impedance mode initialization", type=float, default=50.0)
80
80
  @verify_inputs
81
- def record_data(address, name, filename, overwrite, duration, file_type):
81
+ def record_data(address, name, filename, overwrite, duration, file_type, imp_mode, notch_freq):
82
82
  """Record data from Explore to a file"""
83
83
  explore = explorepy.explore.Explore()
84
84
  explore.connect(mac_address=address, device_name=name)
85
- explore.record_data(file_name=filename, file_type=file_type,
86
- do_overwrite=overwrite, duration=duration, block=True)
85
+ explore.record_data(
86
+ file_name=filename,
87
+ file_type=file_type,
88
+ do_overwrite=overwrite,
89
+ duration=duration,
90
+ imp_mode=imp_mode,
91
+ notch_freq=notch_freq,
92
+ block=True
93
+ )
87
94
 
88
95
 
89
96
  @cli.command()
@@ -153,17 +160,3 @@ def soft_reset(address, name):
153
160
  explore = explorepy.explore.Explore()
154
161
  explore.connect(mac_address=address, device_name=name)
155
162
  explore.reset_soft()
156
-
157
-
158
- @cli.command()
159
- @click.option("--address", "-a", type=str, help="Explore device's MAC address")
160
- @click.option("--name", "-n", type=str, help="Name of the device")
161
- @click.option("-m", "--channel-mask", type=str, required=True,
162
- help="Channel mask, it should be a binary string containing 1 and 0, "
163
- "representing the mask (LSB is channel 1).")
164
- @verify_inputs
165
- def set_channels(address, name, channel_mask):
166
- """Mask the channels of the Explore device"""
167
- explore = explorepy.explore.Explore()
168
- explore.connect(mac_address=address, device_name=name)
169
- explore.set_channels(channel_mask)
@@ -23,23 +23,12 @@ class OpcodeID(Enum):
23
23
  CMD_SPS_SET = b'\xA1'
24
24
  CMD_CH_SET = b'\xA2'
25
25
  CMD_MEM_FORMAT = b'\xA3'
26
- CMD_REC_TIME_SET = b'\xB1'
27
- CMD_MODULE_DISABLE = b'\xA4'
28
- CMD_MODULE_ENABLE = b'\xA5'
29
26
  CMD_ZM_DISABLE = b'\xA6'
30
27
  CMD_ZM_ENABLE = b'\xA7'
31
28
  CMD_SOFT_RESET = b'\xA8'
32
29
  CMD_TEST_SIG = b'\xAA'
33
30
 
34
31
 
35
- class Result(Enum):
36
- """Results of the command execution"""
37
- API_CMD_SUCCESSFUL = b'\x01'
38
- API_CMD_ILLEGAL = b'\x02'
39
- API_CMD_FAILED = b'\x00'
40
- API_CMD_NA = b'\xFF'
41
-
42
-
43
32
  class DeviceConfiguration:
44
33
  """Device Configuration Class"""
45
34
 
@@ -52,10 +41,6 @@ class DeviceConfiguration:
52
41
  self._last_ack_message = None
53
42
  self._last_status_message = None
54
43
 
55
- def get_device_info(self):
56
- """Get device information including adc mask, sampling rate and firmware version."""
57
- raise NotImplementedError
58
-
59
44
  def change_setting(self, command):
60
45
  """Change the settings of the device based on the input command
61
46
 
@@ -147,8 +132,6 @@ class Command:
147
132
  self.opcode = None
148
133
  self.param = None
149
134
  self.fletcher = b'\xaf\xbe\xad\xde'
150
-
151
- self.delivery_state = None
152
135
  self.result = None
153
136
 
154
137
  def translate(self):
@@ -266,26 +249,6 @@ class ZMeasurementEnable(Command2B):
266
249
  return "Impedance measurement enable command"
267
250
 
268
251
 
269
- class SetCh(Command2B):
270
- """Change channel mask command"""
271
-
272
- def __init__(self, ch_mask):
273
- """
274
-
275
- Args:
276
- ch_mask (int): ExG channel mask. It should be integers between 1 and 255.
277
- """
278
- super().__init__()
279
- self.opcode = OpcodeID.CMD_CH_SET
280
- if 1 <= ch_mask <= 255:
281
- self.param = bytes([ch_mask])
282
- else:
283
- raise ValueError("Invalid input")
284
-
285
- def __str__(self):
286
- return "Channel set command"
287
-
288
-
289
252
  class SoftReset(Command2B):
290
253
  """Reset the setting of the device."""
291
254