explorepy 4.1.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.1.0 → explorepy-4.3.0}/.bumpversion.cfg +1 -1
  2. {explorepy-4.1.0 → explorepy-4.3.0}/CHANGELOG.rst +16 -0
  3. {explorepy-4.1.0 → explorepy-4.3.0}/PKG-INFO +9 -23
  4. {explorepy-4.1.0 → explorepy-4.3.0}/README.rst +8 -22
  5. {explorepy-4.1.0 → explorepy-4.3.0}/docs/conf.py +1 -1
  6. {explorepy-4.1.0 → explorepy-4.3.0}/docs/installation.rst +4 -17
  7. {explorepy-4.1.0 → explorepy-4.3.0}/docs/requirements.txt +1 -1
  8. {explorepy-4.1.0 → explorepy-4.3.0}/docs/usage.rst +88 -49
  9. {explorepy-4.1.0 → explorepy-4.3.0}/pyproject.toml +1 -1
  10. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/BLEClient.py +12 -6
  11. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/BTClient.py +0 -9
  12. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/__init__.py +1 -1
  13. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/cli.py +12 -62
  14. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/command.py +0 -80
  15. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/explore.py +162 -111
  16. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/packet.py +42 -37
  17. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/parser.py +44 -30
  18. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/serial_client.py +3 -5
  19. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/stream_processor.py +8 -37
  20. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/tools.py +45 -456
  21. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy.egg-info/PKG-INFO +9 -23
  22. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy.egg-info/SOURCES.txt +1 -0
  23. explorepy-4.3.0/tests/integration_test_ble.py +115 -0
  24. {explorepy-4.1.0 → explorepy-4.3.0}/.cookiecutterrc +0 -0
  25. {explorepy-4.1.0 → explorepy-4.3.0}/.coveragerc +0 -0
  26. {explorepy-4.1.0 → explorepy-4.3.0}/.editorconfig +0 -0
  27. {explorepy-4.1.0 → explorepy-4.3.0}/AUTHORS.rst +0 -0
  28. {explorepy-4.1.0 → explorepy-4.3.0}/CONTRIBUTING.rst +0 -0
  29. {explorepy-4.1.0 → explorepy-4.3.0}/LICENSE +0 -0
  30. {explorepy-4.1.0 → explorepy-4.3.0}/MANIFEST.in +0 -0
  31. {explorepy-4.1.0 → explorepy-4.3.0}/docs/authors.rst +0 -0
  32. {explorepy-4.1.0 → explorepy-4.3.0}/docs/changelog.rst +0 -0
  33. {explorepy-4.1.0 → explorepy-4.3.0}/docs/contributing.rst +0 -0
  34. {explorepy-4.1.0 → explorepy-4.3.0}/docs/explore_legacy_devices.rst +0 -0
  35. {explorepy-4.1.0 → explorepy-4.3.0}/docs/index.rst +0 -0
  36. {explorepy-4.1.0 → explorepy-4.3.0}/docs/logo.jpg +0 -0
  37. {explorepy-4.1.0 → explorepy-4.3.0}/docs/readme.rst +0 -0
  38. {explorepy-4.1.0 → explorepy-4.3.0}/docs/reference/explorepy.rst +0 -0
  39. {explorepy-4.1.0 → explorepy-4.3.0}/docs/reference/index.rst +0 -0
  40. {explorepy-4.1.0 → explorepy-4.3.0}/docs/spelling_wordlist.txt +0 -0
  41. {explorepy-4.1.0 → explorepy-4.3.0}/setup.cfg +0 -0
  42. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/_exceptions.py +0 -0
  43. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/bt_mock_client.py +0 -0
  44. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/bt_mock_server.py +0 -0
  45. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/debug.py +0 -0
  46. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/filters.py +0 -0
  47. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/log_config.py +0 -0
  48. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy/settings_manager.py +0 -0
  49. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy.egg-info/dependency_links.txt +0 -0
  50. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy.egg-info/entry_points.txt +0 -0
  51. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy.egg-info/requires.txt +0 -0
  52. {explorepy-4.1.0 → explorepy-4.3.0}/src/explorepy.egg-info/top_level.txt +0 -0
  53. {explorepy-4.1.0 → explorepy-4.3.0}/tests/README.md +0 -0
  54. {explorepy-4.1.0 → explorepy-4.3.0}/tests/__init__.py +0 -0
  55. {explorepy-4.1.0 → explorepy-4.3.0}/tests/conftest.py +0 -0
  56. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/calibration_info +0 -0
  57. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/calibration_info_usbc +0 -0
  58. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/cmd_rcv +0 -0
  59. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/cmd_stat +0 -0
  60. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/device_info +0 -0
  61. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/device_info_ble +0 -0
  62. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/device_info_v2 +0 -0
  63. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/device_info_v2_2 +0 -0
  64. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/disconnect +0 -0
  65. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/eeg16_ble +0 -0
  66. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/eeg32 +0 -0
  67. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/eeg94 +0 -0
  68. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/eeg98 +0 -0
  69. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/eeg98_ble +0 -0
  70. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/eeg98_usbc +0 -0
  71. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/eeg98_usbc_2 +0 -0
  72. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/env +0 -0
  73. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/orn +0 -0
  74. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/orn_2 +0 -0
  75. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/orn_matrix.txt +0 -0
  76. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/push_marker +0 -0
  77. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/in/trigger_in +0 -0
  78. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/axis_and_angle.txt +0 -0
  79. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/calibration_info_out.txt +0 -0
  80. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/calibration_info_usbc_out.txt +0 -0
  81. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/cmd_rcv_out.txt +0 -0
  82. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/cmd_stat_out.txt +0 -0
  83. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/device_info_ble_out.txt +0 -0
  84. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/device_info_out.txt +0 -0
  85. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/device_info_v2_2_out.txt +0 -0
  86. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/device_info_v2_out.txt +0 -0
  87. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/disconnect_out.txt +0 -0
  88. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/eeg16_ble_out.txt +0 -0
  89. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/eeg32_out.txt +0 -0
  90. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/eeg94_out.txt +0 -0
  91. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/eeg98_ble_out.txt +0 -0
  92. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/eeg98_out.txt +0 -0
  93. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/eeg98_out_fake.txt +0 -0
  94. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/eeg98_usbc_out.txt +0 -0
  95. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/eeg98_usbc_out_2.txt +0 -0
  96. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/env_out.txt +0 -0
  97. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/orn_2_out.txt +0 -0
  98. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/orn_out.txt +0 -0
  99. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/push_marker_out.txt +0 -0
  100. {explorepy-4.1.0 → explorepy-4.3.0}/tests/res/out/trigger_in_out.txt +0 -0
  101. {explorepy-4.1.0 → explorepy-4.3.0}/tests/test_packet.py +0 -0
  102. {explorepy-4.1.0 → explorepy-4.3.0}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 4.1.0
2
+ current_version = 4.3.0
3
3
  commit = False
4
4
  tag = False
5
5
 
@@ -2,6 +2,22 @@
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
+
12
+ 4.2.0 (15.7.2025)
13
+ ------------------
14
+ * Add ExplorePy and Arduino interfacing example
15
+ * Add fieldtrip integration example
16
+ * Add live exg and impedance acquisition example
17
+ * Update MNE integration
18
+ * Remove deprecated functions
19
+ * Update documentation
20
+
5
21
  4.1.0 (31.3.2025)
6
22
  ------------------
7
23
  * Improve bluetooth connection UX
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: explorepy
3
- Version: 4.1.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
@@ -43,7 +43,7 @@ Requires-Dist: flake8==4.0.1; extra == "test"
43
43
  Requires-Dist: isort==5.10.1; extra == "test"
44
44
  Dynamic: license-file
45
45
 
46
- .. image:: logo.png
46
+ .. image:: https://raw.githubusercontent.com/Mentalab-hub/explorepy/master/docs/logo.jpg
47
47
  :scale: 100 %
48
48
  :align: left
49
49
 
@@ -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.1.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.1.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
@@ -82,6 +82,7 @@ Dynamic: license-file
82
82
 
83
83
  .. end-badges
84
84
 
85
+
85
86
  =========================
86
87
  ``explorepy`` overview
87
88
  =========================
@@ -89,18 +90,11 @@ Dynamic: license-file
89
90
  ``explorepy`` is an open-source Python API designed to collect and process ExG data using Mentalab's Explore device. Amongst other things, ``explorepy`` provides the following features:
90
91
 
91
92
  * Real-time streaming of ExG, orientation and environmental data.
92
- * Real-time visualization of ExG, orientation and environmental data.
93
93
  * Data recording in CSV and BDF+ formats.
94
94
  * Integration with LabStreaming Layer.
95
95
  * Electrode impedance measurements.
96
96
  * Explore device configuration.
97
97
 
98
- Quick installation
99
- ==================
100
- For Windows users, the best way to install ``explorepy`` is to download the latest ``explorepy`` version from the `release page <https://github.com/Mentalab-hub/explorepy/releases>`_. Please note that dependencies will install automatically from the release page.
101
-
102
- For other operating systems, or to build the package manually on Windows, please refer to the information below.
103
-
104
98
 
105
99
  Requirements
106
100
  ------------
@@ -121,7 +115,7 @@ To install ``explorepy`` from PyPI run:
121
115
  To install the latest development version (git must be installed before running this command):
122
116
  ::
123
117
 
124
- pip install git+https://github.com/Mentalab-hub/explorepy
118
+ pip install git+https://github.com/Mentalab-hub/explorepy.git@develop
125
119
 
126
120
 
127
121
  Get started
@@ -147,17 +141,9 @@ In Python you can connect to the Explore device and print data using:
147
141
 
148
142
  import explorepy
149
143
  explorer = explorepy.Explore()
150
- explorer.connect(device_name="Explore_XXXX") # Put your device Bluetooth name
144
+ explorer.connect(device_name="Explore_XXXX") # Use correct device ID here
151
145
  explorer.acquire()
152
146
 
153
- You can also visualize the data in real-time.
154
-
155
- ::
156
-
157
- import explorepy
158
- explorer = explorepy.Explore()
159
- explorer.connect(device_name="Explore_XXXX") # Put your device Bluetooth name
160
-
161
147
  Documentation
162
148
  =============
163
149
 
@@ -185,7 +171,7 @@ Authors
185
171
  - `Sonja Stefani`_
186
172
  - `Alex Platt`_
187
173
  - `Andreas Gutsche`_
188
- - `Masooma Fazelian`_
174
+ - `Masoome Fazelian`_
189
175
  - `Philipp Jakovleski`_
190
176
  - `Florian Sesser`_
191
177
  - `Sebastian Herberger`_
@@ -197,7 +183,7 @@ Authors
197
183
  .. _Sonja Stefani: https://github.com/SonjaSt
198
184
  .. _Alex Platt: https://github.com/Nujanauss
199
185
  .. _Andreas Gutsche: https://github.com/andyman410
200
- .. _Masooma Fazelian: https://github.com/fazelian
186
+ .. _Masoome Fazelian: https://github.com/fazelian
201
187
  .. _Philipp Jakovleski: https://github.com/philippjak
202
188
  .. _Florian Sesser : https://github.com/hacklschorsch
203
189
  .. _Sebastian Herberger: https://github.com/SHerberger
@@ -1,4 +1,4 @@
1
- .. image:: logo.png
1
+ .. image:: https://raw.githubusercontent.com/Mentalab-hub/explorepy/master/docs/logo.jpg
2
2
  :scale: 100 %
3
3
  :align: left
4
4
 
@@ -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.1.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.1.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
@@ -37,6 +37,7 @@
37
37
 
38
38
  .. end-badges
39
39
 
40
+
40
41
  =========================
41
42
  ``explorepy`` overview
42
43
  =========================
@@ -44,18 +45,11 @@
44
45
  ``explorepy`` is an open-source Python API designed to collect and process ExG data using Mentalab's Explore device. Amongst other things, ``explorepy`` provides the following features:
45
46
 
46
47
  * Real-time streaming of ExG, orientation and environmental data.
47
- * Real-time visualization of ExG, orientation and environmental data.
48
48
  * Data recording in CSV and BDF+ formats.
49
49
  * Integration with LabStreaming Layer.
50
50
  * Electrode impedance measurements.
51
51
  * Explore device configuration.
52
52
 
53
- Quick installation
54
- ==================
55
- For Windows users, the best way to install ``explorepy`` is to download the latest ``explorepy`` version from the `release page <https://github.com/Mentalab-hub/explorepy/releases>`_. Please note that dependencies will install automatically from the release page.
56
-
57
- For other operating systems, or to build the package manually on Windows, please refer to the information below.
58
-
59
53
 
60
54
  Requirements
61
55
  ------------
@@ -76,7 +70,7 @@ To install ``explorepy`` from PyPI run:
76
70
  To install the latest development version (git must be installed before running this command):
77
71
  ::
78
72
 
79
- pip install git+https://github.com/Mentalab-hub/explorepy
73
+ pip install git+https://github.com/Mentalab-hub/explorepy.git@develop
80
74
 
81
75
 
82
76
  Get started
@@ -102,17 +96,9 @@ In Python you can connect to the Explore device and print data using:
102
96
 
103
97
  import explorepy
104
98
  explorer = explorepy.Explore()
105
- explorer.connect(device_name="Explore_XXXX") # Put your device Bluetooth name
99
+ explorer.connect(device_name="Explore_XXXX") # Use correct device ID here
106
100
  explorer.acquire()
107
101
 
108
- You can also visualize the data in real-time.
109
-
110
- ::
111
-
112
- import explorepy
113
- explorer = explorepy.Explore()
114
- explorer.connect(device_name="Explore_XXXX") # Put your device Bluetooth name
115
-
116
102
  Documentation
117
103
  =============
118
104
 
@@ -140,7 +126,7 @@ Authors
140
126
  - `Sonja Stefani`_
141
127
  - `Alex Platt`_
142
128
  - `Andreas Gutsche`_
143
- - `Masooma Fazelian`_
129
+ - `Masoome Fazelian`_
144
130
  - `Philipp Jakovleski`_
145
131
  - `Florian Sesser`_
146
132
  - `Sebastian Herberger`_
@@ -152,7 +138,7 @@ Authors
152
138
  .. _Sonja Stefani: https://github.com/SonjaSt
153
139
  .. _Alex Platt: https://github.com/Nujanauss
154
140
  .. _Andreas Gutsche: https://github.com/andyman410
155
- .. _Masooma Fazelian: https://github.com/fazelian
141
+ .. _Masoome Fazelian: https://github.com/fazelian
156
142
  .. _Philipp Jakovleski: https://github.com/philippjak
157
143
  .. _Florian Sesser : https://github.com/hacklschorsch
158
144
  .. _Sebastian Herberger: https://github.com/SHerberger
@@ -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.1.0'
31
+ version = release = '4.3.0'
32
32
  pygments_style = 'trac'
33
33
  templates_path = ['.']
34
34
  extlinks = {
@@ -19,25 +19,12 @@ Recommended Requirements
19
19
  How to install
20
20
  --------------
21
21
 
22
- Option 1: Installing via installer file (basic)
23
- """""""""""""""""""""""""""""""""""""""""""""""
24
-
25
- Windows and Mac
26
- ^^^^^^^^^^^^^^^
27
-
28
- *This option is best for users who only intend to use functionalities offered by* ``explorepy`` *via a graphical user interface*
29
-
30
- For example, if you want to quickly visualize and record data and don't need the command line interface or to use it in your own Python script, use this option.
31
-
32
- If you intend to call ``explorepy`` from the command line or a Python script (e.g. from an experiment script), install ``explorepy`` via Anaconda/pip instead.
33
-
34
- For Windows and Mac, the standalone desktop software ExploreDesktop can be installed using the installer files uploaded to the
35
- `release page <https://github.com/Mentalab-hub/explore-desktop-release/releases/latest/>`_. Please note that the dependencies will be installed automatically and bundled locally with the installed software.
36
-
37
-
38
- Option 2: Installing from Python Package Index (PyPI) and pip (advanced)
22
+ Installing from Python Package Index (PyPI)
39
23
  """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
40
24
 
25
+ .. note::
26
+ For Windows, Mac and Linux the standalone desktop software ExploreDesktop can be installed using the installer files uploaded to the
27
+ `release page <https://github.com/Mentalab-hub/explore-desktop-release/releases/latest/>`_.
41
28
 
42
29
  .. note::
43
30
 
@@ -8,7 +8,7 @@ pylsl==1.16.1
8
8
  PyYAML==6.0.1
9
9
  PyYAML==6.0.1
10
10
  scipy==1.11.1
11
- tornado==6.4.2
11
+ tornado==6.5.1
12
12
  sentry_sdk==2.8.0
13
13
  sphinx-rtd-theme==1.3.0
14
14
  sphinx==7.2.4
@@ -27,14 +27,6 @@ For example to get help about the visualize command, run: ``explorepy push2lsl -
27
27
  Available Commands
28
28
  """"""""""""""""""
29
29
 
30
- find-device
31
- %%%%
32
- Scans for nearby Mentalab Explore devices. Prints out the Name and MAC address of found devices.
33
-
34
- Options:
35
- -h, --help Show this message and exit.
36
-
37
-
38
30
  acquire
39
31
  %%%%
40
32
  Connects to a device with selected name or address. Only one input is necessary.
@@ -63,6 +55,7 @@ Connects to a device and records ExG and orientation data into two separate file
63
55
  -d, --duration <integer> Recording duration in seconds
64
56
  --edf Write in EDF file
65
57
  --csv Write in csv file (default type)
58
+ --imp-mode Enable impedance mode with live monitoring
66
59
  -h, --help Show this message and exit.
67
60
 
68
61
 
@@ -135,18 +128,6 @@ Example (overwrite):
135
128
  ::
136
129
  explorepy bin2edf -f input_file.BIN -ow
137
130
 
138
- calibrate-orn
139
- %%%%
140
-
141
- Calibrates the orientation module of a device. This module stores calibration parameters in ``explorepy``'s configuration file. Once calibrated, ``explorepy`` computes the device's orientation (degree and rotation axis).
142
- ::
143
-
144
- Options:
145
- -a, --address TEXT Explore device's MAC address
146
- -n, --name TEXT Name of the device
147
- -ow, --overwrite Overwrite existing file
148
- -h, --help Show this message and exit.
149
-
150
131
 
151
132
  format-memory
152
133
  %%%%
@@ -181,18 +162,6 @@ Example:
181
162
  ::
182
163
  explorepy set-sampling-rate -n Explore_XXXX -sr 500
183
164
 
184
- disable-module
185
- %%%%
186
-
187
- Disables a device module (orientation, environment and ExG).
188
- ::
189
-
190
- Options:
191
- -a, --address TEXT Explore device's MAC address
192
- -n, --name TEXT Name of the device
193
- -m, --module TEXT Module name to be disabled, options: ORN, ENV, EXG
194
- [required]
195
-
196
165
 
197
166
  soft-reset
198
167
  %%%%
@@ -254,6 +223,10 @@ You can record data in realtime to EDF (BDF+) or CSV files using:
254
223
  ::
255
224
  explore.record_data(file_name='test', duration=120, file_type='csv')
256
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
+
257
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).
258
231
  ::
259
232
  explore.record_data(file_name='test', do_overwrite=True, file_type='csv', duration=120)
@@ -408,34 +381,100 @@ Impedance data acquisition in real-time
408
381
  """""""""""""""""""""""""""""""""""""""
409
382
  ::
410
383
 
411
- # An example code for impedance data acquisition from Explore device
412
-
384
+ import argparse
385
+ import csv
413
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
+
414
393
  import explorepy
415
394
  from explorepy.stream_processor import TOPICS
416
395
 
417
396
 
418
- def handle_imp(packet):
419
- """A function that receives impedance packet values"""
420
- imp_values = packet.get_impedances()
421
- 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
+
422
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=',')
423
422
 
424
- exp_device = explorepy.Explore()
425
423
 
426
- # Connect to the Explore device using device bluetooth name or mac address
427
- 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)
428
428
 
429
- exp_device.stream_processor.subscribe(callback=handle_imp, topic=TOPICS.imp)
430
429
 
431
- # enable impedance mode
432
- 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)
433
436
 
434
- count = 0
435
- while count < 15:
437
+
438
+ # ----------------------------- Data Acquisition Loop ----------------------------- #
439
+ for _ in range(RECORD_SECONDS):
436
440
  time.sleep(1)
437
- count += 1
438
441
 
439
- exp_device.stream_processor.disable_imp()
440
- 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()
441
480
 
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'explorepy'
7
- version = "4.1.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.1.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)
@@ -56,13 +54,6 @@ def verify_inputs(func):
56
54
  return update_wrapper(wrapper, func)
57
55
 
58
56
 
59
- @cli.command()
60
- def find_device():
61
- """List available Explore devices"""
62
- explorepy.set_bt_interface(default_bt_backend)
63
- explorepy.tools.run_ble_scanner()
64
-
65
-
66
57
  @cli.command()
67
58
  @click.option("--address", "-a", type=str, help="Explore device's MAC address")
68
59
  @click.option("--name", "-n", type=str, help="Name of the device")
@@ -84,13 +75,22 @@ def acquire(name, address, duration):
84
75
  @click.option("-d", "--duration", type=int, help="Recording duration in seconds", metavar="<integer>")
85
76
  @click.option("--edf", 'file_type', flag_value='edf', help="Write in EDF file")
86
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)
87
80
  @verify_inputs
88
- 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):
89
82
  """Record data from Explore to a file"""
90
83
  explore = explorepy.explore.Explore()
91
84
  explore.connect(mac_address=address, device_name=name)
92
- explore.record_data(file_name=filename, file_type=file_type,
93
- 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
+ )
94
94
 
95
95
 
96
96
  @cli.command()
@@ -160,53 +160,3 @@ def soft_reset(address, name):
160
160
  explore = explorepy.explore.Explore()
161
161
  explore.connect(mac_address=address, device_name=name)
162
162
  explore.reset_soft()
163
-
164
-
165
- @cli.command()
166
- @click.option("--address", "-a", type=str, help="Explore device's MAC address")
167
- @click.option("--name", "-n", type=str, help="Name of the device")
168
- @click.option("-m", "--channel-mask", type=str, required=True,
169
- help="Channel mask, it should be a binary string containing 1 and 0, "
170
- "representing the mask (LSB is channel 1).")
171
- @verify_inputs
172
- def set_channels(address, name, channel_mask):
173
- """Mask the channels of the Explore device"""
174
- explore = explorepy.explore.Explore()
175
- explore.connect(mac_address=address, device_name=name)
176
- explore.set_channels(channel_mask)
177
-
178
-
179
- @cli.command()
180
- @click.option("--address", "-a", type=str, help="Explore device's MAC address")
181
- @click.option("--name", "-n", type=str, help="Name of the device")
182
- @click.option("-m", "--module", required=True, type=str, help="Module name to be disabled, options: ORN, ENV, EXG")
183
- @verify_inputs
184
- def disable_module(address, name, module):
185
- """Disable a module of Explore device"""
186
- explore = explorepy.explore.Explore()
187
- explore.connect(mac_address=address, device_name=name)
188
- explore.disable_module(module)
189
-
190
-
191
- @cli.command()
192
- @click.option("--address", "-a", type=str, help="Explore device's MAC address")
193
- @click.option("--name", "-n", type=str, help="Name of the device")
194
- @click.option("-m", "--module", required=True, type=str, help="Module name to be enabled, options: ORN, ENV, EXG")
195
- @verify_inputs
196
- def enable_module(address, name, module):
197
- """Enable a module of Explore device"""
198
- explore = explorepy.explore.Explore()
199
- explore.connect(mac_address=address, device_name=name)
200
- explore.enable_module(module)
201
-
202
-
203
- @cli.command()
204
- @click.option("--address", "-a", type=str, help="Explore device's MAC address")
205
- @click.option("--name", "-n", type=str, help="Name of the device")
206
- @click.option("-ow", "--overwrite", is_flag=True, help="Overwrite existing file")
207
- @verify_inputs
208
- def calibrate_orn(address, name, overwrite):
209
- """Calibrate the orientation module"""
210
- explore = explorepy.explore.Explore()
211
- explore.connect(mac_address=address, device_name=name)
212
- explore.calibrate_orn(do_overwrite=overwrite)