Skip to content

Commit 8bf2479

Browse files
salman2135andrea-escartinSonjaSt
authored
Release 1.8.0 (#244)
* adapt for 16 channel * temp filter fix * add data recovery from bin feature, and 16 channel bin2csv * linting APIS-709 * fix for 32 channel APIS-709 * save settings on disconnection * fix linting * delete blank space * apply isort * remove thread lock in subscriber for mixed sps fix * replace unused variable with - * remove unused imports * Feature channel 32 APIS-729 (#241) * adapt for 16 channel * temp filter fix * add data recovery from bin feature, and 16 channel bin2csv * linting APIS-709 * fix for 32 channel APIS-709 * save settings on disconnection * fix linting * delete blank space * apply isort * remove thread lock in subscriber for mixed sps fix * replace unused variable with - * remove unused imports Co-authored-by: Andrea Escartin <[email protected]> * round recovery data timestamp APIS-663 * remove debug print * add support for external string marker * Feature channel 32 (#243) * adapt for 16 channel * temp filter fix * add data recovery from bin feature, and 16 channel bin2csv * linting APIS-709 * fix for 32 channel APIS-709 * save settings on disconnection * fix linting * delete blank space * apply isort * remove thread lock in subscriber for mixed sps fix * replace unused variable with - * remove unused imports * round recovery data timestamp APIS-663 * remove debug print * Made recovering from bin a little faster * APIS-663 adjust with time offset * add support for external string marker (#242) * add support for external string marker * add example script to demonstrate integration with LSL markers * add clock sync and thread safety for inlet variable * delete test script --------- Co-authored-by: Andrea Escartin <[email protected]> Co-authored-by: Sonja Stefani <[email protected]> * fix linting * fix isort * Update docs 1.8.0 (#245) * update documentation * bumpversion * Update author list and python versions * remove debug prints * Update installer with new deps * Update deps (#246) * switch to primitive python float for new numpy * undo fixed numpy and pyedflib versions * adjust time offset for external marker * Unit tests for EEG packets (#247) (#248) * Unit tests for EEG packets (#247) * Added unignore lines for test files in gitignore * Added some unit tests for filters.py * Refactored filters.py some and added unit test(s) to test_filters.py * Added tests for packet * Added packet test cases * Added README for tests * Fixed wrong patching in test_packet.py, added test * Added stubs * Added test_packet.py script, changed workflows script * yaml changed * Made tiny change in eeg data for tests * Added Jenkinsfile, cleaned up testscripts and filters.py * Renamed Jenkinsfile * Adjusted Jenkinsfile * Added Dockerfile for Linux container, adapted Jenkinsfile * Adapted Jenkinsfile to record test results, made sure image is built from Dockerfile properly * Changed testing.yml * Converted unittest functions to use pytest and pytest-mock * Added pytest-mock as requirement * Added fixtures for data reading and passing to tests * Changed gitignore to allow tests/, added test and expected output files as well as test fixtures * Added file for expected outcome from fake data * Changed test res structure to have input and output test data, added more (bin) test packets * Parametrized fixture that provides eeg in and output data, changed workflow to create html report * Changed workflow to store artifact, removed windows and macos for now to reduce time * Added clause that archiving runs on success and failure (sigh) * Changed html creation to self contained * Changed workflow back to run on pr on master * Added new test input and output files, added parametrized tests for int24to32, tests for abstract (Packet and EEG), test for timestamp init (Packet) * Added tests for EEG base packet and more expected output data to res/out/ * Edited ReadMe to mention res folders and formats * Added more res files in conftests.py * Removed unnecessary packet * Removed unnecessary files / unfinished tests * Fixed formatting so linter is happy * Sorted imports for isort... * Fixed testing workflow (how did that bracket get there?) * Changed testing workflow to trigger on PR to develop as well --------- Co-authored-by: Sonja Stefani <[email protected]> Co-authored-by: Sonja Stefani <[email protected]> * Update deps (#249) * switch to primitive python float for new numpy * undo fixed numpy and pyedflib versions * replace np.float with float (#250) * Fix numpy chnagelog (#251) * replace np.float with float * remove np namespace var and unused var * Fix numpy chnagelog (#252) * replace np.float with float * remove np namespace var and unused var * remove more numpy var * edit changelog for release --------- Co-authored-by: Andrea Escartin <[email protected]> Co-authored-by: Sonja Stefani <[email protected]> Co-authored-by: Sonja Stefani <[email protected]>
1 parent 88ed91e commit 8bf2479

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+606
-161
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.7.0
2+
current_version = 1.8.0
33
commit = False
44
tag = False
55

.github/workflows/testing.yml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
name: testing
22
on:
33
pull_request:
4-
branches: [master]
4+
branches: [master, develop]
55
jobs:
66
test:
77
strategy:
88
matrix:
99
os: [ubuntu-latest, windows-latest, macos-latest]
10-
python: [3.8.10]
10+
python: [3.9.12]
1111
runs-on: ${{ matrix.os }}
1212
timeout-minutes: 10
1313
steps:
@@ -17,7 +17,7 @@ jobs:
1717
fetch-depth: 0
1818
clean: false
1919

20-
- name: checkout to current branch
20+
- name: Checkout to current branch
2121
run: git checkout ${{ env.BRANCH }}
2222

2323
- name: Install non-python dependencies on Ubuntu
@@ -30,7 +30,14 @@ jobs:
3030
with:
3131
python-version: ${{ matrix.python }}
3232
conda-channels: anaconda, conda-forge
33-
- run: |
33+
- name: Install liblsl from conda-forge
34+
run: |
3435
conda install -c conda-forge liblsl
3536
pip install -e .[test]
36-
python -m pytest --import-mode=append
37+
python -m pytest --import-mode=append --html=pytest_report.html --self-contained-html
38+
- name: Archive test results
39+
if: success() || failure()
40+
uses: actions/upload-artifact@v3
41+
with:
42+
name: pytest-results
43+
path: pytest_report.html

.gitignore

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,8 @@ pip-log.txt
3232
nosetests.xml
3333
coverage.xml
3434
htmlcov
35-
tests/
3635
src/explorepy/example.py
3736

38-
39-
40-
41-
4237
# Translations
4338
*.mo
4439

CHANGELOG.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11

22
Changelog
33
=========
4+
5+
6+
1.8.0 (27.2.2023)
7+
------------------
8+
* Add support for explore+ 16 ch device
9+
* Unit test integration with CI/CD
10+
* Repair CSV from binary data
11+
* Save current device settings on disconnection, restore them on demand
12+
* Receive external triggers from LSL
13+
14+
415
1.7.0 (21.12.2022)
516
------------------
617
* Add suppport for new explore+ 32 ch device
@@ -10,7 +21,7 @@ Changelog
1021

1122
1.6.3 (25.10.2022)
1223
------------------
13-
* Add new 8 channel Explore+ device
24+
* Add new 8 channel Explore+ device
1425

1526

1627
1.6.2 (7.09.2022)

README.rst

Lines changed: 5 additions & 3 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.7.0.svg
20+
.. |commits-since| image:: https://img.shields.io/github/commits-since/Mentalab-hub/explorepy/v1.8.0.svg
2121
:alt: Commits since latest release
22-
:target: https://github.com/Mentalab-hub/explorepy/compare/v1.7.0...master
22+
:target: https://github.com/Mentalab-hub/explorepy/compare/v1.8.0...master
2323

2424

2525
.. |wheel| image:: https://img.shields.io/pypi/wheel/explorepy.svg
@@ -60,7 +60,7 @@ For other operating systems, or to build the package manually on Windows, please
6060
Requirements
6161
------------
6262

63-
* Python 3.7 to Python 3.10.
63+
* Python 3.7 to Python 3.11.
6464
* Visual Studio 2015 community edition (Windows only. For package building).
6565
* Bluetooth header files (Linux only. Use: ``sudo apt-get install libbluetooth-dev``).
6666

@@ -137,6 +137,7 @@ Authors
137137
- `Mohamad Atayi`_
138138
- `Salman Rahman`_
139139
- `Andrea Escartin`_
140+
- `Sonja Stefani`_
140141
- `Alex Platt`_
141142
- `Andreas Gutsche`_
142143
- `Masooma Fazelian`_
@@ -148,6 +149,7 @@ Authors
148149
.. _Mohamad Atayi: https://github.com/bmeatayi
149150
.. _Salman Rahman: https://github.com/salman2135
150151
.. _Andrea Escartin: https://github.com/andrea-escartin
152+
.. _Sonja Stefani: https://github.com/SonjaSt
151153
.. _Alex Platt: https://github.com/Nujanauss
152154
.. _Andreas Gutsche: https://github.com/andyman410
153155
.. _Masooma Fazelian: https://github.com/fazelian

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.7.0'
32+
version = release = '1.8.0'
3333

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

docs/installation.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ Installation
44

55
Minimal Requirements
66
------------
7-
* Python 3.6 to Python 3.10
7+
* Python 3.7 to Python 3.11
88
* Microsoft Build Tools for Visual Studio 2019 (only Windows)
99
* 6GB RAM (minimum 1GB *free* RAM during the session)
1010
* Intel i5 or higher (2x2.5GHz) CPU
1111

1212
Recommended Requirements
1313
------------
14-
* Python 3.6 to Python 3.10
14+
* Python 3.7 to Python 3.11
1515
* Microsoft Build Tools for Visual Studio 2019 (only Windows)
1616
* 8GB RAM
1717
* Intel i7 or higher CPU

examples/receive_external_marker.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import time
2+
from threading import Thread
3+
4+
import explorepy
5+
6+
7+
def modify_variable(explore_instance):
8+
from pylsl import StreamInlet, resolve_stream
9+
print("looking for a marker stream...")
10+
streams = resolve_stream('type', 'Markers')
11+
12+
# create a new inlet to read from the stream
13+
# adds processing flags for clocksync and thread safety
14+
# Reference: https://labstreaminglayer.readthedocs.io/info/faqs.html#lsl-local-clock
15+
inlet = StreamInlet(streams[0], processing_flags=1 | 8)
16+
17+
while True:
18+
sample, timestamp = inlet.pull_sample()
19+
print("got %s at time %s" % (sample[0], timestamp))
20+
explore_instance.set_external_marker(timestamp, str(sample[0]))
21+
time.sleep(1)
22+
23+
24+
def main():
25+
# Create an Explore object
26+
explore = explorepy.Explore()
27+
explore.connect(device_name='Explore_XXXX')
28+
explore.record_data(file_name='test_event_gen', file_type='csv', do_overwrite=True, block=False)
29+
t = Thread(target=modify_variable, args=(explore,))
30+
t.start()
31+
32+
n_iteration = 100
33+
interval = 2
34+
35+
for i in range(n_iteration):
36+
time.sleep(interval)
37+
38+
explore.stop_recording()
39+
40+
41+
if __name__ == "__main__":
42+
main()

installer/windows/installer.cfg

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

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

1212

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

1717
[Include]
1818
pypi_wheels =
19-
explorepy==1.7.0
19+
#explorepy==1.8.0
2020
appdirs==1.4.3
2121
# bokeh==2.2.3 # No wheel available
2222
certifi==2020.12.5
23-
Click==7.0
24-
eeglabio==0.0.2.post2
25-
Jinja2==2.11.2
26-
MarkupSafe==1.1.1
27-
mne==1.1.1
28-
numpy==1.19.2
29-
packaging==20.4
30-
Pillow==7.2.0
31-
pyEDFlib==0.1.15
32-
pylsl==1.13.1
33-
pyparsing==2.4.7
34-
python-dateutil==2.8.1
35-
PyYAML==5.3.1
36-
scipy==1.5.2
23+
click==7.1.2
24+
colorama==0.4.6
25+
contourpy==1.0.7
26+
cycler==0.11.0
27+
decorator==5.1.1
28+
eeglabio==0.0.2.post3
29+
fonttools==4.38.0
30+
idna==3.4
31+
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
44+
pyparsing==3.0.9
45+
python-dateutil==2.8.2
46+
pytz==2022.7.1
47+
PyYAML==6.0
48+
requests==2.28.2
49+
scipy==1.10.0
3750
sentry-sdk==1.0.0
38-
six==1.15.0
39-
tornado==6.0.4
40-
typing-extensions==3.7.4
41-
urllib3==1.26.4
51+
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
4258

4359
local_wheels =
4460
local-wheels/bokeh-2.2.3-py3-none-any.whl
61+
local-wheels/explorepy-1.8.0-cp38-cp38-win_amd64.whl
4562

4663
# Other files and folders that should be installed
4764
files = ../../licenses

lib/windows/test.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

setup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ def read(*names, **kwargs):
3232
return fh.read()
3333

3434

35-
my_req = ['numpy==1.21.4', 'scipy', 'pyedflib==0.1.25', 'click==7.1.2', 'appdirs==1.4.3', 'sentry_sdk==1.0.0', 'mne', 'eeglabio', 'pandas'] # noqa: E501
35+
my_req = ['numpy', 'scipy', 'pyedflib', 'click==7.1.2', 'appdirs==1.4.3', 'sentry_sdk==1.0.0', 'mne', 'eeglabio', 'pandas'] # noqa: E501
3636
test_requirements = ["pytest==6.2.5",
37+
"pytest-mock==3.10.0",
38+
"pytest-html==3.2.0",
3739
"flake8==4.0.1",
3840
"isort==5.10.1"]
3941
extras = {"test": test_requirements}
@@ -80,7 +82,7 @@ def read(*names, **kwargs):
8082
os.system('cp lib/mac/exploresdk.py src/explorepy')
8183
setup(
8284
name='explorepy',
83-
version='1.7.0',
85+
version='1.8.0',
8486
license='MIT license',
8587
description='Python API for Mentalab biosignal aquisition devices',
8688
long_description_content_type="text/markdown",

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.7.0'
25+
__version__ = '1.8.0'
2626

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

src/explorepy/explore.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import os
1818
import re
1919
import time
20-
from datetime import datetime
2120
from threading import Timer
2221

2322
import numpy as np
@@ -170,14 +169,15 @@ def record_data(
170169
file_type=file_type,
171170
do_overwrite=do_overwrite)
172171

172+
# TODO: make sure older timestamp in meta file was not used in any other software!
173173
if file_type == 'csv':
174174
self.recorders['marker'] = create_marker_recorder(filename=marker_out_file, do_overwrite=do_overwrite)
175175
self.recorders['meta'] = create_meta_recorder(filename=meta_out_file,
176176
fs=self.stream_processor.device_info['sampling_rate'],
177177
adc_mask=SettingsManager(self.device_name).get_adc_mask(),
178178
device_name=self.device_name,
179179
do_overwrite=do_overwrite,
180-
timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
180+
timestamp=str(self.stream_processor.parser._time_offset)) # noqa: E501
181181
self.recorders['meta'].write_meta()
182182
self.recorders['meta'].stop()
183183

@@ -252,7 +252,9 @@ def convert_bin(self, bin_file, out_dir='', file_type='edf', do_overwrite=False,
252252
self.mask = self.stream_processor.device_info['adc_mask']
253253
if 'board_id' in self.stream_processor.device_info:
254254
if 'PCB_304_801_XXX' in self.stream_processor.device_info['board_id']:
255-
self.mask = [1 for i in range(0, 32)]
255+
self.mask = [1 for _ in range(0, 32)]
256+
if 'PCB_305_801_XXX' in self.stream_processor.device_info['board_id']:
257+
self.mask = [1 for _ in range(0, 16)]
256258

257259
self.recorders['exg'] = create_exg_recorder(filename=exg_out_file,
258260
file_type=self.recorders['file_type'],
@@ -282,8 +284,8 @@ def convert_bin(self, bin_file, out_dir='', file_type='edf', do_overwrite=False,
282284
def device_info_callback(packet):
283285
new_device_info = packet.get_info()
284286
if not self.stream_processor.compare_device_info(new_device_info):
285-
new_file_name = exg_out_file + "_" + str(np.round(packet.timestamp, 0))
286-
new_meta_name = meta_out_file + "_" + str(np.round(packet.timestamp, 0))
287+
new_file_name = exg_out_file[:-4] + "_" + str(np.round(packet.timestamp, 0)) + '_ExG'
288+
new_meta_name = meta_out_file[:-4] + "_" + str(np.round(packet.timestamp, 0)) + '_Meta'
287289
logger.warning("Creating a new file: " + new_file_name + '.' + self.recorders['file_type'])
288290
self.stream_processor.unsubscribe(callback=self.recorders['exg'].write_data, topic=TOPICS.raw_ExG)
289291
self.stream_processor.unsubscribe(callback=self.recorders['marker'].set_marker, topic=TOPICS.marker)
@@ -414,6 +416,16 @@ def set_marker(self, code):
414416
self._check_connection()
415417
self.stream_processor.set_marker(code=code)
416418

419+
def set_external_marker(self, time_lsl, marker_string):
420+
"""Sets a digital event marker while streaming
421+
422+
Args:
423+
time_lsl (timestamp): timestamp from external marker)
424+
marker_string (string): string to save as experiment marker)
425+
"""
426+
self._check_connection()
427+
self.stream_processor.set_ext_marker(time_lsl, marker_string)
428+
417429
def format_memory(self):
418430
"""Format memory of the device
419431

0 commit comments

Comments
 (0)