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,496 @@
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 ${poly5_file_writer.py}
26
+ * @brief Poly5 File Writer
27
+ *
28
+ */
29
+
30
+
31
+ """
32
+
33
+ from datetime import datetime
34
+
35
+ import os
36
+ import threading
37
+ import queue
38
+ import struct
39
+ import time
40
+ import numpy as np
41
+
42
+ from TMSiSDK.error import TMSiError, TMSiErrorCode
43
+ from TMSiSDK import sample_data_server
44
+ from apex_sdk.device.tmsi_device import TMSiDevice
45
+ from apex_sdk.sample_data_server.sample_data_server import (
46
+ SampleDataServer as ApexSampleDataServer,
47
+ )
48
+
49
+ _QUEUE_SIZE = 1000
50
+
51
+
52
+ class Poly5Writer:
53
+ def __init__(self, filename, download=False):
54
+ self.q_sample_sets = queue.Queue(_QUEUE_SIZE)
55
+ self.device = None
56
+
57
+ fileparts = filename.split(".")
58
+ if not download:
59
+ now = datetime.now()
60
+ filetime = now.strftime("%Y%m%d_%H%M%S")
61
+ if fileparts[-1] == "poly5" or fileparts[-1] == "Poly5":
62
+ self.filename = (
63
+ ".".join(fileparts[:-1]) + "-" + filetime + ".poly5"
64
+ )
65
+ else:
66
+ self.filename = filename + "-" + filetime + ".poly5"
67
+ else:
68
+ if fileparts[-1] == "poly5" or fileparts[-1] == "Poly5":
69
+ self.filename = filename
70
+ else:
71
+ self.filename = filename + ".poly5"
72
+
73
+ self._fp = None
74
+ self._date = None
75
+
76
+ def open(self, device):
77
+ if isinstance(device, TMSiDevice):
78
+ self.__open_TMSiDevice(device)
79
+ return
80
+
81
+ self.device = device
82
+ try:
83
+ self._fp = open(self.filename, "wb")
84
+ self._date = datetime.now()
85
+ self._sample_rate = device.config.sample_rate
86
+ self._num_channels = len(device.channels)
87
+
88
+ # Calculate nr of sample-sets within one sample-data-block:
89
+ # This is the nr of sample-sets in 150 milli-seconds or when the
90
+ # sample-data-block-size exceeds 64kb the it will become the nr of
91
+ # sample-sets that fit in 64kb
92
+ self._num_sample_sets_per_sample_data_block = int(
93
+ self._sample_rate * 0.15
94
+ )
95
+ size_one_sample_set = len(self.device.channels) * 4
96
+ if (
97
+ self._num_sample_sets_per_sample_data_block
98
+ * size_one_sample_set
99
+ ) > 64000:
100
+ self._num_sample_sets_per_sample_data_block = int(
101
+ 64000 / size_one_sample_set
102
+ )
103
+
104
+ # Write poly5-header for thsi measurement
105
+ Poly5Writer._writeHeader(
106
+ self._fp,
107
+ "measurement",
108
+ device.config.sample_rate,
109
+ len(device.channels),
110
+ len(device.channels),
111
+ 0,
112
+ 0,
113
+ self._date,
114
+ )
115
+ for (i, channel) in enumerate(self.device.channels):
116
+ Poly5Writer._writeSignalDescription(
117
+ self._fp, i, channel.name, channel.unit_name
118
+ )
119
+
120
+ fmt = (
121
+ "f"
122
+ * self._num_channels
123
+ * self._num_sample_sets_per_sample_data_block
124
+ )
125
+ self.pack_struct = struct.Struct(fmt)
126
+
127
+ sample_data_server.registerConsumer(
128
+ self.device.id, self.q_sample_sets
129
+ )
130
+
131
+ self._sampling_thread = ConsumerThread(
132
+ self, name="poly5-writer : dev-id-" + str(self.device.id)
133
+ )
134
+ self._sampling_thread.start()
135
+ except OSError as e:
136
+ print(e)
137
+ raise TMSiError(TMSiErrorCode.file_writer_error)
138
+ except:
139
+ raise TMSiError(TMSiErrorCode.file_writer_error)
140
+
141
+ def __open_TMSiDevice(self, device):
142
+ self.device = device
143
+ try:
144
+ self._fp = open(self.filename, "wb")
145
+ self._date = datetime.now()
146
+ self._sample_rate = self.device.get_device_sampling_frequency()
147
+ self._num_channels = self.device.get_num_channels()
148
+
149
+ # Calculate nr of sample-sets within one sample-data-block:
150
+ # This is the nr of sample-sets in 150 milli-seconds or when the
151
+ # sample-data-block-size exceeds 64kb the it will become the nr of
152
+ # sample-sets that fit in 64kb
153
+ self._num_sample_sets_per_sample_data_block = int(
154
+ self._sample_rate * 0.15
155
+ )
156
+ size_one_sample_set = self._num_channels * 4
157
+ if (
158
+ self._num_sample_sets_per_sample_data_block
159
+ * size_one_sample_set
160
+ ) > 64000:
161
+ self._num_sample_sets_per_sample_data_block = int(
162
+ 64000 / size_one_sample_set
163
+ )
164
+
165
+ # Write poly5-header for thsi measurement
166
+ Poly5Writer._writeHeader(
167
+ self._fp,
168
+ "measurement",
169
+ self._sample_rate,
170
+ self._num_channels,
171
+ self._num_channels,
172
+ 0,
173
+ 0,
174
+ self._date,
175
+ )
176
+ for (i, channel) in enumerate(self.device.get_device_channels()):
177
+ Poly5Writer._writeSignalDescription(
178
+ self._fp,
179
+ i,
180
+ channel.get_channel_name(),
181
+ channel.get_channel_unit_name(),
182
+ )
183
+
184
+ fmt = (
185
+ "f"
186
+ * self._num_channels
187
+ * self._num_sample_sets_per_sample_data_block
188
+ )
189
+ self.pack_struct = struct.Struct(fmt)
190
+
191
+ ApexSampleDataServer().register_consumer(
192
+ self.device.get_id(), self.q_sample_sets
193
+ )
194
+
195
+ self._sampling_thread = ConsumerThread(
196
+ self, name="poly5-writer : dev-id-" + str(self.device.get_id())
197
+ )
198
+ self._sampling_thread.start()
199
+ except OSError as e:
200
+ print(e)
201
+ raise TMSiError(TMSiErrorCode.file_writer_error)
202
+ except:
203
+ raise TMSiError(TMSiErrorCode.file_writer_error)
204
+
205
+ def close(self):
206
+ # print("Poly5Writer-close")
207
+ self._sampling_thread.stop_sampling()
208
+
209
+ if isinstance(self.device, TMSiDevice):
210
+ ApexSampleDataServer().unregister_consumer(
211
+ self.device.get_id(), self.q_sample_sets
212
+ )
213
+ else:
214
+ sample_data_server.unregisterConsumer(
215
+ self.device.id, self.q_sample_sets
216
+ )
217
+
218
+ ## Write header of a poly5 file.
219
+ #
220
+ # This function writes the header of a poly5 file to a file.
221
+ # @param f File object
222
+ # @param name Name of measurement
223
+ # @param numSignals Number of signals
224
+ # @param numSamples Number of samples
225
+ # @param numDataBlocks Number of data blocks
226
+ # @param date Date of measurement
227
+ @staticmethod
228
+ def _writeHeader(
229
+ f,
230
+ name,
231
+ sample_rate,
232
+ num_signals,
233
+ num_samples,
234
+ num_data_blocks,
235
+ num_sample_sets_per_sample_data_block,
236
+ date,
237
+ ):
238
+
239
+ data = struct.pack(
240
+ "=31sH81phhBHi4xHHHHHHHiHHH64x",
241
+ b"POLY SAMPLE FILEversion 2.03\r\n\x1a",
242
+ 203,
243
+ bytes(name, "ascii"),
244
+ int(sample_rate),
245
+ int(sample_rate),
246
+ 0,
247
+ num_signals * 2,
248
+ num_samples,
249
+ date.year,
250
+ date.month,
251
+ date.day,
252
+ date.isoweekday() % 7,
253
+ date.hour,
254
+ date.minute,
255
+ date.second,
256
+ num_data_blocks,
257
+ num_sample_sets_per_sample_data_block,
258
+ num_signals * 2 * num_sample_sets_per_sample_data_block * 2,
259
+ 0,
260
+ )
261
+ f.write(data)
262
+
263
+ ## Write a signal description
264
+ #
265
+ # @param f File object
266
+ # @param index Index of the signal description
267
+ # @param name Name of the signal (channel)
268
+ # @param unitname The unit name of the signal
269
+ @staticmethod
270
+ def _writeSignalDescription(f, index, name, unit_name):
271
+ data = struct.pack(
272
+ "=41p4x11pffffH62x",
273
+ bytes("(Lo) " + name, "ascii"),
274
+ bytes(unit_name, "utf-8"),
275
+ 0.0,
276
+ 1000.0,
277
+ 0.0,
278
+ 1000.0,
279
+ index,
280
+ )
281
+ f.write(data)
282
+
283
+ data = struct.pack(
284
+ "=41p4x11pffffH62x",
285
+ bytes("(Hi) " + name, "ascii"),
286
+ bytes(unit_name, "utf-8"),
287
+ 0.0,
288
+ 1000.0,
289
+ 0.0,
290
+ 1000.0,
291
+ index,
292
+ )
293
+ f.write(data)
294
+
295
+ ## Write a signal block
296
+ #
297
+ # @param f File object
298
+ # @param index Index of the data block
299
+ # @param date Date of the sample_data block (measurement)
300
+ # @param signals A list of sample_data, containing NumPy arrays
301
+ @staticmethod
302
+ def _writeSignalBlock(
303
+ f,
304
+ index,
305
+ date,
306
+ sample_sets_block,
307
+ num_sample_sets_per_sample_data_block,
308
+ n_chan,
309
+ pack_struct,
310
+ ):
311
+ data = struct.pack(
312
+ "=i4xHHHHHHH64x",
313
+ int(index * num_sample_sets_per_sample_data_block),
314
+ date.year,
315
+ date.month,
316
+ date.day,
317
+ date.isoweekday() % 7,
318
+ date.hour,
319
+ date.minute,
320
+ date.second,
321
+ )
322
+ f.write(data)
323
+
324
+ sample_sets_block[n_chan - 1 :: n_chan] = sample_sets_block[
325
+ n_chan - 1 :: n_chan
326
+ ] % (2**24)
327
+
328
+ bin = pack_struct.pack(*sample_sets_block)
329
+ f.write(bin)
330
+
331
+
332
+ class ConsumerThread(threading.Thread):
333
+ def __init__(self, file_writer, name):
334
+ super(ConsumerThread, self).__init__()
335
+ self.name = name
336
+ self.q_sample_sets = file_writer.q_sample_sets
337
+ self.sampling = True
338
+ self._sample_set_block_index = 0
339
+ self._date = file_writer._date
340
+ self._fp = file_writer._fp
341
+ self._sample_rate = file_writer._sample_rate
342
+ self._num_channels = file_writer._num_channels
343
+ self._num_sample_sets_per_sample_data_block = (
344
+ file_writer._num_sample_sets_per_sample_data_block
345
+ )
346
+ self._sample_sets_in_block = []
347
+ self.pack_struct = file_writer.pack_struct
348
+ self._remaining_samples = np.array([])
349
+
350
+ def run(self):
351
+ # print(self.name, " started")
352
+
353
+ while (self.sampling) or (not self.q_sample_sets.empty()):
354
+ while not self.q_sample_sets.empty():
355
+ sd = self.q_sample_sets.get()
356
+ self.q_sample_sets.task_done()
357
+
358
+ if self._remaining_samples.size:
359
+ samples = np.concatenate(
360
+ (self._remaining_samples, sd.samples)
361
+ )
362
+ else:
363
+ samples = np.array(sd.samples)
364
+
365
+ n_samp = int(len(samples) / sd.num_samples_per_sample_set)
366
+
367
+ try:
368
+ for i in range(
369
+ np.int(
370
+ np.floor(
371
+ n_samp
372
+ / self._num_sample_sets_per_sample_data_block
373
+ )
374
+ )
375
+ ):
376
+ self._sample_sets_in_block = samples[
377
+ i
378
+ * self._num_sample_sets_per_sample_data_block
379
+ * sd.num_samples_per_sample_set : (i + 1)
380
+ * self._num_sample_sets_per_sample_data_block
381
+ * sd.num_samples_per_sample_set
382
+ ]
383
+ Poly5Writer._writeSignalBlock(
384
+ self._fp,
385
+ self._sample_set_block_index,
386
+ self._date,
387
+ self._sample_sets_in_block,
388
+ self._num_sample_sets_per_sample_data_block,
389
+ self._num_channels,
390
+ self.pack_struct,
391
+ )
392
+ self._sample_set_block_index += 1
393
+
394
+ if not (self._sample_set_block_index % 20):
395
+ # Go back to start and rewrite header
396
+ self._fp.seek(0)
397
+ Poly5Writer._writeHeader(
398
+ self._fp,
399
+ "measurement",
400
+ self._sample_rate,
401
+ self._num_channels,
402
+ self._sample_set_block_index
403
+ * self._num_sample_sets_per_sample_data_block,
404
+ self._sample_set_block_index,
405
+ self._num_sample_sets_per_sample_data_block,
406
+ self._date,
407
+ )
408
+
409
+ # Flush all data from buffers to the file
410
+ self._fp.flush()
411
+ os.fsync(self._fp.fileno())
412
+
413
+ # Go back to end of file
414
+ self._fp.seek(0, os.SEEK_END)
415
+
416
+ i = np.int(
417
+ np.floor(
418
+ n_samp / self._num_sample_sets_per_sample_data_block
419
+ )
420
+ )
421
+ ind = np.arange(
422
+ i
423
+ * self._num_sample_sets_per_sample_data_block
424
+ * sd.num_samples_per_sample_set,
425
+ n_samp * sd.num_samples_per_sample_set,
426
+ )
427
+ if ind.any:
428
+ self._remaining_samples = samples[ind]
429
+ else:
430
+ self._remaining_samples = np.array([])
431
+
432
+ except:
433
+ raise TMSiError(TMSiErrorCode.file_writer_error)
434
+
435
+ time.sleep(0.01)
436
+
437
+ while self._remaining_samples.any():
438
+ # Last data block is omitted from saving, to prevent an incomplete data block being part of the Poly5 file
439
+ # This would result in 0s at the end of the file
440
+ if (
441
+ np.shape(self._remaining_samples)[0]
442
+ < self._num_sample_sets_per_sample_data_block
443
+ * self._num_channels
444
+ ):
445
+ self._remaining_samples = np.array([])
446
+
447
+ else:
448
+ self._sample_sets_in_block = self._remaining_samples[
449
+ : self._num_sample_sets_per_sample_data_block
450
+ * self._num_channels
451
+ ]
452
+
453
+ self._remaining_samples = self._remaining_samples[
454
+ self._num_sample_sets_per_sample_data_block
455
+ * self._num_channels :
456
+ ]
457
+
458
+ Poly5Writer._writeSignalBlock(
459
+ self._fp,
460
+ self._sample_set_block_index,
461
+ self._date,
462
+ self._sample_sets_in_block,
463
+ self._num_sample_sets_per_sample_data_block,
464
+ self._num_channels,
465
+ self.pack_struct,
466
+ )
467
+ self._sample_set_block_index += 1
468
+
469
+ # Go back to start and rewrite header
470
+ self._fp.seek(0)
471
+ Poly5Writer._writeHeader(
472
+ self._fp,
473
+ "measurement",
474
+ self._sample_rate,
475
+ self._num_channels,
476
+ self._sample_set_block_index
477
+ * self._num_sample_sets_per_sample_data_block,
478
+ self._sample_set_block_index,
479
+ self._num_sample_sets_per_sample_data_block,
480
+ self._date,
481
+ )
482
+
483
+ # Flush all data from buffers to the file
484
+ self._fp.flush()
485
+ os.fsync(self._fp.fileno())
486
+
487
+ # Go back to end of file
488
+ self._fp.seek(0, os.SEEK_END)
489
+
490
+ # print(self.name, " ready, closing file")
491
+ self._fp.close()
492
+ return
493
+
494
+ def stop_sampling(self):
495
+ # print(self.name, " stop sampling")
496
+ self.sampling = False