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