py-neuromodulation 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl
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.
- docs/build/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
- docs/build/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +233 -0
- docs/build/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/build/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
- docs/build/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/build/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
- docs/build/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
- docs/build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +239 -0
- docs/build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
- docs/build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
- docs/build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/source/_build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +76 -0
- docs/source/_build/html/_downloads/0d0d0a76e8f648d5d3cbc47da6351932/plot_real_time_demo.py +97 -0
- docs/source/_build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +240 -0
- docs/source/_build/html/_downloads/5d73cadc59a8805c47e3b84063afc157/plot_example_BIDS.py +233 -0
- docs/source/_build/html/_downloads/7660317fa5a6bfbd12fcca9961457fc4/plot_example_rmap_computing.py +63 -0
- docs/source/_build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/source/_build/html/_downloads/839e5b319379f7fd9e867deb00fd797f/plot_example_gridPointProjection.py +210 -0
- docs/source/_build/html/_downloads/ae8be19afe5e559f011fc9b138968ba0/plot_first_demo.py +192 -0
- docs/source/_build/html/_downloads/b8b06cacc17969d3725a0b6f1d7741c5/plot_example_sharpwave_analysis.py +219 -0
- docs/source/_build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +121 -0
- docs/source/_build/html/_downloads/c31a86c0b68cb4167d968091ace8080d/plot_example_add_feature.py +68 -0
- docs/source/_build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/source/_build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +189 -0
- docs/source/_build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/source/auto_examples/plot_0_first_demo.py +189 -0
- docs/source/auto_examples/plot_1_example_BIDS.py +240 -0
- docs/source/auto_examples/plot_2_example_add_feature.py +76 -0
- docs/source/auto_examples/plot_3_example_sharpwave_analysis.py +219 -0
- docs/source/auto_examples/plot_4_example_gridPointProjection.py +210 -0
- docs/source/auto_examples/plot_5_example_rmap_computing.py +64 -0
- docs/source/auto_examples/plot_6_real_time_demo.py +121 -0
- docs/source/conf.py +105 -0
- examples/plot_0_first_demo.py +189 -0
- examples/plot_1_example_BIDS.py +240 -0
- examples/plot_2_example_add_feature.py +76 -0
- examples/plot_3_example_sharpwave_analysis.py +219 -0
- examples/plot_4_example_gridPointProjection.py +210 -0
- examples/plot_5_example_rmap_computing.py +64 -0
- examples/plot_6_real_time_demo.py +121 -0
- packages/realtime_decoding/build/lib/realtime_decoding/__init__.py +4 -0
- packages/realtime_decoding/build/lib/realtime_decoding/decoder.py +104 -0
- packages/realtime_decoding/build/lib/realtime_decoding/features.py +163 -0
- packages/realtime_decoding/build/lib/realtime_decoding/helpers.py +15 -0
- packages/realtime_decoding/build/lib/realtime_decoding/run_decoding.py +345 -0
- packages/realtime_decoding/build/lib/realtime_decoding/trainer.py +54 -0
- packages/tmsi/build/lib/TMSiFileFormats/__init__.py +37 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/__init__.py +36 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/lsl_stream_writer.py +200 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_file_writer.py +496 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_to_edf_converter.py +236 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/xdf_file_writer.py +977 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/__init__.py +35 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/edf_reader.py +116 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/poly5reader.py +294 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/xdf_reader.py +229 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_writer.py +102 -0
- packages/tmsi/build/lib/TMSiPlotters/__init__.py +2 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/__init__.py +39 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/_plotter_gui.py +234 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/plotting_gui.py +440 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/__init__.py +44 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/hd_emg_plotter.py +446 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/impedance_plotter.py +589 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/signal_plotter.py +1326 -0
- packages/tmsi/build/lib/TMSiSDK/__init__.py +54 -0
- packages/tmsi/build/lib/TMSiSDK/device.py +588 -0
- packages/tmsi/build/lib/TMSiSDK/devices/__init__.py +34 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/TMSi_Device_API.py +1764 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/__init__.py +34 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_device.py +1366 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_types.py +520 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/xml_saga_config.py +165 -0
- packages/tmsi/build/lib/TMSiSDK/error.py +95 -0
- packages/tmsi/build/lib/TMSiSDK/sample_data.py +63 -0
- packages/tmsi/build/lib/TMSiSDK/sample_data_server.py +99 -0
- packages/tmsi/build/lib/TMSiSDK/settings.py +45 -0
- packages/tmsi/build/lib/TMSiSDK/tmsi_device.py +111 -0
- packages/tmsi/build/lib/__init__.py +4 -0
- packages/tmsi/build/lib/apex_sdk/__init__.py +34 -0
- packages/tmsi/build/lib/apex_sdk/device/__init__.py +41 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API.py +1009 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_enums.py +239 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_structures.py +668 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_device.py +1611 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_dongle.py +38 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_event_reader.py +57 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_channel.py +44 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_config.py +150 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_const.py +36 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_impedance_channel.py +48 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_info.py +108 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/dongle_info.py +39 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/download_measurement.py +77 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/eeg_measurement.py +150 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/impedance_measurement.py +129 -0
- packages/tmsi/build/lib/apex_sdk/device/threads/conversion_thread.py +59 -0
- packages/tmsi/build/lib/apex_sdk/device/threads/sampling_thread.py +57 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_channel.py +83 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_device.py +201 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_device_enums.py +103 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_dongle.py +43 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_event_reader.py +50 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_measurement.py +118 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/__init__.py +33 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/event_data.py +44 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data.py +50 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data_server.py +136 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_errors/error.py +126 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_sdk.py +113 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/apex/apex_structure_generator.py +134 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/decorators.py +60 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/logger_filter.py +42 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/singleton.py +42 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/support_functions.py +72 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/tmsi_logger.py +98 -0
- py_neuromodulation/{helper.py → _write_example_dataset_helper.py} +1 -1
- py_neuromodulation/nm_EpochStream.py +2 -3
- py_neuromodulation/nm_IO.py +43 -70
- py_neuromodulation/nm_RMAP.py +308 -11
- py_neuromodulation/nm_analysis.py +1 -1
- py_neuromodulation/nm_artifacts.py +25 -0
- py_neuromodulation/nm_bispectra.py +64 -29
- py_neuromodulation/nm_bursts.py +44 -30
- py_neuromodulation/nm_coherence.py +2 -1
- py_neuromodulation/nm_features.py +4 -2
- py_neuromodulation/nm_filter.py +63 -32
- py_neuromodulation/nm_filter_preprocessing.py +91 -0
- py_neuromodulation/nm_fooof.py +47 -29
- py_neuromodulation/nm_mne_connectivity.py +1 -1
- py_neuromodulation/nm_normalization.py +50 -74
- py_neuromodulation/nm_oscillatory.py +151 -31
- py_neuromodulation/nm_plots.py +13 -10
- py_neuromodulation/nm_rereference.py +10 -8
- py_neuromodulation/nm_run_analysis.py +28 -13
- py_neuromodulation/nm_sharpwaves.py +103 -136
- py_neuromodulation/nm_stats.py +44 -30
- py_neuromodulation/nm_stream_abc.py +18 -10
- py_neuromodulation/nm_stream_offline.py +181 -40
- py_neuromodulation/utils/_logging.py +24 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/METADATA +182 -142
- py_neuromodulation-0.0.3.dist-info/RECORD +188 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/WHEEL +2 -1
- py_neuromodulation-0.0.3.dist-info/top_level.txt +5 -0
- tests/__init__.py +0 -0
- tests/conftest.py +117 -0
- tests/test_all_examples.py +10 -0
- tests/test_all_features.py +63 -0
- tests/test_bispectra.py +70 -0
- tests/test_bursts.py +105 -0
- tests/test_feature_sampling_rates.py +143 -0
- tests/test_fooof.py +16 -0
- tests/test_initalization_offline_stream.py +41 -0
- tests/test_multiprocessing.py +58 -0
- tests/test_nan_values.py +29 -0
- tests/test_nm_filter.py +95 -0
- tests/test_nm_resample.py +63 -0
- tests/test_normalization_settings.py +146 -0
- tests/test_notch_filter.py +31 -0
- tests/test_osc_features.py +424 -0
- tests/test_preprocessing_filter.py +151 -0
- tests/test_rereference.py +171 -0
- tests/test_sampling.py +57 -0
- tests/test_settings_change_after_init.py +76 -0
- tests/test_sharpwave.py +165 -0
- tests/test_target_channel_add.py +100 -0
- tests/test_timing.py +80 -0
- py_neuromodulation/data/README +0 -6
- py_neuromodulation/data/dataset_description.json +0 -8
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/MOV_aligned_features_ch_ECOG_RIGHT_0_all.png +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/all_feature_plt.pdf +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_FEATURES.csv +0 -182
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_LM_ML_RES.p +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SETTINGS.json +0 -273
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SIDECAR.json +0 -6
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_decoding_performance.png +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_nm_channels.csv +0 -11
- py_neuromodulation/data/participants.json +0 -32
- py_neuromodulation/data/participants.tsv +0 -2
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +0 -5
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +0 -11
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +0 -11
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +0 -18
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +0 -35
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +0 -13
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +0 -2
- py_neuromodulation/grid_cortex.tsv +0 -40
- py_neuromodulation/grid_subcortex.tsv +0 -1429
- py_neuromodulation/nm_settings.json +0 -290
- py_neuromodulation/plots/STN_surf.mat +0 -0
- py_neuromodulation/plots/Vertices.mat +0 -0
- py_neuromodulation/plots/faces.mat +0 -0
- py_neuromodulation/plots/grid.mat +0 -0
- py_neuromodulation/py_neuromodulation.egg-info/PKG-INFO +0 -104
- py_neuromodulation/py_neuromodulation.egg-info/dependency_links.txt +0 -1
- py_neuromodulation/py_neuromodulation.egg-info/requires.txt +0 -26
- py_neuromodulation/py_neuromodulation.egg-info/top_level.txt +0 -1
- py_neuromodulation-0.0.2.dist-info/RECORD +0 -73
- /py_neuromodulation/{py_neuromodulation.egg-info/SOURCES.txt → utils/__init__.py} +0 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
(c) 2022 Twente Medical Systems International B.V., Oldenzaal The Netherlands
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
####### # # ##### #
|
|
17
|
+
# ## ## #
|
|
18
|
+
# # # # # # #
|
|
19
|
+
# # # # ##### #
|
|
20
|
+
# # # # #
|
|
21
|
+
# # # # #
|
|
22
|
+
# # # ##### #
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @file ${__init__.py}
|
|
26
|
+
* @brief Initialisation of the file_readers directory classes.
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from .poly5reader import Poly5Reader
|
|
34
|
+
from .xdf_reader import Xdf_Reader
|
|
35
|
+
from .edf_reader import Edf_Reader
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
(c) 2022 Twente Medical Systems International B.V., Oldenzaal The Netherlands
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
####### # # ##### #
|
|
17
|
+
# ## ## #
|
|
18
|
+
# # # # # # #
|
|
19
|
+
# # # # ##### #
|
|
20
|
+
# # # # #
|
|
21
|
+
# # # # #
|
|
22
|
+
# # # ##### #
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @file ${xdf_reader.py}
|
|
26
|
+
* @brief EDF to MNE Converter.
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import tkinter as tk
|
|
34
|
+
from tkinter import filedialog
|
|
35
|
+
import mne
|
|
36
|
+
import pandas as pd
|
|
37
|
+
|
|
38
|
+
from os.path import join, dirname, realpath
|
|
39
|
+
|
|
40
|
+
Reader_dir = dirname(realpath(__file__)) # directory of this file
|
|
41
|
+
modules_dir = join(Reader_dir, "../../") # directory with all modules
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Edf_Reader:
|
|
45
|
+
def __init__(self, filename=None, add_ch_locs=False):
|
|
46
|
+
if filename == None:
|
|
47
|
+
root = tk.Tk()
|
|
48
|
+
filename = filedialog.askopenfilename(
|
|
49
|
+
title="Select edf-file",
|
|
50
|
+
filetypes=(("edf-files", "*.edf"), ("All files", "*.*")),
|
|
51
|
+
)
|
|
52
|
+
root.withdraw()
|
|
53
|
+
|
|
54
|
+
# read raw edf-file
|
|
55
|
+
# change channel type of COUNTER channel to misc
|
|
56
|
+
mne_object = mne.io.read_raw_edf(filename, misc=[-2], preload=True)
|
|
57
|
+
|
|
58
|
+
if add_ch_locs:
|
|
59
|
+
# add channel locations from txt file
|
|
60
|
+
chLocs = pd.read_csv(
|
|
61
|
+
join(modules_dir, "TMSiSDK/_resources/EEGchannelsTMSi3D.txt"),
|
|
62
|
+
sep="\t",
|
|
63
|
+
header=None,
|
|
64
|
+
)
|
|
65
|
+
chLocs.columns = ["default_name", "eeg_name", "X", "Y", "Z"]
|
|
66
|
+
# add locations and convert to head size of 95 mm
|
|
67
|
+
for idx, ch in enumerate(mne_object.info["chs"]):
|
|
68
|
+
try:
|
|
69
|
+
a = [
|
|
70
|
+
i
|
|
71
|
+
for i, e in (
|
|
72
|
+
enumerate(chLocs["eeg_name"].values)
|
|
73
|
+
or enumerate(chLocs["default_name"].values)
|
|
74
|
+
)
|
|
75
|
+
if e == ch["ch_name"]
|
|
76
|
+
]
|
|
77
|
+
mne_object.info["chs"][idx]["loc"][0] = (
|
|
78
|
+
95 * 1e-3 * chLocs["X"].values[a]
|
|
79
|
+
)
|
|
80
|
+
mne_object.info["chs"][idx]["loc"][1] = (
|
|
81
|
+
95 * 1e-3 * chLocs["Y"].values[a]
|
|
82
|
+
)
|
|
83
|
+
mne_object.info["chs"][idx]["loc"][2] = (
|
|
84
|
+
95 * 1e-3 * chLocs["Z"].values[a]
|
|
85
|
+
)
|
|
86
|
+
except:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
# unit conversion of eeg channels
|
|
90
|
+
mne_object.apply_function(lambda x: x * 1e-6, picks="eeg")
|
|
91
|
+
|
|
92
|
+
self.mne_object = mne_object
|
|
93
|
+
|
|
94
|
+
def add_impedances(self, imp_filename=None):
|
|
95
|
+
"""Add impedances from .txt-file"""
|
|
96
|
+
if imp_filename == None:
|
|
97
|
+
root = tk.Tk()
|
|
98
|
+
imp_filename = filedialog.askopenfilename(
|
|
99
|
+
title="Select impedance file",
|
|
100
|
+
filetypes=(("text files", "*.txt"), ("All files", "*.*")),
|
|
101
|
+
)
|
|
102
|
+
root.withdraw()
|
|
103
|
+
impedances = []
|
|
104
|
+
|
|
105
|
+
imp_df = pd.read_csv(imp_filename, delimiter="\t", header=None)
|
|
106
|
+
imp_df.columns = ["ch_name", "impedance", "unit"]
|
|
107
|
+
|
|
108
|
+
for ch in range(len(self.mne_object.info["chs"])):
|
|
109
|
+
for i_ch in range(len(imp_df)):
|
|
110
|
+
if (
|
|
111
|
+
self.mne_object.info["chs"][ch]["ch_name"]
|
|
112
|
+
== imp_df["ch_name"][i_ch]
|
|
113
|
+
):
|
|
114
|
+
impedances.append(imp_df["impedance"][i_ch])
|
|
115
|
+
|
|
116
|
+
self.mne_object.impedances = impedances
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"""
|
|
2
|
+
(c) 2022 Twente Medical Systems International B.V., Oldenzaal The Netherlands
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
####### # # ##### #
|
|
17
|
+
# ## ## #
|
|
18
|
+
# # # # # # #
|
|
19
|
+
# # # # ##### #
|
|
20
|
+
# # # # #
|
|
21
|
+
# # # # #
|
|
22
|
+
# # # ##### #
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @file ${poly5reader.py}
|
|
26
|
+
* @brief Poly5 File Reader.
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import numpy as np
|
|
34
|
+
import struct
|
|
35
|
+
import datetime
|
|
36
|
+
import mne
|
|
37
|
+
import tkinter as tk
|
|
38
|
+
from tkinter import filedialog
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Poly5Reader:
|
|
42
|
+
def __init__(self, filename=None, readAll=True):
|
|
43
|
+
if filename == None:
|
|
44
|
+
root = tk.Tk()
|
|
45
|
+
|
|
46
|
+
filename = filedialog.askopenfilename(
|
|
47
|
+
title="Select poly5-file",
|
|
48
|
+
filetypes=(("poly5-files", "*.poly5"), ("All files", "*.*")),
|
|
49
|
+
)
|
|
50
|
+
root.withdraw()
|
|
51
|
+
|
|
52
|
+
self.filename = filename
|
|
53
|
+
self.readAll = readAll
|
|
54
|
+
print("Reading file ", filename)
|
|
55
|
+
self._readFile(filename)
|
|
56
|
+
|
|
57
|
+
def read_data_MNE(
|
|
58
|
+
self,
|
|
59
|
+
) -> mne.io.RawArray:
|
|
60
|
+
"""Return MNE RawArray given internal channel names and types
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
mne.io.RawArray
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
streams = self.channels
|
|
68
|
+
fs = self.sample_rate
|
|
69
|
+
labels = [s._Channel__name for s in streams]
|
|
70
|
+
units = [s._Channel__unit_name for s in streams]
|
|
71
|
+
|
|
72
|
+
type_options = [
|
|
73
|
+
"ecg",
|
|
74
|
+
"bio",
|
|
75
|
+
"stim",
|
|
76
|
+
"eog",
|
|
77
|
+
"misc",
|
|
78
|
+
"seeg",
|
|
79
|
+
"dbs",
|
|
80
|
+
"ecog",
|
|
81
|
+
"mag",
|
|
82
|
+
"eeg",
|
|
83
|
+
"ref_meg",
|
|
84
|
+
"grad",
|
|
85
|
+
"emg",
|
|
86
|
+
"hbr",
|
|
87
|
+
"hbo",
|
|
88
|
+
]
|
|
89
|
+
types_clean = []
|
|
90
|
+
for t in labels:
|
|
91
|
+
for t_option in type_options:
|
|
92
|
+
if t_option in t.lower():
|
|
93
|
+
types_clean.append(t_option)
|
|
94
|
+
break
|
|
95
|
+
else:
|
|
96
|
+
types_clean.append("misc")
|
|
97
|
+
|
|
98
|
+
info = mne.create_info(ch_names=labels, sfreq=fs, ch_types=types_clean)
|
|
99
|
+
|
|
100
|
+
# convert from microvolts to volts if necessary
|
|
101
|
+
scale = np.array([1e-6 if u == "µVolt" else 1 for u in units])
|
|
102
|
+
|
|
103
|
+
raw = mne.io.RawArray(
|
|
104
|
+
self.samples * np.expand_dims(scale, axis=1), info
|
|
105
|
+
)
|
|
106
|
+
return raw
|
|
107
|
+
|
|
108
|
+
def _readFile(self, filename):
|
|
109
|
+
try:
|
|
110
|
+
self.file_obj = open(filename, "rb")
|
|
111
|
+
file_obj = self.file_obj
|
|
112
|
+
try:
|
|
113
|
+
self._readHeader(file_obj)
|
|
114
|
+
self.channels = self._readSignalDescription(file_obj)
|
|
115
|
+
self._myfmt = (
|
|
116
|
+
"f" * self.num_channels * self.num_samples_per_block
|
|
117
|
+
)
|
|
118
|
+
self._buffer_size = (
|
|
119
|
+
self.num_channels * self.num_samples_per_block
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if self.readAll:
|
|
123
|
+
sample_buffer = np.zeros(
|
|
124
|
+
self.num_channels * self.num_samples
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
for i in range(self.num_data_blocks):
|
|
128
|
+
print(
|
|
129
|
+
"\rProgress: % 0.1f %%"
|
|
130
|
+
% (100 * i / self.num_data_blocks),
|
|
131
|
+
end="\r",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Check whether final data block is filled completely or not
|
|
135
|
+
if i == self.num_data_blocks - 1:
|
|
136
|
+
_final_block_size = (
|
|
137
|
+
self.num_samples / self.num_data_blocks
|
|
138
|
+
)
|
|
139
|
+
if (
|
|
140
|
+
_final_block_size % self.num_samples_per_block
|
|
141
|
+
!= 0
|
|
142
|
+
):
|
|
143
|
+
data_block = self._readSignalBlock(
|
|
144
|
+
file_obj,
|
|
145
|
+
buffer_size=(
|
|
146
|
+
self.num_samples
|
|
147
|
+
% self.num_samples_per_block
|
|
148
|
+
)
|
|
149
|
+
* self.num_channels,
|
|
150
|
+
myfmt="f"
|
|
151
|
+
* (
|
|
152
|
+
self.num_samples
|
|
153
|
+
% self.num_samples_per_block
|
|
154
|
+
)
|
|
155
|
+
* self.num_channels,
|
|
156
|
+
)
|
|
157
|
+
else:
|
|
158
|
+
data_block = self._readSignalBlock(
|
|
159
|
+
file_obj, self._buffer_size, self._myfmt
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
data_block = self._readSignalBlock(
|
|
163
|
+
file_obj, self._buffer_size, self._myfmt
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Get indices that need to be filled in the samples array
|
|
167
|
+
i1 = i * self.num_samples_per_block * self.num_channels
|
|
168
|
+
i2 = (
|
|
169
|
+
(i + 1)
|
|
170
|
+
* self.num_samples_per_block
|
|
171
|
+
* self.num_channels
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Correct for final data block if this is not fully filled
|
|
175
|
+
if i2 >= self.num_samples * self.num_channels:
|
|
176
|
+
i2 = self.num_samples * self.num_channels
|
|
177
|
+
|
|
178
|
+
# Insert the read data_block into the sample_buffer array
|
|
179
|
+
sample_buffer[i1:i2] = data_block
|
|
180
|
+
|
|
181
|
+
samples = np.transpose(
|
|
182
|
+
np.reshape(
|
|
183
|
+
sample_buffer, [self.num_samples, self.num_channels]
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
self.ch_names = [s._Channel__name for s in self.channels]
|
|
188
|
+
self.ch_unit_names = [
|
|
189
|
+
s._Channel__unit_name for s in self.channels
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
self.samples = samples
|
|
193
|
+
print("Done reading data.")
|
|
194
|
+
self.file_obj.close()
|
|
195
|
+
|
|
196
|
+
except Exception as e:
|
|
197
|
+
print("Reading data failed, because of the following error:\n")
|
|
198
|
+
raise
|
|
199
|
+
except OSError:
|
|
200
|
+
print("Could not open file. ")
|
|
201
|
+
|
|
202
|
+
def readSamples(self, n_blocks=None):
|
|
203
|
+
"Function to read a subset of sample blocks from a file"
|
|
204
|
+
if n_blocks == None:
|
|
205
|
+
n_blocks = self.num_data_blocks
|
|
206
|
+
|
|
207
|
+
sample_buffer = np.zeros(
|
|
208
|
+
self.num_channels * n_blocks * self.num_samples_per_block
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
for i in range(n_blocks):
|
|
212
|
+
data_block = self._readSignalBlock(
|
|
213
|
+
self.file_obj, self._buffer_size, self._myfmt
|
|
214
|
+
)
|
|
215
|
+
i1 = i * self.num_samples_per_block * self.num_channels
|
|
216
|
+
i2 = (i + 1) * self.num_samples_per_block * self.num_channels
|
|
217
|
+
sample_buffer[i1:i2] = data_block
|
|
218
|
+
|
|
219
|
+
samples = np.transpose(
|
|
220
|
+
np.reshape(
|
|
221
|
+
sample_buffer,
|
|
222
|
+
[self.num_samples_per_block * (i + 1), self.num_channels],
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
return samples
|
|
226
|
+
|
|
227
|
+
def _readHeader(self, f):
|
|
228
|
+
header_data = struct.unpack(
|
|
229
|
+
"=31sH81phhBHi4xHHHHHHHiHHH64x", f.read(217)
|
|
230
|
+
)
|
|
231
|
+
magic_number = str(header_data[0])
|
|
232
|
+
version_number = header_data[1]
|
|
233
|
+
self.sample_rate = header_data[3]
|
|
234
|
+
# self.storage_rate=header_data[4]
|
|
235
|
+
self.num_channels = header_data[6] // 2
|
|
236
|
+
self.num_samples = header_data[7]
|
|
237
|
+
self.start_time = datetime.datetime(
|
|
238
|
+
header_data[8],
|
|
239
|
+
header_data[9],
|
|
240
|
+
header_data[10],
|
|
241
|
+
header_data[12],
|
|
242
|
+
header_data[13],
|
|
243
|
+
header_data[14],
|
|
244
|
+
)
|
|
245
|
+
self.num_data_blocks = header_data[15]
|
|
246
|
+
self.num_samples_per_block = header_data[16]
|
|
247
|
+
if magic_number != "b'POLY SAMPLE FILEversion 2.03\\r\\n\\x1a'":
|
|
248
|
+
print("This is not a Poly5 file.")
|
|
249
|
+
elif version_number != 203:
|
|
250
|
+
print("Version number of file is invalid.")
|
|
251
|
+
else:
|
|
252
|
+
print("\t Number of samples: %s " % self.num_samples)
|
|
253
|
+
print("\t Number of channels: %s " % self.num_channels)
|
|
254
|
+
print("\t Sample rate: %s Hz" % self.sample_rate)
|
|
255
|
+
|
|
256
|
+
def _readSignalDescription(self, f):
|
|
257
|
+
chan_list = []
|
|
258
|
+
for ch in range(self.num_channels):
|
|
259
|
+
channel_description = struct.unpack(
|
|
260
|
+
"=41p4x11pffffH62x", f.read(136)
|
|
261
|
+
)
|
|
262
|
+
name = channel_description[0][5:].decode("ascii")
|
|
263
|
+
unit_name = channel_description[1].decode("utf-8")
|
|
264
|
+
ch = Channel(name, unit_name)
|
|
265
|
+
chan_list.append(ch)
|
|
266
|
+
f.read(136)
|
|
267
|
+
return chan_list
|
|
268
|
+
|
|
269
|
+
def _readSignalBlock(self, f, buffer_size, myfmt):
|
|
270
|
+
f.read(86)
|
|
271
|
+
sampleData = f.read(buffer_size * 4)
|
|
272
|
+
DataBlock = struct.unpack(myfmt, sampleData)
|
|
273
|
+
SignalBlock = np.asarray(DataBlock)
|
|
274
|
+
return SignalBlock
|
|
275
|
+
|
|
276
|
+
def close(self):
|
|
277
|
+
self.file_obj.close()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class Channel:
|
|
281
|
+
"""'Channel' represents a device channel. It has the next properties:
|
|
282
|
+
|
|
283
|
+
name : 'string' The name of the channel.
|
|
284
|
+
|
|
285
|
+
unit_name : 'string' The name of the unit (e.g. 'μVolt) of the sample-data of the channel.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
def __init__(self, name, unit_name):
|
|
289
|
+
self.__unit_name = unit_name
|
|
290
|
+
self.__name = name
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
if __name__ == "__main__":
|
|
294
|
+
data = Poly5Reader()
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
(c) 2022 Twente Medical Systems International B.V., Oldenzaal The Netherlands
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
####### # # ##### #
|
|
17
|
+
# ## ## #
|
|
18
|
+
# # # # # # #
|
|
19
|
+
# # # # ##### #
|
|
20
|
+
# # # # #
|
|
21
|
+
# # # # #
|
|
22
|
+
# # # ##### #
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @file ${xdf_reader.py}
|
|
26
|
+
* @brief XDF File Reader.
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from pyxdf import load_xdf
|
|
34
|
+
import mne
|
|
35
|
+
import tkinter as tk
|
|
36
|
+
from tkinter import filedialog
|
|
37
|
+
import numpy as np
|
|
38
|
+
import pandas as pd
|
|
39
|
+
import copy
|
|
40
|
+
|
|
41
|
+
from os.path import join, dirname, realpath
|
|
42
|
+
|
|
43
|
+
Reader_dir = dirname(realpath(__file__)) # directory of this file
|
|
44
|
+
modules_dir = join(Reader_dir, "../../") # directory with all modules
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Xdf_Reader:
|
|
48
|
+
def __init__(self, filename=None, add_ch_locs=False):
|
|
49
|
+
if filename == None:
|
|
50
|
+
root = tk.Tk()
|
|
51
|
+
|
|
52
|
+
filename = filedialog.askopenfilename(
|
|
53
|
+
title="Select xdf-file",
|
|
54
|
+
filetypes=(("xdf-files", "*.xdf"), ("All files", "*.*")),
|
|
55
|
+
)
|
|
56
|
+
root.withdraw()
|
|
57
|
+
|
|
58
|
+
self.filename = filename
|
|
59
|
+
self.add_ch_locs = add_ch_locs
|
|
60
|
+
print("Reading file ", filename)
|
|
61
|
+
self.data, self.time_stamps = self._readFile(filename)
|
|
62
|
+
|
|
63
|
+
def _readFile(self, fname):
|
|
64
|
+
try:
|
|
65
|
+
streams, header = load_xdf(fname)
|
|
66
|
+
num_streams = len(streams)
|
|
67
|
+
self.stream_info = {}
|
|
68
|
+
|
|
69
|
+
print("Number of streams in file: " + str(num_streams))
|
|
70
|
+
for i in range(num_streams):
|
|
71
|
+
stream = streams[i]
|
|
72
|
+
self.stream_info[i] = stream["info"]
|
|
73
|
+
if stream is not None:
|
|
74
|
+
fs = float(stream["info"]["nominal_srate"][0])
|
|
75
|
+
|
|
76
|
+
labels, types, units, impedances = self._get_ch_info(stream)
|
|
77
|
+
|
|
78
|
+
type_options = [
|
|
79
|
+
"ecg",
|
|
80
|
+
"bio",
|
|
81
|
+
"stim",
|
|
82
|
+
"eog",
|
|
83
|
+
"misc",
|
|
84
|
+
"seeg",
|
|
85
|
+
"dbs",
|
|
86
|
+
"ecog",
|
|
87
|
+
"mag",
|
|
88
|
+
"eeg",
|
|
89
|
+
"ref_meg",
|
|
90
|
+
"grad",
|
|
91
|
+
"emg",
|
|
92
|
+
"hbr",
|
|
93
|
+
"hbo",
|
|
94
|
+
]
|
|
95
|
+
for ind, t in enumerate(types):
|
|
96
|
+
if t == "EEG":
|
|
97
|
+
types[ind] = "eeg"
|
|
98
|
+
elif not t in type_options:
|
|
99
|
+
types[ind] = "misc"
|
|
100
|
+
info = mne.create_info(
|
|
101
|
+
ch_names=labels, sfreq=fs, ch_types=types
|
|
102
|
+
)
|
|
103
|
+
info = self._get_ch_locations(stream, info)
|
|
104
|
+
if self.add_ch_locs:
|
|
105
|
+
info = self._add_ch_locations(info)
|
|
106
|
+
|
|
107
|
+
# convert from microvolts to volts if necessary
|
|
108
|
+
scale = np.array(
|
|
109
|
+
[1e-6 if u == "µVolt" else 1 for u in units]
|
|
110
|
+
)
|
|
111
|
+
raw = mne.io.RawArray(
|
|
112
|
+
(stream["time_series"] * scale).T, info
|
|
113
|
+
)
|
|
114
|
+
raw.impedances = impedances
|
|
115
|
+
|
|
116
|
+
if raw is not None:
|
|
117
|
+
print(raw, end="\n\n")
|
|
118
|
+
print(raw.info)
|
|
119
|
+
|
|
120
|
+
if num_streams == 1:
|
|
121
|
+
return (raw,), (stream["time_stamps"],)
|
|
122
|
+
else:
|
|
123
|
+
if i == 0:
|
|
124
|
+
output_data = (copy.copy(raw),)
|
|
125
|
+
output_timestamps = (
|
|
126
|
+
copy.copy(stream["time_stamps"]),
|
|
127
|
+
)
|
|
128
|
+
elif i == num_streams - 1:
|
|
129
|
+
output_data = output_data + (copy.copy(raw),)
|
|
130
|
+
output_timestamps = output_timestamps + (
|
|
131
|
+
copy.copy(stream["time_stamps"]),
|
|
132
|
+
)
|
|
133
|
+
return output_data, output_timestamps
|
|
134
|
+
else:
|
|
135
|
+
output_data = output_data + (copy.copy(raw),)
|
|
136
|
+
output_timestamps = output_timestamps + (
|
|
137
|
+
copy.copy(stream["time_stamps"]),
|
|
138
|
+
)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
print("Reading data failed because of the following error:\n")
|
|
141
|
+
raise
|
|
142
|
+
|
|
143
|
+
def add_impedances(self, imp_filename=None):
|
|
144
|
+
"""Add impedances from .txt-file"""
|
|
145
|
+
if imp_filename == None:
|
|
146
|
+
root = tk.Tk()
|
|
147
|
+
imp_filename = filedialog.askopenfilename(
|
|
148
|
+
title="Select impedance file",
|
|
149
|
+
filetypes=(("text files", "*.txt"), ("All files", "*.*")),
|
|
150
|
+
)
|
|
151
|
+
root.withdraw()
|
|
152
|
+
impedances = []
|
|
153
|
+
|
|
154
|
+
imp_df = pd.read_csv(imp_filename, delimiter="\t", header=None)
|
|
155
|
+
imp_df.columns = ["ch_name", "impedance", "unit"]
|
|
156
|
+
|
|
157
|
+
for ch in range(len(self.data[0].info["chs"])):
|
|
158
|
+
for i_ch in range(len(imp_df)):
|
|
159
|
+
if (
|
|
160
|
+
self.data[0].info["chs"][ch]["ch_name"]
|
|
161
|
+
== imp_df["ch_name"][i_ch]
|
|
162
|
+
):
|
|
163
|
+
impedances.append(imp_df["impedance"][i_ch])
|
|
164
|
+
|
|
165
|
+
self.data[0].impedances = impedances
|
|
166
|
+
|
|
167
|
+
def _get_ch_info(self, stream):
|
|
168
|
+
# read channel labels, types, units and impedances
|
|
169
|
+
labels, types, units, impedances = [], [], [], []
|
|
170
|
+
|
|
171
|
+
for ch in stream["info"]["desc"][0]["channels"][0]["channel"]:
|
|
172
|
+
labels.append(str(ch["label"][0]))
|
|
173
|
+
types.append(ch["type"][0])
|
|
174
|
+
units.append(ch["unit"][0])
|
|
175
|
+
if ch["impedance"]:
|
|
176
|
+
impedances.append(str(ch["impedance"][0]))
|
|
177
|
+
return labels, types, units, impedances
|
|
178
|
+
|
|
179
|
+
def _get_ch_locations(self, stream, info):
|
|
180
|
+
# read channel locations and convert unit from mm to m
|
|
181
|
+
for i, ch in enumerate(
|
|
182
|
+
stream["info"]["desc"][0]["channels"][0]["channel"]
|
|
183
|
+
):
|
|
184
|
+
if ch["location"]:
|
|
185
|
+
info["chs"][i]["loc"][0] = (
|
|
186
|
+
float(ch["location"][0]["X"][0]) * 1e-3
|
|
187
|
+
)
|
|
188
|
+
info["chs"][i]["loc"][1] = (
|
|
189
|
+
float(ch["location"][0]["Y"][0]) * 1e-3
|
|
190
|
+
)
|
|
191
|
+
info["chs"][i]["loc"][2] = (
|
|
192
|
+
float(ch["location"][0]["Z"][0]) * 1e-3
|
|
193
|
+
)
|
|
194
|
+
self.add_ch_locs = False
|
|
195
|
+
return info
|
|
196
|
+
|
|
197
|
+
def _add_ch_locations(self, info):
|
|
198
|
+
# add channel locations from txt file
|
|
199
|
+
chLocs = pd.read_csv(
|
|
200
|
+
join(modules_dir, "TMSiSDK/_resources/EEGchannelsTMSi3D.txt"),
|
|
201
|
+
sep="\t",
|
|
202
|
+
header=None,
|
|
203
|
+
)
|
|
204
|
+
chLocs.columns = ["default_name", "eeg_name", "X", "Y", "Z"]
|
|
205
|
+
|
|
206
|
+
# add locations and convert to head size of 95 mm
|
|
207
|
+
for idx, ch in enumerate(info["chs"]):
|
|
208
|
+
try:
|
|
209
|
+
a = [
|
|
210
|
+
i
|
|
211
|
+
for i, e in (
|
|
212
|
+
enumerate(chLocs["eeg_name"].values)
|
|
213
|
+
or enumerate(chLocs["default_name"].values)
|
|
214
|
+
)
|
|
215
|
+
if e == ch["ch_name"]
|
|
216
|
+
]
|
|
217
|
+
info["chs"][idx]["loc"][0] = 95 * 1e-3 * chLocs["X"].values[a]
|
|
218
|
+
info["chs"][idx]["loc"][1] = 95 * 1e-3 * chLocs["Y"].values[a]
|
|
219
|
+
info["chs"][idx]["loc"][2] = 95 * 1e-3 * chLocs["Z"].values[a]
|
|
220
|
+
except:
|
|
221
|
+
pass
|
|
222
|
+
return info
|
|
223
|
+
|
|
224
|
+
def get_stream_info(self):
|
|
225
|
+
# Retrieve the information from the streams in the data.
|
|
226
|
+
if hasattr(self, "stream_info"):
|
|
227
|
+
return self.stream_info
|
|
228
|
+
else:
|
|
229
|
+
return None
|