Skip to content

Commit 3f7fd9b

Browse files
committed
Merge branch 'develop'
2 parents 205b7e3 + 604da11 commit 3f7fd9b

File tree

13 files changed

+189
-50
lines changed

13 files changed

+189
-50
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.8.1
2+
current_version = 1.8.2
33
commit = False
44
tag = False
55

.readthedocs.yaml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Read the Docs configuration file for Sphinx projects
2+
3+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4+
5+
6+
# Required
7+
8+
version: 2
9+
10+
11+
# Set the OS, Python version and other tools you might need
12+
13+
build:
14+
15+
os: ubuntu-22.04
16+
17+
tools:
18+
19+
python: "3.11"
20+
21+
# You can also specify other tool versions:
22+
23+
# nodejs: "20"
24+
25+
# rust: "1.70"
26+
27+
# golang: "1.20"
28+
29+
30+
# Build documentation in the "docs/" directory with Sphinx
31+
32+
sphinx:
33+
34+
configuration: docs/conf.py
35+
36+
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
37+
38+
# builder: "dirhtml"
39+
40+
# Fail on all warnings to avoid broken references
41+
42+
# fail_on_warning: true
43+
44+
45+
# Optionally build your docs in additional formats such as PDF and ePub
46+
47+
# formats:
48+
49+
# - pdf
50+
51+
# - epub
52+
53+
54+
# Optional but recommended, declare the Python requirements required
55+
56+
# to build your documentation
57+
58+
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
59+
60+
python:
61+
62+
install:
63+
64+
- requirements: docs/requirements.txt

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
:target: https://pypi.org/project/explorepy
1818

1919

20-
.. |commits-since| image:: https://img.shields.io/github/commits-since/Mentalab-hub/explorepy/v1.8.1.svg
20+
.. |commits-since| image:: https://img.shields.io/github/commits-since/Mentalab-hub/explorepy/v1.8.2.svg
2121
:alt: Commits since latest release
22-
:target: https://github.com/Mentalab-hub/explorepy/compare/v1.8.1...master
22+
:target: https://github.com/Mentalab-hub/explorepy/compare/v1.8.2...master
2323

2424

2525
.. |wheel| image:: https://img.shields.io/pypi/wheel/explorepy.svg

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
year = '2018-2022'
3030
author = 'Mentalab GmbH.'
3131
copyright = '{0}, {1}'.format(year, author)
32-
version = release = '1.8.1'
32+
version = release = '1.8.2'
3333

3434
pygments_style = 'trac'
3535
templates_path = ['.']

docs/requirements.txt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
sphinx>=1.3
2-
sphinx-rtd-theme
3-
-e .
4-
sphinxcontrib-napoleon
1+
appdirs==1.4.3
2+
bokeh==2.2.3
3+
click==7.1.2
4+
Jinja2==3.0.0
5+
mne==0.24.1
6+
numpy==1.24.4
7+
pandas==1.3.4
8+
pyEDFlib==0.1.34
9+
pylsl==1.16.1
10+
PyYAML==6.0.1
11+
PyYAML==6.0.1
12+
scipy==1.11.1
13+
sentry_sdk==1.19.1
14+
tornado==6.3.2

installer/windows/installer.cfg

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[Application]
22
name=MentaLab ExplorePy
3-
version=1.8.1
3+
version=1.8.2
44
entry_point=explorepy.cli:cli
55
console=true
66
icon=mentalab.ico
77

88
[Python]
9-
version=3.8.0
9+
version=3.9.0
1010
bitness=64
1111

1212

@@ -16,49 +16,54 @@ entry_point=explorepy.cli:cli
1616

1717
[Include]
1818
pypi_wheels =
19-
#explorepy==1.8.1
2019
appdirs==1.4.3
21-
# bokeh==2.2.3 # No wheel available
22-
certifi==2020.12.5
20+
certifi==2023.7.22
21+
charset-normalizer==3.2.0
2322
click==7.1.2
2423
colorama==0.4.6
25-
contourpy==1.0.7
24+
contourpy==1.1.0
2625
cycler==0.11.0
2726
decorator==5.1.1
28-
eeglabio==0.0.2.post3
29-
fonttools==4.38.0
27+
distlib==0.3.7
28+
eeglabio==0.0.2.post4
29+
fonttools==4.42.1
3030
idna==3.4
31+
importlib-resources==6.0.1
3132
Jinja2==3.0.0
32-
kiwisolver==1.4.4
33-
MarkupSafe==2.1.2
34-
matplotlib==3.6.3
35-
mne==1.3.0
36-
numpy==1.21.4
37-
packaging==23.0
38-
pandas==1.5.3
39-
Pillow==9.4.0
40-
pipdeptree==2.3.3
41-
pooch==1.6.0
42-
pyEDFlib==0.1.25
43-
pylsl==1.16.0
33+
kiwisolver==1.4.5
34+
MarkupSafe==2.1.3
35+
matplotlib==3.7.2
36+
mne==1.5.0
37+
numpy==1.25.2
38+
packaging==23.1
39+
pandas==2.0.3
40+
Pillow==10.0.0
41+
pipdeptree==2.13.0
42+
platformdirs==3.10.0
43+
pooch==1.7.0
44+
pyEDFlib==0.1.34
45+
pylsl==1.16.1
46+
pynsist==2.8
4447
pyparsing==3.0.9
4548
python-dateutil==2.8.2
46-
pytz==2022.7.1
47-
PyYAML==6.0
48-
requests==2.28.2
49-
scipy==1.10.0
50-
sentry-sdk==1.0.0
49+
pytz==2023.3
50+
PyYAML==6.0.1
51+
requests==2.31.0
52+
requests_download==0.1.2
53+
scipy==1.11.2
54+
sentry-sdk==1.19.1
5155
six==1.16.0
52-
tornado==6.2
53-
tqdm==4.64.1
54-
typing_extensions==4.4.0
55-
urllib3==1.26.14
56-
wincertstore==0.2
57-
setuptools==67.1.0
56+
tornado==6.3.3
57+
tqdm==4.66.1
58+
typing_extensions==4.7.1
59+
tzdata==2023.3
60+
urllib3==2.0.4
61+
yarg==0.1.9
62+
zipp==3.16.2
5863

5964
local_wheels =
6065
local-wheels/bokeh-2.2.3-py3-none-any.whl
61-
local-wheels/explorepy-1.8.0-cp38-cp38-win_amd64.whl
66+
local-wheels/explorepy-1.8.2-cp39-cp39-win_amd64.whl
6267

6368
# Other files and folders that should be installed
6469
files = ../../licenses

lib/windows/ex.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import explorepy
2+
from explorepy.stream_processor import TOPICS
3+
exp_device = explorepy.Explore()
4+
5+
# Connect to the Explore device using device bluetooth name or mac address
6+
exp_device.connect('Explore_8531')
7+
exp_device.set_channels(channel_mask="00110011001100110011001100110011")
8+
exp_device.record_data( 'file_name', do_overwrite=True, duration=15, file_type='csv')

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def read(*names, **kwargs):
8282
os.system('cp lib/mac/exploresdk.py src/explorepy')
8383
setup(
8484
name='explorepy',
85-
version='1.8.1',
85+
version='1.8.2',
8686
license='MIT license',
8787
description='Python API for Mentalab biosignal aquisition devices',
8888
long_description_content_type="text/markdown",
@@ -112,6 +112,7 @@ def read(*names, **kwargs):
112112
'Programming Language :: Python :: 3.8',
113113
'Programming Language :: Python :: 3.9',
114114
'Programming Language :: Python :: 3.10',
115+
'Programming Language :: Python :: 3.11',
115116
'Topic :: Education',
116117
'Topic :: Scientific/Engineering :: Medical Science Apps.',
117118
'Topic :: Scientific/Engineering :: Visualization'

src/explorepy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323

2424
__all__ = ["Explore", "Dashboard", "command", "exploresdk", "tools", "log_config"]
25-
__version__ = '1.8.1'
25+
__version__ = '1.8.2'
2626

2727
this = sys.modules[__name__]
2828
this._bt_interface = 'sdk'

src/explorepy/bt_mock_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def __init__(self, device_name=None, mac_address=None):
2828
raise InputError("Either name or address options must be provided!")
2929
self.is_connected = False
3030
self.mac_address = 'ABCD_EFGH_IJKL_MNOP' # dummy name as MAC address
31-
self.device_name = 'Explore_Mock_Device' # dummy name, can be changed
31+
self.device_name = device_name
3232
self.bt_serial_port_manager = None
3333
self.device_manager = None
3434

src/explorepy/bt_mock_server.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ class MockBtServer:
2828
CMD_STATUS_PID = b'\xC1'
2929
CMD_STATUS_PAYLOAD_LENGTH = b'\x0E\x00'
3030

31+
CALIB_INFO_PID = b'\xC5'
32+
CALIB_INFO_PAYLOAD_LENGTH = b'\x0C\x00'
33+
CALIB_INFO_SLOPE = b'\xCF\x5A'
34+
CALIB_INFO_OFFSET = b'\xCE\x28'
35+
3136
FLETCHER = b'\xAF\xBE\xAD\xDE'
3237

3338
def __init__(self):
@@ -157,6 +162,19 @@ def generate_dev_info_v2_packet(self):
157162
dev_info += self.FLETCHER
158163
return dev_info
159164

165+
def generate_calibration_info(self):
166+
"""
167+
Generates a calibration info packet that includes slope and offset for impedance measurement.
168+
"""
169+
calib_info = self.CALIB_INFO_PID
170+
calib_info += b'\x00'
171+
calib_info += self.CALIB_INFO_PAYLOAD_LENGTH
172+
calib_info += self.timestamp.to_bytes(4, byteorder='little')
173+
calib_info += self.CALIB_INFO_SLOPE
174+
calib_info += self.CALIB_INFO_OFFSET
175+
calib_info += self.FLETCHER
176+
return calib_info
177+
160178
def generate_cmd_rcv(self, cmd_pid, cmd_ts):
161179
"""
162180
Generates a command received packet based on command opcode (pid) and timestamp received.
@@ -169,7 +187,7 @@ def generate_cmd_rcv(self, cmd_pid, cmd_ts):
169187
self.counter.to_bytes(1, byteorder='little') + \
170188
self.CMD_RCV_PAYLOAD_LENGTH + \
171189
self.timestamp.to_bytes(4, byteorder='little')
172-
cmd_rcv += cmd_pid
190+
cmd_rcv += cmd_pid.to_bytes(1, byteorder='little')
173191
cmd_rcv += cmd_ts
174192
cmd_rcv += self.FLETCHER
175193
return cmd_rcv
@@ -187,7 +205,7 @@ def generate_cmd_status(self, cmd_pid, cmd_ts):
187205
self.counter.to_bytes(1, byteorder='little') + \
188206
self.CMD_STATUS_PAYLOAD_LENGTH + \
189207
self.timestamp.to_bytes(4, byteorder='little')
190-
cmd_status += cmd_pid
208+
cmd_status += cmd_pid.to_bytes(1, byteorder='little')
191209
cmd_status += cmd_ts
192210
cmd_status += b'\x01' # Can't find list of possible status messages, have only found b'\x01' in streams
193211
cmd_status += self.FLETCHER
@@ -204,7 +222,11 @@ def generate_command_packets(self, cmd_pid, cmd_ts):
204222
"""
205223
cmd = self.generate_cmd_rcv(cmd_pid, cmd_ts)
206224
self.counter = 0
207-
cmd += self.generate_dev_info_v2_packet()
225+
if cmd_pid == 167:
226+
# Generate calibration info packet if received PID corresponds to impedance mode activation
227+
cmd += self.generate_calibration_info()
228+
else:
229+
cmd += self.generate_dev_info_v2_packet()
208230
cmd += self.generate_cmd_status(cmd_pid, cmd_ts)
209231
self.counter = 1
210232
# Counter behaviour taken from a stream, is 0 for dev-info and cmd-status and then starts counting up again
@@ -262,7 +284,6 @@ def Read(self, length):
262284
Returns:
263285
A list of bytes
264286
"""
265-
266287
if len(self.buffer) <= length:
267288
self.buffer += self.generate_packet_buffer()
268289

@@ -286,9 +307,12 @@ def process_incoming_data(self, data):
286307
pid = data[0]
287308
if pid == 160 or pid == 176:
288309
# 160 == Command(API2BCMD), 176 == Command(API2BCMD), 27 = TS (TS is sent at the start)
289-
ts = data[4:7]
310+
# cnt = data[1]
311+
# payload_length = data[2:4]
312+
ts = data[4:8]
290313
opcode = data[8]
291314
param = data[9]
315+
# fletcher = data[10:]
292316
if opcode == 161:
293317
# set sampling rate
294318
self.exg_sr = self.cmd_sr_to_sr(param)

src/explorepy/explore.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def record_data(
143143
Args:
144144
file_name (str): Output file name
145145
do_overwrite (bool): Overwrite if files exist already
146-
duration (float): Duration of recording in seconds (if None records endlessly).
146+
duration (float): Duration of recording in seconds (if None records for 3 hours).
147147
file_type (str): File type of the recorded file. Supported file types: 'csv', 'edf'
148148
block (bool): Record in blocking mode if 'block' is True
149149
exg_ch_names (list): list of channel names. If None, default names are used.
@@ -587,3 +587,12 @@ def _check_duration(duration):
587587
logger.warning("Duration has not been set by the user. The duration is 3 hours by default.")
588588
duration = 3 * 60 * 60 # 3 hours
589589
return duration
590+
591+
def is_explore_plus_device(self):
592+
return True if 'board_id' in self.stream_processor.device_info.keys() else False
593+
594+
def is_bt_link_unstable(self):
595+
if not self.stream_processor:
596+
return False
597+
else:
598+
return self.stream_processor.is_connection_unstable()

0 commit comments

Comments
 (0)