explorepy 4.2.0__tar.gz → 4.3.1__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.1}/.bumpversion.cfg +1 -1
  2. {explorepy-4.2.0 → explorepy-4.3.1}/CHANGELOG.rst +13 -0
  3. {explorepy-4.2.0 → explorepy-4.3.1}/PKG-INFO +3 -3
  4. {explorepy-4.2.0 → explorepy-4.3.1}/README.rst +2 -2
  5. {explorepy-4.2.0 → explorepy-4.3.1}/docs/conf.py +1 -1
  6. {explorepy-4.2.0 → explorepy-4.3.1}/docs/usage.rst +89 -19
  7. {explorepy-4.2.0 → explorepy-4.3.1}/pyproject.toml +1 -1
  8. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/BLEClient.py +12 -6
  9. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/BTClient.py +0 -9
  10. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/__init__.py +1 -1
  11. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/cli.py +12 -19
  12. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/command.py +0 -37
  13. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/explore.py +178 -40
  14. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/packet.py +42 -37
  15. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/parser.py +49 -37
  16. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/serial_client.py +2 -3
  17. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/stream_processor.py +7 -17
  18. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/tools.py +29 -241
  19. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy.egg-info/PKG-INFO +3 -3
  20. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy.egg-info/SOURCES.txt +1 -0
  21. explorepy-4.3.1/tests/integration_test_ble.py +115 -0
  22. {explorepy-4.2.0 → explorepy-4.3.1}/.cookiecutterrc +0 -0
  23. {explorepy-4.2.0 → explorepy-4.3.1}/.coveragerc +0 -0
  24. {explorepy-4.2.0 → explorepy-4.3.1}/.editorconfig +0 -0
  25. {explorepy-4.2.0 → explorepy-4.3.1}/AUTHORS.rst +0 -0
  26. {explorepy-4.2.0 → explorepy-4.3.1}/CONTRIBUTING.rst +0 -0
  27. {explorepy-4.2.0 → explorepy-4.3.1}/LICENSE +0 -0
  28. {explorepy-4.2.0 → explorepy-4.3.1}/MANIFEST.in +0 -0
  29. {explorepy-4.2.0 → explorepy-4.3.1}/docs/authors.rst +0 -0
  30. {explorepy-4.2.0 → explorepy-4.3.1}/docs/changelog.rst +0 -0
  31. {explorepy-4.2.0 → explorepy-4.3.1}/docs/contributing.rst +0 -0
  32. {explorepy-4.2.0 → explorepy-4.3.1}/docs/explore_legacy_devices.rst +0 -0
  33. {explorepy-4.2.0 → explorepy-4.3.1}/docs/index.rst +0 -0
  34. {explorepy-4.2.0 → explorepy-4.3.1}/docs/installation.rst +0 -0
  35. {explorepy-4.2.0 → explorepy-4.3.1}/docs/logo.jpg +0 -0
  36. {explorepy-4.2.0 → explorepy-4.3.1}/docs/readme.rst +0 -0
  37. {explorepy-4.2.0 → explorepy-4.3.1}/docs/reference/explorepy.rst +0 -0
  38. {explorepy-4.2.0 → explorepy-4.3.1}/docs/reference/index.rst +0 -0
  39. {explorepy-4.2.0 → explorepy-4.3.1}/docs/requirements.txt +0 -0
  40. {explorepy-4.2.0 → explorepy-4.3.1}/docs/spelling_wordlist.txt +0 -0
  41. {explorepy-4.2.0 → explorepy-4.3.1}/setup.cfg +0 -0
  42. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/_exceptions.py +0 -0
  43. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/bt_mock_client.py +0 -0
  44. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/bt_mock_server.py +0 -0
  45. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/debug.py +0 -0
  46. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/filters.py +0 -0
  47. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/log_config.py +0 -0
  48. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy/settings_manager.py +0 -0
  49. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy.egg-info/dependency_links.txt +0 -0
  50. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy.egg-info/entry_points.txt +0 -0
  51. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy.egg-info/requires.txt +0 -0
  52. {explorepy-4.2.0 → explorepy-4.3.1}/src/explorepy.egg-info/top_level.txt +0 -0
  53. {explorepy-4.2.0 → explorepy-4.3.1}/tests/README.md +0 -0
  54. {explorepy-4.2.0 → explorepy-4.3.1}/tests/__init__.py +0 -0
  55. {explorepy-4.2.0 → explorepy-4.3.1}/tests/conftest.py +0 -0
  56. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/calibration_info +0 -0
  57. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/calibration_info_usbc +0 -0
  58. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/cmd_rcv +0 -0
  59. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/cmd_stat +0 -0
  60. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/device_info +0 -0
  61. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/device_info_ble +0 -0
  62. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/device_info_v2 +0 -0
  63. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/device_info_v2_2 +0 -0
  64. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/disconnect +0 -0
  65. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/eeg16_ble +0 -0
  66. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/eeg32 +0 -0
  67. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/eeg94 +0 -0
  68. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/eeg98 +0 -0
  69. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/eeg98_ble +0 -0
  70. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/eeg98_usbc +0 -0
  71. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/eeg98_usbc_2 +0 -0
  72. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/env +0 -0
  73. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/orn +0 -0
  74. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/orn_2 +0 -0
  75. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/orn_matrix.txt +0 -0
  76. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/push_marker +0 -0
  77. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/in/trigger_in +0 -0
  78. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/axis_and_angle.txt +0 -0
  79. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/calibration_info_out.txt +0 -0
  80. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/calibration_info_usbc_out.txt +0 -0
  81. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/cmd_rcv_out.txt +0 -0
  82. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/cmd_stat_out.txt +0 -0
  83. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/device_info_ble_out.txt +0 -0
  84. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/device_info_out.txt +0 -0
  85. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/device_info_v2_2_out.txt +0 -0
  86. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/device_info_v2_out.txt +0 -0
  87. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/disconnect_out.txt +0 -0
  88. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/eeg16_ble_out.txt +0 -0
  89. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/eeg32_out.txt +0 -0
  90. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/eeg94_out.txt +0 -0
  91. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/eeg98_ble_out.txt +0 -0
  92. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/eeg98_out.txt +0 -0
  93. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/eeg98_out_fake.txt +0 -0
  94. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/eeg98_usbc_out.txt +0 -0
  95. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/eeg98_usbc_out_2.txt +0 -0
  96. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/env_out.txt +0 -0
  97. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/orn_2_out.txt +0 -0
  98. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/orn_out.txt +0 -0
  99. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/push_marker_out.txt +0 -0
  100. {explorepy-4.2.0 → explorepy-4.3.1}/tests/res/out/trigger_in_out.txt +0 -0
  101. {explorepy-4.2.0 → explorepy-4.3.1}/tests/test_packet.py +0 -0
  102. {explorepy-4.2.0 → explorepy-4.3.1}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 4.2.0
2
+ current_version = 4.3.1
3
3
  commit = False
4
4
  tag = False
5
5
 
@@ -2,6 +2,19 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ 4.3.1 (17.9.2025)
6
+ ------------------
7
+ * Bugfix for 32 channel impedance recording
8
+ * Bugfix for BLE connection drop
9
+ * Update binary converter
10
+
11
+ 4.3.0 (10.9.2025)
12
+ ------------------
13
+ * Live impedance acquisition and recording
14
+ * Improve parser efficiency
15
+ * Bugfix for Linux release
16
+ * Refactor codebase
17
+
5
18
  4.2.0 (15.7.2025)
6
19
  ------------------
7
20
  * 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.1
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.1.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.1...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.1.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.1...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.1'
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 real-time monitoring (CSV only)
58
59
  -h, --help Show this message and exit.
59
60
 
60
61
 
@@ -221,10 +222,13 @@ Recording
221
222
  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')
225
+ 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. It is also possible to add arguments to overwrite files.
224
226
 
225
- 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).
227
+ Enable imp_mode argument to record impedance data as well:
226
228
  ::
227
- explore.record_data(file_name='test', do_overwrite=True, file_type='csv', duration=120)
229
+ explore.record_data(file_name='test', duration=120, file_type='csv', imp_mode=True)
230
+
231
+ .. note:: Impedance recording is only supported for CSV files!
228
232
 
229
233
  .. note:: To load EDF files, you can use `pyedflib <https://github.com/holgern/pyedflib>`_ or `mne <https://github.com/mne-tools/mne-python>`_ (for mne, you may need to change the file extension to ``bdf`` manually) in Python.
230
234
 
@@ -376,34 +380,100 @@ Impedance data acquisition in real-time
376
380
  """""""""""""""""""""""""""""""""""""""
377
381
  ::
378
382
 
379
- # An example code for impedance data acquisition from Explore device
380
-
383
+ import argparse
384
+ import csv
381
385
  import time
386
+
387
+ import numpy as np
388
+ import pandas as pd
389
+ import matplotlib.pyplot as plt
390
+ from scipy import signal
391
+
382
392
  import explorepy
383
393
  from explorepy.stream_processor import TOPICS
384
394
 
385
395
 
386
- def handle_imp(packet):
387
- """A function that receives impedance packet values"""
388
- imp_values = packet.get_impedances()
389
- print(imp_values)
396
+ # ----------------------------- Argument Parsing ----------------------------- #
397
+ parser = argparse.ArgumentParser(description="Acquire and filter impedance-mode ExG data from an Explore device.")
398
+ parser.add_argument("--device-name", required=True, help="Name of the Explore device (e.g., Explore_AAXX)")
399
+ args = parser.parse_args()
400
+
401
+
402
+ # ----------------------------- Configuration ----------------------------- #
403
+ FS = 250 # Sampling rate in Hz
404
+ CHANNEL_LABELS = [f"ch{i}" for i in range(1, 33)]
405
+ OUTPUT_FILENAME = "exg_data_imp_mode.csv"
406
+ RECORD_SECONDS = 40
407
+
408
+
409
+ # ----------------------------- CSV Setup ----------------------------- #
410
+ csv_file = open(OUTPUT_FILENAME, 'w', newline='\n')
411
+ csv_writer = csv.writer(csv_file, delimiter=",")
412
+ csv_writer.writerow(['Timestamp'] + CHANNEL_LABELS[:8]) # Log only first 8 channels
413
+
390
414
 
415
+ # ----------------------------- Packet Handlers ----------------------------- #
416
+ def handle_exg_packet(packet):
417
+ """Callback to handle incoming ExG data packets."""
418
+ timestamps, signals = packet.get_data(FS)
419
+ data = np.concatenate((np.array(timestamps)[:, np.newaxis].T, np.array(signals)), axis=0)
420
+ np.savetxt(csv_file, np.round(data.T, 4), fmt='%4f', delimiter=',')
391
421
 
392
- exp_device = explorepy.Explore()
393
422
 
394
- # Connect to the Explore device using device bluetooth name or mac address
395
- exp_device.connect('Explore_XXXX')
423
+ def handle_impedance_packet(packet):
424
+ """Callback to handle incoming impedance packets."""
425
+ impedance_values = packet.get_impedances()
426
+ print("Impedance:", impedance_values)
396
427
 
397
- exp_device.stream_processor.subscribe(callback=handle_imp, topic=TOPICS.imp)
398
428
 
399
- # enable impedance mode
400
- exp_device.stream_processor.imp_initialize(notch_freq=50)
429
+ # ----------------------------- Device Initialization ----------------------------- #
430
+ device = explorepy.Explore()
431
+ device.connect(args.device_name)
432
+ device.stream_processor.subscribe(callback=handle_impedance_packet, topic=TOPICS.imp)
433
+ device.stream_processor.subscribe(callback=handle_exg_packet, topic=TOPICS.raw_ExG)
434
+ device.stream_processor.imp_initialize(notch_freq=50)
401
435
 
402
- count = 0
403
- while count < 15:
436
+
437
+ # ----------------------------- Data Acquisition Loop ----------------------------- #
438
+ for _ in range(RECORD_SECONDS):
404
439
  time.sleep(1)
405
- count += 1
406
440
 
407
- exp_device.stream_processor.disable_imp()
408
- exp_device.stream_processor.unsubscribe(callback=handle_imp, topic=TOPICS.imp)
441
+
442
+ # ----------------------------- Cleanup ----------------------------- #
443
+ device.stream_processor.disable_imp()
444
+ device.stream_processor.unsubscribe(callback=handle_impedance_packet, topic=TOPICS.imp)
445
+ device.stream_processor.unsubscribe(callback=handle_exg_packet, topic=TOPICS.raw_ExG)
446
+ csv_file.close()
447
+
448
+
449
+ # ----------------------------- Signal Processing ----------------------------- #
450
+ def apply_bandpass_filter(signal_data, low_freq, high_freq, fs, order=3):
451
+ """Apply a Butterworth bandpass filter to the signal."""
452
+ b, a = signal.butter(order, [low_freq / fs, high_freq / fs], btype='bandpass')
453
+ return signal.filtfilt(b, a, signal_data)
454
+
455
+
456
+ def apply_notch_filter(signal_data, fs, freq=50, quality_factor=30.0):
457
+ """Apply a notch filter at the specified frequency."""
458
+ b, a = signal.iirnotch(freq, quality_factor, fs)
459
+ return signal.filtfilt(b, a, signal_data)
460
+
461
+
462
+ # ----------------------------- Load and Filter Data ----------------------------- #
463
+ df = pd.read_csv(OUTPUT_FILENAME, delimiter=',', dtype=np.float64)
464
+ raw_ch1 = df['ch1']
465
+ filtered = apply_notch_filter(raw_ch1, FS, freq=62.5)
466
+ filtered = apply_notch_filter(filtered, FS, freq=50)
467
+ filtered = apply_bandpass_filter(filtered, low_freq=0.5, high_freq=30, fs=FS)
468
+
469
+
470
+ # ----------------------------- Plot Filtered Signal ----------------------------- #
471
+ plt.figure(figsize=(10, 4))
472
+ plt.plot(df['Timestamp'], filtered, label='Filtered ch1')
473
+ plt.xlabel("Time (s)")
474
+ plt.ylabel("Amplitude (µV)")
475
+ plt.title("Filtered EEG Signal - Channel 1")
476
+ plt.grid(True)
477
+ plt.tight_layout()
478
+ plt.show()
409
479
 
@@ -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.1"
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.1'
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=None)
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