py-neuromodulation 0.0.3__py3-none-any.whl → 0.0.4__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.
Files changed (176) hide show
  1. py_neuromodulation/ConnectivityDecoding/Automated Anatomical Labeling 3 (Rolls 2020).nii +0 -0
  2. py_neuromodulation/ConnectivityDecoding/_get_grid_hull.m +34 -0
  3. py_neuromodulation/ConnectivityDecoding/_get_grid_whole_brain.py +106 -0
  4. py_neuromodulation/ConnectivityDecoding/_helper_write_connectome.py +119 -0
  5. py_neuromodulation/ConnectivityDecoding/mni_coords_cortical_surface.mat +0 -0
  6. py_neuromodulation/ConnectivityDecoding/mni_coords_whole_brain.mat +0 -0
  7. py_neuromodulation/ConnectivityDecoding/rmap_func_all.nii +0 -0
  8. py_neuromodulation/ConnectivityDecoding/rmap_struc.nii +0 -0
  9. py_neuromodulation/data/README +6 -0
  10. py_neuromodulation/data/dataset_description.json +8 -0
  11. py_neuromodulation/data/participants.json +32 -0
  12. py_neuromodulation/data/participants.tsv +2 -0
  13. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +5 -0
  14. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +11 -0
  15. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +11 -0
  16. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
  17. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +18 -0
  18. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +35 -0
  19. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +13 -0
  20. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +2 -0
  21. py_neuromodulation/grid_cortex.tsv +40 -0
  22. py_neuromodulation/grid_subcortex.tsv +1429 -0
  23. py_neuromodulation/nm_settings.json +338 -0
  24. py_neuromodulation/nm_stream_offline.py +7 -6
  25. py_neuromodulation/plots/STN_surf.mat +0 -0
  26. py_neuromodulation/plots/Vertices.mat +0 -0
  27. py_neuromodulation/plots/faces.mat +0 -0
  28. py_neuromodulation/plots/grid.mat +0 -0
  29. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.4.dist-info}/METADATA +182 -182
  30. py_neuromodulation-0.0.4.dist-info/RECORD +72 -0
  31. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.4.dist-info}/WHEEL +1 -2
  32. docs/build/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -68
  33. docs/build/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -233
  34. docs/build/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  35. docs/build/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -97
  36. docs/build/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  37. docs/build/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -192
  38. docs/build/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  39. docs/build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -68
  40. docs/build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -239
  41. docs/build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  42. docs/build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -97
  43. docs/build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  44. docs/build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -192
  45. docs/build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  46. docs/source/_build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -76
  47. docs/source/_build/html/_downloads/0d0d0a76e8f648d5d3cbc47da6351932/plot_real_time_demo.py +0 -97
  48. docs/source/_build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -240
  49. docs/source/_build/html/_downloads/5d73cadc59a8805c47e3b84063afc157/plot_example_BIDS.py +0 -233
  50. docs/source/_build/html/_downloads/7660317fa5a6bfbd12fcca9961457fc4/plot_example_rmap_computing.py +0 -63
  51. docs/source/_build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  52. docs/source/_build/html/_downloads/839e5b319379f7fd9e867deb00fd797f/plot_example_gridPointProjection.py +0 -210
  53. docs/source/_build/html/_downloads/ae8be19afe5e559f011fc9b138968ba0/plot_first_demo.py +0 -192
  54. docs/source/_build/html/_downloads/b8b06cacc17969d3725a0b6f1d7741c5/plot_example_sharpwave_analysis.py +0 -219
  55. docs/source/_build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -121
  56. docs/source/_build/html/_downloads/c31a86c0b68cb4167d968091ace8080d/plot_example_add_feature.py +0 -68
  57. docs/source/_build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  58. docs/source/_build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -189
  59. docs/source/_build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  60. docs/source/auto_examples/plot_0_first_demo.py +0 -189
  61. docs/source/auto_examples/plot_1_example_BIDS.py +0 -240
  62. docs/source/auto_examples/plot_2_example_add_feature.py +0 -76
  63. docs/source/auto_examples/plot_3_example_sharpwave_analysis.py +0 -219
  64. docs/source/auto_examples/plot_4_example_gridPointProjection.py +0 -210
  65. docs/source/auto_examples/plot_5_example_rmap_computing.py +0 -64
  66. docs/source/auto_examples/plot_6_real_time_demo.py +0 -121
  67. docs/source/conf.py +0 -105
  68. examples/plot_0_first_demo.py +0 -189
  69. examples/plot_1_example_BIDS.py +0 -240
  70. examples/plot_2_example_add_feature.py +0 -76
  71. examples/plot_3_example_sharpwave_analysis.py +0 -219
  72. examples/plot_4_example_gridPointProjection.py +0 -210
  73. examples/plot_5_example_rmap_computing.py +0 -64
  74. examples/plot_6_real_time_demo.py +0 -121
  75. packages/realtime_decoding/build/lib/realtime_decoding/__init__.py +0 -4
  76. packages/realtime_decoding/build/lib/realtime_decoding/decoder.py +0 -104
  77. packages/realtime_decoding/build/lib/realtime_decoding/features.py +0 -163
  78. packages/realtime_decoding/build/lib/realtime_decoding/helpers.py +0 -15
  79. packages/realtime_decoding/build/lib/realtime_decoding/run_decoding.py +0 -345
  80. packages/realtime_decoding/build/lib/realtime_decoding/trainer.py +0 -54
  81. packages/tmsi/build/lib/TMSiFileFormats/__init__.py +0 -37
  82. packages/tmsi/build/lib/TMSiFileFormats/file_formats/__init__.py +0 -36
  83. packages/tmsi/build/lib/TMSiFileFormats/file_formats/lsl_stream_writer.py +0 -200
  84. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_file_writer.py +0 -496
  85. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_to_edf_converter.py +0 -236
  86. packages/tmsi/build/lib/TMSiFileFormats/file_formats/xdf_file_writer.py +0 -977
  87. packages/tmsi/build/lib/TMSiFileFormats/file_readers/__init__.py +0 -35
  88. packages/tmsi/build/lib/TMSiFileFormats/file_readers/edf_reader.py +0 -116
  89. packages/tmsi/build/lib/TMSiFileFormats/file_readers/poly5reader.py +0 -294
  90. packages/tmsi/build/lib/TMSiFileFormats/file_readers/xdf_reader.py +0 -229
  91. packages/tmsi/build/lib/TMSiFileFormats/file_writer.py +0 -102
  92. packages/tmsi/build/lib/TMSiPlotters/__init__.py +0 -2
  93. packages/tmsi/build/lib/TMSiPlotters/gui/__init__.py +0 -39
  94. packages/tmsi/build/lib/TMSiPlotters/gui/_plotter_gui.py +0 -234
  95. packages/tmsi/build/lib/TMSiPlotters/gui/plotting_gui.py +0 -440
  96. packages/tmsi/build/lib/TMSiPlotters/plotters/__init__.py +0 -44
  97. packages/tmsi/build/lib/TMSiPlotters/plotters/hd_emg_plotter.py +0 -446
  98. packages/tmsi/build/lib/TMSiPlotters/plotters/impedance_plotter.py +0 -589
  99. packages/tmsi/build/lib/TMSiPlotters/plotters/signal_plotter.py +0 -1326
  100. packages/tmsi/build/lib/TMSiSDK/__init__.py +0 -54
  101. packages/tmsi/build/lib/TMSiSDK/device.py +0 -588
  102. packages/tmsi/build/lib/TMSiSDK/devices/__init__.py +0 -34
  103. packages/tmsi/build/lib/TMSiSDK/devices/saga/TMSi_Device_API.py +0 -1764
  104. packages/tmsi/build/lib/TMSiSDK/devices/saga/__init__.py +0 -34
  105. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_device.py +0 -1366
  106. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_types.py +0 -520
  107. packages/tmsi/build/lib/TMSiSDK/devices/saga/xml_saga_config.py +0 -165
  108. packages/tmsi/build/lib/TMSiSDK/error.py +0 -95
  109. packages/tmsi/build/lib/TMSiSDK/sample_data.py +0 -63
  110. packages/tmsi/build/lib/TMSiSDK/sample_data_server.py +0 -99
  111. packages/tmsi/build/lib/TMSiSDK/settings.py +0 -45
  112. packages/tmsi/build/lib/TMSiSDK/tmsi_device.py +0 -111
  113. packages/tmsi/build/lib/__init__.py +0 -4
  114. packages/tmsi/build/lib/apex_sdk/__init__.py +0 -34
  115. packages/tmsi/build/lib/apex_sdk/device/__init__.py +0 -41
  116. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API.py +0 -1009
  117. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_enums.py +0 -239
  118. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_structures.py +0 -668
  119. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_device.py +0 -1611
  120. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_dongle.py +0 -38
  121. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_event_reader.py +0 -57
  122. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_channel.py +0 -44
  123. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_config.py +0 -150
  124. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_const.py +0 -36
  125. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_impedance_channel.py +0 -48
  126. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_info.py +0 -108
  127. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/dongle_info.py +0 -39
  128. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/download_measurement.py +0 -77
  129. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/eeg_measurement.py +0 -150
  130. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/impedance_measurement.py +0 -129
  131. packages/tmsi/build/lib/apex_sdk/device/threads/conversion_thread.py +0 -59
  132. packages/tmsi/build/lib/apex_sdk/device/threads/sampling_thread.py +0 -57
  133. packages/tmsi/build/lib/apex_sdk/device/tmsi_channel.py +0 -83
  134. packages/tmsi/build/lib/apex_sdk/device/tmsi_device.py +0 -201
  135. packages/tmsi/build/lib/apex_sdk/device/tmsi_device_enums.py +0 -103
  136. packages/tmsi/build/lib/apex_sdk/device/tmsi_dongle.py +0 -43
  137. packages/tmsi/build/lib/apex_sdk/device/tmsi_event_reader.py +0 -50
  138. packages/tmsi/build/lib/apex_sdk/device/tmsi_measurement.py +0 -118
  139. packages/tmsi/build/lib/apex_sdk/sample_data_server/__init__.py +0 -33
  140. packages/tmsi/build/lib/apex_sdk/sample_data_server/event_data.py +0 -44
  141. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data.py +0 -50
  142. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data_server.py +0 -136
  143. packages/tmsi/build/lib/apex_sdk/tmsi_errors/error.py +0 -126
  144. packages/tmsi/build/lib/apex_sdk/tmsi_sdk.py +0 -113
  145. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/apex/apex_structure_generator.py +0 -134
  146. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/decorators.py +0 -60
  147. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/logger_filter.py +0 -42
  148. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/singleton.py +0 -42
  149. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/support_functions.py +0 -72
  150. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/tmsi_logger.py +0 -98
  151. py_neuromodulation-0.0.3.dist-info/RECORD +0 -188
  152. py_neuromodulation-0.0.3.dist-info/top_level.txt +0 -5
  153. tests/__init__.py +0 -0
  154. tests/conftest.py +0 -117
  155. tests/test_all_examples.py +0 -10
  156. tests/test_all_features.py +0 -63
  157. tests/test_bispectra.py +0 -70
  158. tests/test_bursts.py +0 -105
  159. tests/test_feature_sampling_rates.py +0 -143
  160. tests/test_fooof.py +0 -16
  161. tests/test_initalization_offline_stream.py +0 -41
  162. tests/test_multiprocessing.py +0 -58
  163. tests/test_nan_values.py +0 -29
  164. tests/test_nm_filter.py +0 -95
  165. tests/test_nm_resample.py +0 -63
  166. tests/test_normalization_settings.py +0 -146
  167. tests/test_notch_filter.py +0 -31
  168. tests/test_osc_features.py +0 -424
  169. tests/test_preprocessing_filter.py +0 -151
  170. tests/test_rereference.py +0 -171
  171. tests/test_sampling.py +0 -57
  172. tests/test_settings_change_after_init.py +0 -76
  173. tests/test_sharpwave.py +0 -165
  174. tests/test_target_channel_add.py +0 -100
  175. tests/test_timing.py +0 -80
  176. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.4.dist-info/licenses}/LICENSE +0 -0
@@ -1,35 +0,0 @@
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
@@ -1,116 +0,0 @@
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
@@ -1,294 +0,0 @@
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()
@@ -1,229 +0,0 @@
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