py-neuromodulation 0.0.3__py3-none-any.whl → 0.0.5__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 (233) 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 +95 -0
  4. py_neuromodulation/ConnectivityDecoding/_helper_write_connectome.py +107 -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/FieldTrip.py +589 -589
  10. py_neuromodulation/__init__.py +74 -13
  11. py_neuromodulation/_write_example_dataset_helper.py +83 -65
  12. py_neuromodulation/data/README +6 -0
  13. py_neuromodulation/data/dataset_description.json +8 -0
  14. py_neuromodulation/data/participants.json +32 -0
  15. py_neuromodulation/data/participants.tsv +2 -0
  16. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +5 -0
  17. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +11 -0
  18. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +11 -0
  19. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
  20. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +18 -0
  21. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +35 -0
  22. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +13 -0
  23. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +2 -0
  24. py_neuromodulation/grid_cortex.tsv +40 -0
  25. py_neuromodulation/grid_subcortex.tsv +1429 -0
  26. py_neuromodulation/liblsl/libpugixml.so.1.12 +0 -0
  27. py_neuromodulation/liblsl/linux/bionic_amd64/liblsl.1.16.2.so +0 -0
  28. py_neuromodulation/liblsl/linux/bookworm_amd64/liblsl.1.16.2.so +0 -0
  29. py_neuromodulation/liblsl/linux/focal_amd46/liblsl.1.16.2.so +0 -0
  30. py_neuromodulation/liblsl/linux/jammy_amd64/liblsl.1.16.2.so +0 -0
  31. py_neuromodulation/liblsl/linux/jammy_x86/liblsl.1.16.2.so +0 -0
  32. py_neuromodulation/liblsl/linux/noble_amd64/liblsl.1.16.2.so +0 -0
  33. py_neuromodulation/liblsl/macos/amd64/liblsl.1.16.2.dylib +0 -0
  34. py_neuromodulation/liblsl/macos/arm64/liblsl.1.16.0.dylib +0 -0
  35. py_neuromodulation/liblsl/windows/amd64/liblsl.1.16.2.dll +0 -0
  36. py_neuromodulation/liblsl/windows/x86/liblsl.1.16.2.dll +0 -0
  37. py_neuromodulation/nm_IO.py +413 -417
  38. py_neuromodulation/nm_RMAP.py +496 -531
  39. py_neuromodulation/nm_analysis.py +993 -1074
  40. py_neuromodulation/nm_artifacts.py +30 -25
  41. py_neuromodulation/nm_bispectra.py +154 -168
  42. py_neuromodulation/nm_bursts.py +292 -198
  43. py_neuromodulation/nm_coherence.py +251 -205
  44. py_neuromodulation/nm_database.py +149 -0
  45. py_neuromodulation/nm_decode.py +918 -992
  46. py_neuromodulation/nm_define_nmchannels.py +300 -302
  47. py_neuromodulation/nm_features.py +144 -116
  48. py_neuromodulation/nm_filter.py +219 -219
  49. py_neuromodulation/nm_filter_preprocessing.py +79 -91
  50. py_neuromodulation/nm_fooof.py +139 -159
  51. py_neuromodulation/nm_generator.py +45 -37
  52. py_neuromodulation/nm_hjorth_raw.py +52 -73
  53. py_neuromodulation/nm_kalmanfilter.py +71 -58
  54. py_neuromodulation/nm_linelength.py +21 -33
  55. py_neuromodulation/nm_logger.py +66 -0
  56. py_neuromodulation/nm_mne_connectivity.py +149 -112
  57. py_neuromodulation/nm_mnelsl_generator.py +90 -0
  58. py_neuromodulation/nm_mnelsl_stream.py +116 -0
  59. py_neuromodulation/nm_nolds.py +96 -93
  60. py_neuromodulation/nm_normalization.py +173 -214
  61. py_neuromodulation/nm_oscillatory.py +423 -448
  62. py_neuromodulation/nm_plots.py +585 -612
  63. py_neuromodulation/nm_preprocessing.py +83 -0
  64. py_neuromodulation/nm_projection.py +370 -394
  65. py_neuromodulation/nm_rereference.py +97 -95
  66. py_neuromodulation/nm_resample.py +59 -50
  67. py_neuromodulation/nm_run_analysis.py +325 -435
  68. py_neuromodulation/nm_settings.py +289 -68
  69. py_neuromodulation/nm_settings.yaml +244 -0
  70. py_neuromodulation/nm_sharpwaves.py +423 -401
  71. py_neuromodulation/nm_stats.py +464 -480
  72. py_neuromodulation/nm_stream.py +398 -0
  73. py_neuromodulation/nm_stream_abc.py +166 -218
  74. py_neuromodulation/nm_types.py +193 -0
  75. py_neuromodulation/plots/STN_surf.mat +0 -0
  76. py_neuromodulation/plots/Vertices.mat +0 -0
  77. py_neuromodulation/plots/faces.mat +0 -0
  78. py_neuromodulation/plots/grid.mat +0 -0
  79. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.5.dist-info}/METADATA +185 -182
  80. py_neuromodulation-0.0.5.dist-info/RECORD +83 -0
  81. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.5.dist-info}/WHEEL +1 -2
  82. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.5.dist-info/licenses}/LICENSE +21 -21
  83. docs/build/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -68
  84. docs/build/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -233
  85. docs/build/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  86. docs/build/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -97
  87. docs/build/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  88. docs/build/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -192
  89. docs/build/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  90. docs/build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -68
  91. docs/build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -239
  92. docs/build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  93. docs/build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -97
  94. docs/build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  95. docs/build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -192
  96. docs/build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  97. docs/source/_build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -76
  98. docs/source/_build/html/_downloads/0d0d0a76e8f648d5d3cbc47da6351932/plot_real_time_demo.py +0 -97
  99. docs/source/_build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -240
  100. docs/source/_build/html/_downloads/5d73cadc59a8805c47e3b84063afc157/plot_example_BIDS.py +0 -233
  101. docs/source/_build/html/_downloads/7660317fa5a6bfbd12fcca9961457fc4/plot_example_rmap_computing.py +0 -63
  102. docs/source/_build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  103. docs/source/_build/html/_downloads/839e5b319379f7fd9e867deb00fd797f/plot_example_gridPointProjection.py +0 -210
  104. docs/source/_build/html/_downloads/ae8be19afe5e559f011fc9b138968ba0/plot_first_demo.py +0 -192
  105. docs/source/_build/html/_downloads/b8b06cacc17969d3725a0b6f1d7741c5/plot_example_sharpwave_analysis.py +0 -219
  106. docs/source/_build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -121
  107. docs/source/_build/html/_downloads/c31a86c0b68cb4167d968091ace8080d/plot_example_add_feature.py +0 -68
  108. docs/source/_build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  109. docs/source/_build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -189
  110. docs/source/_build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  111. docs/source/auto_examples/plot_0_first_demo.py +0 -189
  112. docs/source/auto_examples/plot_1_example_BIDS.py +0 -240
  113. docs/source/auto_examples/plot_2_example_add_feature.py +0 -76
  114. docs/source/auto_examples/plot_3_example_sharpwave_analysis.py +0 -219
  115. docs/source/auto_examples/plot_4_example_gridPointProjection.py +0 -210
  116. docs/source/auto_examples/plot_5_example_rmap_computing.py +0 -64
  117. docs/source/auto_examples/plot_6_real_time_demo.py +0 -121
  118. docs/source/conf.py +0 -105
  119. examples/plot_0_first_demo.py +0 -189
  120. examples/plot_1_example_BIDS.py +0 -240
  121. examples/plot_2_example_add_feature.py +0 -76
  122. examples/plot_3_example_sharpwave_analysis.py +0 -219
  123. examples/plot_4_example_gridPointProjection.py +0 -210
  124. examples/plot_5_example_rmap_computing.py +0 -64
  125. examples/plot_6_real_time_demo.py +0 -121
  126. packages/realtime_decoding/build/lib/realtime_decoding/__init__.py +0 -4
  127. packages/realtime_decoding/build/lib/realtime_decoding/decoder.py +0 -104
  128. packages/realtime_decoding/build/lib/realtime_decoding/features.py +0 -163
  129. packages/realtime_decoding/build/lib/realtime_decoding/helpers.py +0 -15
  130. packages/realtime_decoding/build/lib/realtime_decoding/run_decoding.py +0 -345
  131. packages/realtime_decoding/build/lib/realtime_decoding/trainer.py +0 -54
  132. packages/tmsi/build/lib/TMSiFileFormats/__init__.py +0 -37
  133. packages/tmsi/build/lib/TMSiFileFormats/file_formats/__init__.py +0 -36
  134. packages/tmsi/build/lib/TMSiFileFormats/file_formats/lsl_stream_writer.py +0 -200
  135. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_file_writer.py +0 -496
  136. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_to_edf_converter.py +0 -236
  137. packages/tmsi/build/lib/TMSiFileFormats/file_formats/xdf_file_writer.py +0 -977
  138. packages/tmsi/build/lib/TMSiFileFormats/file_readers/__init__.py +0 -35
  139. packages/tmsi/build/lib/TMSiFileFormats/file_readers/edf_reader.py +0 -116
  140. packages/tmsi/build/lib/TMSiFileFormats/file_readers/poly5reader.py +0 -294
  141. packages/tmsi/build/lib/TMSiFileFormats/file_readers/xdf_reader.py +0 -229
  142. packages/tmsi/build/lib/TMSiFileFormats/file_writer.py +0 -102
  143. packages/tmsi/build/lib/TMSiPlotters/__init__.py +0 -2
  144. packages/tmsi/build/lib/TMSiPlotters/gui/__init__.py +0 -39
  145. packages/tmsi/build/lib/TMSiPlotters/gui/_plotter_gui.py +0 -234
  146. packages/tmsi/build/lib/TMSiPlotters/gui/plotting_gui.py +0 -440
  147. packages/tmsi/build/lib/TMSiPlotters/plotters/__init__.py +0 -44
  148. packages/tmsi/build/lib/TMSiPlotters/plotters/hd_emg_plotter.py +0 -446
  149. packages/tmsi/build/lib/TMSiPlotters/plotters/impedance_plotter.py +0 -589
  150. packages/tmsi/build/lib/TMSiPlotters/plotters/signal_plotter.py +0 -1326
  151. packages/tmsi/build/lib/TMSiSDK/__init__.py +0 -54
  152. packages/tmsi/build/lib/TMSiSDK/device.py +0 -588
  153. packages/tmsi/build/lib/TMSiSDK/devices/__init__.py +0 -34
  154. packages/tmsi/build/lib/TMSiSDK/devices/saga/TMSi_Device_API.py +0 -1764
  155. packages/tmsi/build/lib/TMSiSDK/devices/saga/__init__.py +0 -34
  156. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_device.py +0 -1366
  157. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_types.py +0 -520
  158. packages/tmsi/build/lib/TMSiSDK/devices/saga/xml_saga_config.py +0 -165
  159. packages/tmsi/build/lib/TMSiSDK/error.py +0 -95
  160. packages/tmsi/build/lib/TMSiSDK/sample_data.py +0 -63
  161. packages/tmsi/build/lib/TMSiSDK/sample_data_server.py +0 -99
  162. packages/tmsi/build/lib/TMSiSDK/settings.py +0 -45
  163. packages/tmsi/build/lib/TMSiSDK/tmsi_device.py +0 -111
  164. packages/tmsi/build/lib/__init__.py +0 -4
  165. packages/tmsi/build/lib/apex_sdk/__init__.py +0 -34
  166. packages/tmsi/build/lib/apex_sdk/device/__init__.py +0 -41
  167. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API.py +0 -1009
  168. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_enums.py +0 -239
  169. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_structures.py +0 -668
  170. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_device.py +0 -1611
  171. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_dongle.py +0 -38
  172. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_event_reader.py +0 -57
  173. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_channel.py +0 -44
  174. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_config.py +0 -150
  175. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_const.py +0 -36
  176. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_impedance_channel.py +0 -48
  177. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_info.py +0 -108
  178. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/dongle_info.py +0 -39
  179. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/download_measurement.py +0 -77
  180. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/eeg_measurement.py +0 -150
  181. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/impedance_measurement.py +0 -129
  182. packages/tmsi/build/lib/apex_sdk/device/threads/conversion_thread.py +0 -59
  183. packages/tmsi/build/lib/apex_sdk/device/threads/sampling_thread.py +0 -57
  184. packages/tmsi/build/lib/apex_sdk/device/tmsi_channel.py +0 -83
  185. packages/tmsi/build/lib/apex_sdk/device/tmsi_device.py +0 -201
  186. packages/tmsi/build/lib/apex_sdk/device/tmsi_device_enums.py +0 -103
  187. packages/tmsi/build/lib/apex_sdk/device/tmsi_dongle.py +0 -43
  188. packages/tmsi/build/lib/apex_sdk/device/tmsi_event_reader.py +0 -50
  189. packages/tmsi/build/lib/apex_sdk/device/tmsi_measurement.py +0 -118
  190. packages/tmsi/build/lib/apex_sdk/sample_data_server/__init__.py +0 -33
  191. packages/tmsi/build/lib/apex_sdk/sample_data_server/event_data.py +0 -44
  192. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data.py +0 -50
  193. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data_server.py +0 -136
  194. packages/tmsi/build/lib/apex_sdk/tmsi_errors/error.py +0 -126
  195. packages/tmsi/build/lib/apex_sdk/tmsi_sdk.py +0 -113
  196. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/apex/apex_structure_generator.py +0 -134
  197. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/decorators.py +0 -60
  198. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/logger_filter.py +0 -42
  199. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/singleton.py +0 -42
  200. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/support_functions.py +0 -72
  201. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/tmsi_logger.py +0 -98
  202. py_neuromodulation/nm_EpochStream.py +0 -92
  203. py_neuromodulation/nm_across_patient_decoding.py +0 -927
  204. py_neuromodulation/nm_cohortwrapper.py +0 -435
  205. py_neuromodulation/nm_eval_timing.py +0 -239
  206. py_neuromodulation/nm_features_abc.py +0 -39
  207. py_neuromodulation/nm_stream_offline.py +0 -358
  208. py_neuromodulation/utils/_logging.py +0 -24
  209. py_neuromodulation-0.0.3.dist-info/RECORD +0 -188
  210. py_neuromodulation-0.0.3.dist-info/top_level.txt +0 -5
  211. tests/__init__.py +0 -0
  212. tests/conftest.py +0 -117
  213. tests/test_all_examples.py +0 -10
  214. tests/test_all_features.py +0 -63
  215. tests/test_bispectra.py +0 -70
  216. tests/test_bursts.py +0 -105
  217. tests/test_feature_sampling_rates.py +0 -143
  218. tests/test_fooof.py +0 -16
  219. tests/test_initalization_offline_stream.py +0 -41
  220. tests/test_multiprocessing.py +0 -58
  221. tests/test_nan_values.py +0 -29
  222. tests/test_nm_filter.py +0 -95
  223. tests/test_nm_resample.py +0 -63
  224. tests/test_normalization_settings.py +0 -146
  225. tests/test_notch_filter.py +0 -31
  226. tests/test_osc_features.py +0 -424
  227. tests/test_preprocessing_filter.py +0 -151
  228. tests/test_rereference.py +0 -171
  229. tests/test_sampling.py +0 -57
  230. tests/test_settings_change_after_init.py +0 -76
  231. tests/test_sharpwave.py +0 -165
  232. tests/test_target_channel_add.py +0 -100
  233. tests/test_timing.py +0 -80
@@ -1,589 +1,589 @@
1
- """
2
- FieldTrip buffer (V1) client in pure Python
3
- (C) 2010 S. Klanke
4
- """
5
-
6
- # Obtained from https://github.com/fieldtrip/fieldtrip/blob/master/realtime/src/buffer/python/FieldTrip.py
7
-
8
- # We need socket, struct, and numpy
9
- import socket
10
- import struct
11
- import numpy
12
- import unicodedata
13
-
14
- VERSION = 1
15
-
16
- PUT_HDR = 0x0101
17
- PUT_DAT = 0x0102
18
- PUT_EVT = 0x0103
19
- PUT_OK = 0x0104
20
- PUT_ERR = 0x0105
21
- GET_HDR = 0x0201
22
- GET_DAT = 0x0202
23
- GET_EVT = 0x0203
24
- GET_OK = 0x0204
25
- GET_ERR = 0x0205
26
- FLUSH_HDR = 0x0301
27
- FLUSH_DAT = 0x0302
28
- FLUSH_EVT = 0x0303
29
- FLUSH_OK = 0x0304
30
- FLUSH_ERR = 0x0305
31
- WAIT_DAT = 0x0402
32
- WAIT_OK = 0x0404
33
- WAIT_ERR = 0x0405
34
- PUT_HDR_NORESPONSE = 0x0501
35
- PUT_DAT_NORESPONSE = 0x0502
36
- PUT_EVT_NORESPONSE = 0x0503
37
-
38
- DATATYPE_CHAR = 0
39
- DATATYPE_UINT8 = 1
40
- DATATYPE_UINT16 = 2
41
- DATATYPE_UINT32 = 3
42
- DATATYPE_UINT64 = 4
43
- DATATYPE_INT8 = 5
44
- DATATYPE_INT16 = 6
45
- DATATYPE_INT32 = 7
46
- DATATYPE_INT64 = 8
47
- DATATYPE_FLOAT32 = 9
48
- DATATYPE_FLOAT64 = 10
49
- DATATYPE_UNKNOWN = 0xFFFFFFFF
50
-
51
- CHUNK_UNSPECIFIED = 0
52
- CHUNK_CHANNEL_NAMES = 1
53
- CHUNK_CHANNEL_FLAGS = 2
54
- CHUNK_RESOLUTIONS = 3
55
- CHUNK_ASCII_KEYVAL = 4
56
- CHUNK_NIFTI1 = 5
57
- CHUNK_SIEMENS_AP = 6
58
- CHUNK_CTF_RES4 = 7
59
- CHUNK_NEUROMAG_FIF = 8
60
- CHUNK_NEUROMAG_ISOTRAK = 9
61
- CHUNK_NEUROMAG_HPIRESULT = 10
62
-
63
- # List for converting FieldTrip datatypes to Numpy datatypes
64
- numpyType = ['int8', 'uint8', 'uint16', 'uint32', 'uint64',
65
- 'int8', 'int16', 'int32', 'int64', 'float32', 'float64']
66
- # Corresponding word sizes
67
- wordSize = [1, 1, 2, 4, 8, 1, 2, 4, 8, 4, 8]
68
- # FieldTrip data type as indexed by numpy dtype.num
69
- # this goes 0 => nothing, 1..4 => int8, uint8, int16, uint16, 7..10 =>
70
- # int32, uint32, int64, uint64 11..12 => float32, float64
71
- dataType = [-1, 5, 1, 6, 2, -1, -1, 7, 3, 8, 4, 9, 10]
72
-
73
-
74
- def serialize(A):
75
- """
76
- Returns FieldTrip data type and string representation of the given
77
- object, if possible.
78
- """
79
- if isinstance(A, str):
80
- return (0, A)
81
-
82
- if isinstance(A, numpy.ndarray):
83
- dt = A.dtype
84
- if not(dt.isnative) or dt.num < 1 or dt.num >= len(dataType):
85
- return (DATATYPE_UNKNOWN, None)
86
-
87
- ft = dataType[dt.num]
88
- if ft == -1:
89
- return (DATATYPE_UNKNOWN, None)
90
-
91
- if A.flags['C_CONTIGUOUS']:
92
- # great, just use the array's buffer interface
93
- return (ft, A.tostring())
94
-
95
- # otherwise, we need a copy to C order
96
- AC = A.copy('C')
97
- return (ft, AC.tostring())
98
-
99
- if isinstance(A, int):
100
- return (DATATYPE_INT32, struct.pack('i', A))
101
-
102
- if isinstance(A, float):
103
- return (DATATYPE_FLOAT64, struct.pack('d', A))
104
-
105
- return (DATATYPE_UNKNOWN, None)
106
-
107
-
108
- class Chunk:
109
-
110
- def __init__(self):
111
- self.type = 0
112
- self.size = 0
113
- self.buf = ''
114
-
115
-
116
- class Header:
117
-
118
- """Class for storing header information in the FieldTrip buffer format"""
119
-
120
- def __init__(self):
121
- self.nChannels = 0
122
- self.nSamples = 0
123
- self.nEvents = 0
124
- self.fSample = 0.0
125
- self.dataType = 0
126
- self.chunks = {}
127
- self.labels = []
128
-
129
- def __str__(self):
130
- return ('Channels.: %i\nSamples..: %i\nEvents...: %i\nSampFreq.: '
131
- '%f\nDataType.: %s\n'
132
- % (self.nChannels, self.nSamples, self.nEvents,
133
- self.fSample, numpyType[self.dataType]))
134
-
135
-
136
- class Event:
137
- """Class for storing events in the FieldTrip buffer format"""
138
-
139
- def __init__(self, S=None):
140
- if S is None:
141
- self.type = ''
142
- self.value = ''
143
- self.sample = 0
144
- self.offset = 0
145
- self.duration = 0
146
- else:
147
- self.deserialize(S)
148
-
149
- def __str__(self):
150
- return ('Type.....: %s\nValue....: %s\nSample...: %i\nOffset...: '
151
- '%i\nDuration.: %i\n' % (str(self.type), str(self.value),
152
- self.sample, self.offset,
153
- self.duration))
154
-
155
- def deserialize(self, buf):
156
- bufsize = len(buf)
157
- if bufsize < 32:
158
- return 0
159
-
160
- (type_type, type_numel, value_type, value_numel, sample,
161
- offset, duration, bsiz) = struct.unpack('IIIIIiiI', buf[0:32])
162
-
163
- self.sample = sample
164
- self.offset = offset
165
- self.duration = duration
166
-
167
- st = type_numel * wordSize[type_type]
168
- sv = value_numel * wordSize[value_type]
169
-
170
- if bsiz + 32 > bufsize or st + sv > bsiz:
171
- raise IOError(
172
- 'Invalid event definition -- does not fit in given buffer')
173
-
174
- raw_type = buf[32:32 + st]
175
- raw_value = buf[32 + st:32 + st + sv]
176
-
177
- if type_type == 0:
178
- self.type = raw_type
179
- else:
180
- self.type = numpy.ndarray(
181
- (type_numel), dtype=numpyType[type_type], buffer=raw_type)
182
-
183
- if value_type == 0:
184
- self.value = raw_value
185
- else:
186
- self.value = numpy.ndarray(
187
- (value_numel), dtype=numpyType[value_type], buffer=raw_value)
188
-
189
- return bsiz + 32
190
-
191
- def serialize(self):
192
- """
193
- Returns the contents of this event as a string, ready to
194
- send over the network, or None in case of conversion problems.
195
- """
196
- type_type, type_buf = serialize(self.type)
197
- if type_type == DATATYPE_UNKNOWN:
198
- return None
199
- type_size = len(type_buf)
200
- type_numel = type_size / wordSize[type_type]
201
-
202
- value_type, value_buf = serialize(self.value)
203
- if value_type == DATATYPE_UNKNOWN:
204
- return None
205
- value_size = len(value_buf)
206
- value_numel = value_size / wordSize[value_type]
207
-
208
- bufsize = type_size + value_size
209
-
210
- S = struct.pack('IIIIIiiI', type_type, type_numel, value_type,
211
- value_numel, int(self.sample), int(self.offset),
212
- int(self.duration), bufsize)
213
- return S + type_buf + value_buf
214
-
215
-
216
- class Client:
217
-
218
- """Class for managing a client connection to a FieldTrip buffer."""
219
-
220
- def __init__(self):
221
- self.isConnected = False
222
- self.sock = []
223
-
224
- def connect(self, hostname, port=1972):
225
- """
226
- connect(hostname [, port]) -- make a connection, default port is
227
- 1972.
228
- """
229
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
230
- self.sock.connect((hostname, port))
231
- self.sock.setblocking(True)
232
- self.isConnected = True
233
-
234
- def disconnect(self):
235
- """disconnect() -- close a connection."""
236
- if self.isConnected:
237
- self.sock.close()
238
- self.sock = []
239
- self.isConnected = False
240
-
241
- def sendRaw(self, request):
242
- """Send all bytes of the string 'request' out to socket."""
243
- if not(self.isConnected):
244
- raise IOError('Not connected to FieldTrip buffer')
245
-
246
- N = len(request)
247
- nw = self.sock.send(request)
248
- while nw < N:
249
- nw += self.sock.send(request[nw:])
250
-
251
- def sendRequest(self, command, payload=None):
252
- if payload is None:
253
- request = struct.pack('HHI', VERSION, command, 0)
254
- else:
255
- request = struct.pack(
256
- 'HHI', VERSION, command, len(payload)) + payload
257
- self.sendRaw(request)
258
-
259
- def receiveResponse(self, minBytes=0):
260
- """
261
- Receive response from server on socket 's' and return it as
262
- (status,bufsize,payload).
263
- """
264
-
265
- resp_hdr = self.sock.recv(8)
266
- while len(resp_hdr) < 8:
267
- resp_hdr += self.sock.recv(8 - len(resp_hdr))
268
-
269
- (version, command, bufsize) = struct.unpack('HHI', resp_hdr)
270
-
271
- if version != VERSION:
272
- self.disconnect()
273
- raise IOError('Bad response from buffer server - disconnecting')
274
-
275
- if bufsize > 0:
276
- payload = self.sock.recv(bufsize)
277
- while len(payload) < bufsize:
278
- payload += self.sock.recv(bufsize - len(payload))
279
- else:
280
- payload = None
281
- return (command, bufsize, payload)
282
-
283
- def getHeader(self):
284
- """
285
- getHeader() -- grabs header information from the buffer an returns
286
- it as a Header object.
287
- """
288
-
289
- self.sendRequest(GET_HDR)
290
- (status, bufsize, payload) = self.receiveResponse()
291
-
292
- if status == GET_ERR:
293
- return None
294
-
295
- if status != GET_OK:
296
- self.disconnect()
297
- raise IOError('Bad response from buffer server - disconnecting')
298
-
299
- if bufsize < 24:
300
- self.disconnect()
301
- raise IOError('Invalid HEADER packet received (too few bytes) - '
302
- 'disconnecting')
303
-
304
- (nchans, nsamp, nevt, fsamp, dtype,
305
- bfsiz) = struct.unpack('IIIfII', payload[0:24])
306
-
307
- H = Header()
308
- H.nChannels = nchans
309
- H.nSamples = nsamp
310
- H.nEvents = nevt
311
- H.fSample = fsamp
312
- H.dataType = dtype
313
-
314
- if bfsiz > 0:
315
- offset = 24
316
- while offset + 8 < bufsize:
317
- (chunk_type, chunk_len) = struct.unpack(
318
- 'II', payload[offset:offset + 8])
319
- offset += 8
320
- if offset + chunk_len > bufsize:
321
- break
322
- H.chunks[chunk_type] = payload[offset:offset + chunk_len]
323
- offset += chunk_len
324
-
325
- if CHUNK_CHANNEL_NAMES in H.chunks:
326
- L = H.chunks[CHUNK_CHANNEL_NAMES].split(b'\0')
327
- numLab = len(L)
328
- if numLab >= H.nChannels:
329
- H.labels = [x.decode('utf-8') for x in L[0:H.nChannels]]
330
-
331
- return H
332
-
333
- def putHeader(self, nChannels, fSample, dataType, labels=None,
334
- chunks=None, reponse=True):
335
- haveLabels = False
336
- extras = b''
337
-
338
- if (type(labels)==list) and (len(labels)==0):
339
- labels=None
340
-
341
- if not(labels is None):
342
- serLabels = b''
343
- for n in range(0, nChannels):
344
- # ensure that labels are ascii strings, not unicode
345
- serLabels += labels[n].encode('ascii', 'ignore') + b'\0'
346
- try:
347
- pass
348
- except:
349
- raise ValueError('Channels names (labels), if given,'
350
- ' must be a list of N=numChannels strings')
351
-
352
- extras = struct.pack('II', CHUNK_CHANNEL_NAMES,
353
- len(serLabels)) + serLabels
354
- haveLabels = True
355
-
356
- if not(chunks is None):
357
- for chunk_type, chunk_data in chunks:
358
- if haveLabels and chunk_type == CHUNK_CHANNEL_NAMES:
359
- # ignore channel names chunk in case we got labels
360
- continue
361
- extras += struct.pack('II', chunk_type,
362
- len(chunk_data)) + chunk_data
363
-
364
- sizeChunks = len(extras)
365
-
366
- if reponse:
367
- command = PUT_HDR
368
- else:
369
- command = PUT_HDR_NORESPONSE
370
-
371
- hdef = struct.pack('IIIfII', nChannels, 0, 0,
372
- fSample, dataType, sizeChunks)
373
- request = struct.pack('HHI', VERSION, command,
374
- sizeChunks + len(hdef)) + hdef + extras
375
- self.sendRaw(request)
376
-
377
- if reponse:
378
- (status, bufsize, resp_buf) = self.receiveResponse()
379
- if status != PUT_OK:
380
- raise IOError('Header could not be written')
381
-
382
- def getData(self, index=None):
383
- """
384
- getData([indices]) -- retrieve data samples and return them as a
385
- Numpy array, samples in rows(!). The 'indices' argument is optional,
386
- and if given, must be a tuple or list with inclusive, zero-based
387
- start/end indices.
388
- """
389
-
390
- if index is None:
391
- request = struct.pack('HHI', VERSION, GET_DAT, 0)
392
- else:
393
- indS = int(index[0])
394
- indE = int(index[1])
395
- request = struct.pack('HHIII', VERSION, GET_DAT, 8, indS, indE)
396
- self.sendRaw(request)
397
-
398
- (status, bufsize, payload) = self.receiveResponse()
399
- if status == GET_ERR:
400
- return None
401
-
402
- if status != GET_OK:
403
- self.disconnect()
404
- raise IOError('Bad response from buffer server - disconnecting')
405
-
406
- if bufsize < 16:
407
- self.disconnect()
408
- raise IOError('Invalid DATA packet received (too few bytes)')
409
-
410
- (nchans, nsamp, datype, bfsiz) = struct.unpack('IIII', payload[0:16])
411
-
412
- if bfsiz < bufsize - 16 or datype >= len(numpyType):
413
- raise IOError('Invalid DATA packet received')
414
-
415
- raw = payload[16:bfsiz + 16]
416
- D = numpy.ndarray((nsamp, nchans), dtype=numpyType[datype], buffer=raw)
417
-
418
- return D
419
-
420
- def getEvents(self, index=None):
421
- """
422
- getEvents([indices]) -- retrieve events and return them as a list
423
- of Event objects. The 'indices' argument is optional, and if given,
424
- must be a tuple or list with inclusive, zero-based start/end indices.
425
- The 'type' and 'value' fields of the event will be converted to strings
426
- or Numpy arrays.
427
- """
428
-
429
- if index is None:
430
- request = struct.pack('HHI', VERSION, GET_EVT, 0)
431
- else:
432
- indS = int(index[0])
433
- indE = int(index[1])
434
- request = struct.pack('HHIII', VERSION, GET_EVT, 8, indS, indE)
435
- self.sendRaw(request)
436
-
437
- (status, bufsize, resp_buf) = self.receiveResponse()
438
- if status == GET_ERR:
439
- return []
440
-
441
- if status != GET_OK:
442
- self.disconnect()
443
- raise IOError('Bad response from buffer server - disconnecting')
444
-
445
- offset = 0
446
- E = []
447
- while 1:
448
- e = Event()
449
- nextOffset = e.deserialize(resp_buf[offset:])
450
- if nextOffset == 0:
451
- break
452
- E.append(e)
453
- offset = offset + nextOffset
454
-
455
- return E
456
-
457
- def putEvents(self, E, reponse=True):
458
- """
459
- putEvents(E) -- writes a single or multiple events, depending on
460
- whether an 'Event' object, or a list of 'Event' objects is
461
- given as an argument.
462
- """
463
- if isinstance(E, Event):
464
- buf = E.serialize()
465
- else:
466
- buf = ''
467
- num = 0
468
- for e in E:
469
- if not(isinstance(e, Event)):
470
- raise 'Element %i in given list is not an Event' % num
471
- buf = buf + e.serialize()
472
- num = num + 1
473
-
474
- if reponse:
475
- command = PUT_EVT
476
- else:
477
- command = PUT_EVT_NORESPONSE
478
-
479
- self.sendRequest(command, buf)
480
-
481
- if reponse:
482
- (status, bufsize, resp_buf) = self.receiveResponse()
483
- if status != PUT_OK:
484
- raise IOError('Events could not be written.')
485
-
486
- def putData(self, D, response=True):
487
- """
488
- putData(D) -- writes samples that must be given as a NUMPY array,
489
- samples x channels. The type of the samples (D) and the number of
490
- channels must match the corresponding quantities in the FieldTrip
491
- buffer.
492
- """
493
-
494
- if not(isinstance(D, numpy.ndarray)) or len(D.shape) != 2:
495
- raise ValueError(
496
- 'Data must be given as a NUMPY array (samples x channels)')
497
-
498
- nSamp = D.shape[0]
499
- nChan = D.shape[1]
500
-
501
- (dataType, dataBuf) = serialize(D)
502
-
503
- dataBufSize = len(dataBuf)
504
-
505
- if response:
506
- command = PUT_DAT
507
- else:
508
- command = PUT_DAT_NORESPONSE
509
-
510
- request = struct.pack('HHI', VERSION, command, 16 + dataBufSize)
511
- dataDef = struct.pack('IIII', nChan, nSamp, dataType, dataBufSize)
512
- self.sendRaw(request + dataDef + dataBuf)
513
-
514
- if response:
515
- (status, bufsize, resp_buf) = self.receiveResponse()
516
- if status != PUT_OK:
517
- raise IOError('Samples could not be written.')
518
-
519
- def poll(self):
520
-
521
- request = struct.pack('HHIIII', VERSION, WAIT_DAT, 12, 0, 0, 0)
522
- self.sendRaw(request)
523
-
524
- (status, bufsize, resp_buf) = self.receiveResponse()
525
-
526
- if status != WAIT_OK or bufsize < 8:
527
- raise IOError('Polling failed.')
528
-
529
- return struct.unpack('II', resp_buf[0:8])
530
-
531
- def wait(self, nsamples, nevents, timeout):
532
- request = struct.pack('HHIIII', VERSION, WAIT_DAT,
533
- 12, int(nsamples), int(nevents), int(timeout))
534
- self.sendRaw(request)
535
-
536
- (status, bufsize, resp_buf) = self.receiveResponse()
537
-
538
- if status != WAIT_OK or bufsize < 8:
539
- raise IOError('Wait request failed.')
540
-
541
- return struct.unpack('II', resp_buf[0:8])
542
-
543
- if __name__ == "__main__":
544
- # Just a small demo for testing purposes...
545
- # This should be moved to a separate file at some point
546
- import sys
547
-
548
- hostname = 'localhost'
549
- port = 1972
550
-
551
- if len(sys.argv) > 1:
552
- hostname = sys.argv[1]
553
- if len(sys.argv) > 2:
554
- try:
555
- port = int(sys.argv[2])
556
- except:
557
- print(('Error: second argument (%s) must be a valid (=integer)'
558
- ' port number' % sys.argv[2]))
559
- sys.exit(1)
560
-
561
- ftc = Client()
562
-
563
- print('Trying to connect to buffer on %s:%i ...' % (hostname, port))
564
- ftc.connect(hostname, port)
565
-
566
- print('\nConnected - trying to read header...')
567
- H = ftc.getHeader()
568
-
569
- if H is None:
570
- print('Failed!')
571
- else:
572
- print(H)
573
- print(H.labels)
574
-
575
- if H.nSamples > 0:
576
- print('\nTrying to read last sample...')
577
- index = H.nSamples - 1
578
- D = ftc.getData([index, index])
579
- print(D)
580
-
581
- if H.nEvents > 0:
582
- print('\nTrying to read (all) events...')
583
- E = ftc.getEvents()
584
- for e in E:
585
- print(e)
586
-
587
- print(ftc.poll())
588
-
589
- ftc.disconnect()
1
+ """
2
+ FieldTrip buffer (V1) client in pure Python
3
+ (C) 2010 S. Klanke
4
+ """
5
+
6
+ # Obtained from https://github.com/fieldtrip/fieldtrip/blob/master/realtime/src/buffer/python/FieldTrip.py
7
+
8
+ # We need socket, struct, and numpy
9
+ import socket
10
+ import struct
11
+ import numpy
12
+ import unicodedata
13
+
14
+ VERSION = 1
15
+
16
+ PUT_HDR = 0x0101
17
+ PUT_DAT = 0x0102
18
+ PUT_EVT = 0x0103
19
+ PUT_OK = 0x0104
20
+ PUT_ERR = 0x0105
21
+ GET_HDR = 0x0201
22
+ GET_DAT = 0x0202
23
+ GET_EVT = 0x0203
24
+ GET_OK = 0x0204
25
+ GET_ERR = 0x0205
26
+ FLUSH_HDR = 0x0301
27
+ FLUSH_DAT = 0x0302
28
+ FLUSH_EVT = 0x0303
29
+ FLUSH_OK = 0x0304
30
+ FLUSH_ERR = 0x0305
31
+ WAIT_DAT = 0x0402
32
+ WAIT_OK = 0x0404
33
+ WAIT_ERR = 0x0405
34
+ PUT_HDR_NORESPONSE = 0x0501
35
+ PUT_DAT_NORESPONSE = 0x0502
36
+ PUT_EVT_NORESPONSE = 0x0503
37
+
38
+ DATATYPE_CHAR = 0
39
+ DATATYPE_UINT8 = 1
40
+ DATATYPE_UINT16 = 2
41
+ DATATYPE_UINT32 = 3
42
+ DATATYPE_UINT64 = 4
43
+ DATATYPE_INT8 = 5
44
+ DATATYPE_INT16 = 6
45
+ DATATYPE_INT32 = 7
46
+ DATATYPE_INT64 = 8
47
+ DATATYPE_FLOAT32 = 9
48
+ DATATYPE_FLOAT64 = 10
49
+ DATATYPE_UNKNOWN = 0xFFFFFFFF
50
+
51
+ CHUNK_UNSPECIFIED = 0
52
+ CHUNK_CHANNEL_NAMES = 1
53
+ CHUNK_CHANNEL_FLAGS = 2
54
+ CHUNK_RESOLUTIONS = 3
55
+ CHUNK_ASCII_KEYVAL = 4
56
+ CHUNK_NIFTI1 = 5
57
+ CHUNK_SIEMENS_AP = 6
58
+ CHUNK_CTF_RES4 = 7
59
+ CHUNK_NEUROMAG_FIF = 8
60
+ CHUNK_NEUROMAG_ISOTRAK = 9
61
+ CHUNK_NEUROMAG_HPIRESULT = 10
62
+
63
+ # List for converting FieldTrip datatypes to Numpy datatypes
64
+ numpyType = ['int8', 'uint8', 'uint16', 'uint32', 'uint64',
65
+ 'int8', 'int16', 'int32', 'int64', 'float32', 'float64']
66
+ # Corresponding word sizes
67
+ wordSize = [1, 1, 2, 4, 8, 1, 2, 4, 8, 4, 8]
68
+ # FieldTrip data type as indexed by numpy dtype.num
69
+ # this goes 0 => nothing, 1..4 => int8, uint8, int16, uint16, 7..10 =>
70
+ # int32, uint32, int64, uint64 11..12 => float32, float64
71
+ dataType = [-1, 5, 1, 6, 2, -1, -1, 7, 3, 8, 4, 9, 10]
72
+
73
+
74
+ def serialize(A):
75
+ """
76
+ Returns FieldTrip data type and string representation of the given
77
+ object, if possible.
78
+ """
79
+ if isinstance(A, str):
80
+ return (0, A)
81
+
82
+ if isinstance(A, numpy.ndarray):
83
+ dt = A.dtype
84
+ if not(dt.isnative) or dt.num < 1 or dt.num >= len(dataType):
85
+ return (DATATYPE_UNKNOWN, None)
86
+
87
+ ft = dataType[dt.num]
88
+ if ft == -1:
89
+ return (DATATYPE_UNKNOWN, None)
90
+
91
+ if A.flags['C_CONTIGUOUS']:
92
+ # great, just use the array's buffer interface
93
+ return (ft, A.tostring())
94
+
95
+ # otherwise, we need a copy to C order
96
+ AC = A.copy('C')
97
+ return (ft, AC.tostring())
98
+
99
+ if isinstance(A, int):
100
+ return (DATATYPE_INT32, struct.pack('i', A))
101
+
102
+ if isinstance(A, float):
103
+ return (DATATYPE_FLOAT64, struct.pack('d', A))
104
+
105
+ return (DATATYPE_UNKNOWN, None)
106
+
107
+
108
+ class Chunk:
109
+
110
+ def __init__(self):
111
+ self.type = 0
112
+ self.size = 0
113
+ self.buf = ''
114
+
115
+
116
+ class Header:
117
+
118
+ """Class for storing header information in the FieldTrip buffer format"""
119
+
120
+ def __init__(self):
121
+ self.nChannels = 0
122
+ self.nSamples = 0
123
+ self.nEvents = 0
124
+ self.fSample = 0.0
125
+ self.dataType = 0
126
+ self.chunks = {}
127
+ self.labels = []
128
+
129
+ def __str__(self):
130
+ return ('Channels.: %i\nSamples..: %i\nEvents...: %i\nSampFreq.: '
131
+ '%f\nDataType.: %s\n'
132
+ % (self.nChannels, self.nSamples, self.nEvents,
133
+ self.fSample, numpyType[self.dataType]))
134
+
135
+
136
+ class Event:
137
+ """Class for storing events in the FieldTrip buffer format"""
138
+
139
+ def __init__(self, S=None):
140
+ if S is None:
141
+ self.type = ''
142
+ self.value = ''
143
+ self.sample = 0
144
+ self.offset = 0
145
+ self.duration = 0
146
+ else:
147
+ self.deserialize(S)
148
+
149
+ def __str__(self):
150
+ return ('Type.....: %s\nValue....: %s\nSample...: %i\nOffset...: '
151
+ '%i\nDuration.: %i\n' % (str(self.type), str(self.value),
152
+ self.sample, self.offset,
153
+ self.duration))
154
+
155
+ def deserialize(self, buf):
156
+ bufsize = len(buf)
157
+ if bufsize < 32:
158
+ return 0
159
+
160
+ (type_type, type_numel, value_type, value_numel, sample,
161
+ offset, duration, bsiz) = struct.unpack('IIIIIiiI', buf[0:32])
162
+
163
+ self.sample = sample
164
+ self.offset = offset
165
+ self.duration = duration
166
+
167
+ st = type_numel * wordSize[type_type]
168
+ sv = value_numel * wordSize[value_type]
169
+
170
+ if bsiz + 32 > bufsize or st + sv > bsiz:
171
+ raise IOError(
172
+ 'Invalid event definition -- does not fit in given buffer')
173
+
174
+ raw_type = buf[32:32 + st]
175
+ raw_value = buf[32 + st:32 + st + sv]
176
+
177
+ if type_type == 0:
178
+ self.type = raw_type
179
+ else:
180
+ self.type = numpy.ndarray(
181
+ (type_numel), dtype=numpyType[type_type], buffer=raw_type)
182
+
183
+ if value_type == 0:
184
+ self.value = raw_value
185
+ else:
186
+ self.value = numpy.ndarray(
187
+ (value_numel), dtype=numpyType[value_type], buffer=raw_value)
188
+
189
+ return bsiz + 32
190
+
191
+ def serialize(self):
192
+ """
193
+ Returns the contents of this event as a string, ready to
194
+ send over the network, or None in case of conversion problems.
195
+ """
196
+ type_type, type_buf = serialize(self.type)
197
+ if type_type == DATATYPE_UNKNOWN:
198
+ return None
199
+ type_size = len(type_buf)
200
+ type_numel = type_size / wordSize[type_type]
201
+
202
+ value_type, value_buf = serialize(self.value)
203
+ if value_type == DATATYPE_UNKNOWN:
204
+ return None
205
+ value_size = len(value_buf)
206
+ value_numel = value_size / wordSize[value_type]
207
+
208
+ bufsize = type_size + value_size
209
+
210
+ S = struct.pack('IIIIIiiI', type_type, type_numel, value_type,
211
+ value_numel, int(self.sample), int(self.offset),
212
+ int(self.duration), bufsize)
213
+ return S + type_buf + value_buf
214
+
215
+
216
+ class Client:
217
+
218
+ """Class for managing a client connection to a FieldTrip buffer."""
219
+
220
+ def __init__(self):
221
+ self.isConnected = False
222
+ self.sock = []
223
+
224
+ def connect(self, hostname, port=1972):
225
+ """
226
+ connect(hostname [, port]) -- make a connection, default port is
227
+ 1972.
228
+ """
229
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
230
+ self.sock.connect((hostname, port))
231
+ self.sock.setblocking(True)
232
+ self.isConnected = True
233
+
234
+ def disconnect(self):
235
+ """disconnect() -- close a connection."""
236
+ if self.isConnected:
237
+ self.sock.close()
238
+ self.sock = []
239
+ self.isConnected = False
240
+
241
+ def sendRaw(self, request):
242
+ """Send all bytes of the string 'request' out to socket."""
243
+ if not(self.isConnected):
244
+ raise IOError('Not connected to FieldTrip buffer')
245
+
246
+ N = len(request)
247
+ nw = self.sock.send(request)
248
+ while nw < N:
249
+ nw += self.sock.send(request[nw:])
250
+
251
+ def sendRequest(self, command, payload=None):
252
+ if payload is None:
253
+ request = struct.pack('HHI', VERSION, command, 0)
254
+ else:
255
+ request = struct.pack(
256
+ 'HHI', VERSION, command, len(payload)) + payload
257
+ self.sendRaw(request)
258
+
259
+ def receiveResponse(self, minBytes=0):
260
+ """
261
+ Receive response from server on socket 's' and return it as
262
+ (status,bufsize,payload).
263
+ """
264
+
265
+ resp_hdr = self.sock.recv(8)
266
+ while len(resp_hdr) < 8:
267
+ resp_hdr += self.sock.recv(8 - len(resp_hdr))
268
+
269
+ (version, command, bufsize) = struct.unpack('HHI', resp_hdr)
270
+
271
+ if version != VERSION:
272
+ self.disconnect()
273
+ raise IOError('Bad response from buffer server - disconnecting')
274
+
275
+ if bufsize > 0:
276
+ payload = self.sock.recv(bufsize)
277
+ while len(payload) < bufsize:
278
+ payload += self.sock.recv(bufsize - len(payload))
279
+ else:
280
+ payload = None
281
+ return (command, bufsize, payload)
282
+
283
+ def getHeader(self):
284
+ """
285
+ getHeader() -- grabs header information from the buffer an returns
286
+ it as a Header object.
287
+ """
288
+
289
+ self.sendRequest(GET_HDR)
290
+ (status, bufsize, payload) = self.receiveResponse()
291
+
292
+ if status == GET_ERR:
293
+ return None
294
+
295
+ if status != GET_OK:
296
+ self.disconnect()
297
+ raise IOError('Bad response from buffer server - disconnecting')
298
+
299
+ if bufsize < 24:
300
+ self.disconnect()
301
+ raise IOError('Invalid HEADER packet received (too few bytes) - '
302
+ 'disconnecting')
303
+
304
+ (nchans, nsamp, nevt, fsamp, dtype,
305
+ bfsiz) = struct.unpack('IIIfII', payload[0:24])
306
+
307
+ H = Header()
308
+ H.nChannels = nchans
309
+ H.nSamples = nsamp
310
+ H.nEvents = nevt
311
+ H.fSample = fsamp
312
+ H.dataType = dtype
313
+
314
+ if bfsiz > 0:
315
+ offset = 24
316
+ while offset + 8 < bufsize:
317
+ (chunk_type, chunk_len) = struct.unpack(
318
+ 'II', payload[offset:offset + 8])
319
+ offset += 8
320
+ if offset + chunk_len > bufsize:
321
+ break
322
+ H.chunks[chunk_type] = payload[offset:offset + chunk_len]
323
+ offset += chunk_len
324
+
325
+ if CHUNK_CHANNEL_NAMES in H.chunks:
326
+ L = H.chunks[CHUNK_CHANNEL_NAMES].split(b'\0')
327
+ numLab = len(L)
328
+ if numLab >= H.nChannels:
329
+ H.labels = [x.decode('utf-8') for x in L[0:H.nChannels]]
330
+
331
+ return H
332
+
333
+ def putHeader(self, nChannels, fSample, dataType, labels=None,
334
+ chunks=None, reponse=True):
335
+ haveLabels = False
336
+ extras = b''
337
+
338
+ if (type(labels)==list) and (len(labels)==0):
339
+ labels=None
340
+
341
+ if not(labels is None):
342
+ serLabels = b''
343
+ for n in range(0, nChannels):
344
+ # ensure that labels are ascii strings, not unicode
345
+ serLabels += labels[n].encode('ascii', 'ignore') + b'\0'
346
+ try:
347
+ pass
348
+ except:
349
+ raise ValueError('Channels names (labels), if given,'
350
+ ' must be a list of N=numChannels strings')
351
+
352
+ extras = struct.pack('II', CHUNK_CHANNEL_NAMES,
353
+ len(serLabels)) + serLabels
354
+ haveLabels = True
355
+
356
+ if not(chunks is None):
357
+ for chunk_type, chunk_data in chunks:
358
+ if haveLabels and chunk_type == CHUNK_CHANNEL_NAMES:
359
+ # ignore channel names chunk in case we got labels
360
+ continue
361
+ extras += struct.pack('II', chunk_type,
362
+ len(chunk_data)) + chunk_data
363
+
364
+ sizeChunks = len(extras)
365
+
366
+ if reponse:
367
+ command = PUT_HDR
368
+ else:
369
+ command = PUT_HDR_NORESPONSE
370
+
371
+ hdef = struct.pack('IIIfII', nChannels, 0, 0,
372
+ fSample, dataType, sizeChunks)
373
+ request = struct.pack('HHI', VERSION, command,
374
+ sizeChunks + len(hdef)) + hdef + extras
375
+ self.sendRaw(request)
376
+
377
+ if reponse:
378
+ (status, bufsize, resp_buf) = self.receiveResponse()
379
+ if status != PUT_OK:
380
+ raise IOError('Header could not be written')
381
+
382
+ def getData(self, index=None):
383
+ """
384
+ getData([indices]) -- retrieve data samples and return them as a
385
+ Numpy array, samples in rows(!). The 'indices' argument is optional,
386
+ and if given, must be a tuple or list with inclusive, zero-based
387
+ start/end indices.
388
+ """
389
+
390
+ if index is None:
391
+ request = struct.pack('HHI', VERSION, GET_DAT, 0)
392
+ else:
393
+ indS = int(index[0])
394
+ indE = int(index[1])
395
+ request = struct.pack('HHIII', VERSION, GET_DAT, 8, indS, indE)
396
+ self.sendRaw(request)
397
+
398
+ (status, bufsize, payload) = self.receiveResponse()
399
+ if status == GET_ERR:
400
+ return None
401
+
402
+ if status != GET_OK:
403
+ self.disconnect()
404
+ raise IOError('Bad response from buffer server - disconnecting')
405
+
406
+ if bufsize < 16:
407
+ self.disconnect()
408
+ raise IOError('Invalid DATA packet received (too few bytes)')
409
+
410
+ (nchans, nsamp, datype, bfsiz) = struct.unpack('IIII', payload[0:16])
411
+
412
+ if bfsiz < bufsize - 16 or datype >= len(numpyType):
413
+ raise IOError('Invalid DATA packet received')
414
+
415
+ raw = payload[16:bfsiz + 16]
416
+ D = numpy.ndarray((nsamp, nchans), dtype=numpyType[datype], buffer=raw)
417
+
418
+ return D
419
+
420
+ def getEvents(self, index=None):
421
+ """
422
+ getEvents([indices]) -- retrieve events and return them as a list
423
+ of Event objects. The 'indices' argument is optional, and if given,
424
+ must be a tuple or list with inclusive, zero-based start/end indices.
425
+ The 'type' and 'value' fields of the event will be converted to strings
426
+ or Numpy arrays.
427
+ """
428
+
429
+ if index is None:
430
+ request = struct.pack('HHI', VERSION, GET_EVT, 0)
431
+ else:
432
+ indS = int(index[0])
433
+ indE = int(index[1])
434
+ request = struct.pack('HHIII', VERSION, GET_EVT, 8, indS, indE)
435
+ self.sendRaw(request)
436
+
437
+ (status, bufsize, resp_buf) = self.receiveResponse()
438
+ if status == GET_ERR:
439
+ return []
440
+
441
+ if status != GET_OK:
442
+ self.disconnect()
443
+ raise IOError('Bad response from buffer server - disconnecting')
444
+
445
+ offset = 0
446
+ E = []
447
+ while 1:
448
+ e = Event()
449
+ nextOffset = e.deserialize(resp_buf[offset:])
450
+ if nextOffset == 0:
451
+ break
452
+ E.append(e)
453
+ offset = offset + nextOffset
454
+
455
+ return E
456
+
457
+ def putEvents(self, E, reponse=True):
458
+ """
459
+ putEvents(E) -- writes a single or multiple events, depending on
460
+ whether an 'Event' object, or a list of 'Event' objects is
461
+ given as an argument.
462
+ """
463
+ if isinstance(E, Event):
464
+ buf = E.serialize()
465
+ else:
466
+ buf = ''
467
+ num = 0
468
+ for e in E:
469
+ if not(isinstance(e, Event)):
470
+ raise 'Element %i in given list is not an Event' % num
471
+ buf = buf + e.serialize()
472
+ num = num + 1
473
+
474
+ if reponse:
475
+ command = PUT_EVT
476
+ else:
477
+ command = PUT_EVT_NORESPONSE
478
+
479
+ self.sendRequest(command, buf)
480
+
481
+ if reponse:
482
+ (status, bufsize, resp_buf) = self.receiveResponse()
483
+ if status != PUT_OK:
484
+ raise IOError('Events could not be written.')
485
+
486
+ def putData(self, D, response=True):
487
+ """
488
+ putData(D) -- writes samples that must be given as a NUMPY array,
489
+ samples x channels. The type of the samples (D) and the number of
490
+ channels must match the corresponding quantities in the FieldTrip
491
+ buffer.
492
+ """
493
+
494
+ if not(isinstance(D, numpy.ndarray)) or len(D.shape) != 2:
495
+ raise ValueError(
496
+ 'Data must be given as a NUMPY array (samples x channels)')
497
+
498
+ nSamp = D.shape[0]
499
+ nChan = D.shape[1]
500
+
501
+ (dataType, dataBuf) = serialize(D)
502
+
503
+ dataBufSize = len(dataBuf)
504
+
505
+ if response:
506
+ command = PUT_DAT
507
+ else:
508
+ command = PUT_DAT_NORESPONSE
509
+
510
+ request = struct.pack('HHI', VERSION, command, 16 + dataBufSize)
511
+ dataDef = struct.pack('IIII', nChan, nSamp, dataType, dataBufSize)
512
+ self.sendRaw(request + dataDef + dataBuf)
513
+
514
+ if response:
515
+ (status, bufsize, resp_buf) = self.receiveResponse()
516
+ if status != PUT_OK:
517
+ raise IOError('Samples could not be written.')
518
+
519
+ def poll(self):
520
+
521
+ request = struct.pack('HHIIII', VERSION, WAIT_DAT, 12, 0, 0, 0)
522
+ self.sendRaw(request)
523
+
524
+ (status, bufsize, resp_buf) = self.receiveResponse()
525
+
526
+ if status != WAIT_OK or bufsize < 8:
527
+ raise IOError('Polling failed.')
528
+
529
+ return struct.unpack('II', resp_buf[0:8])
530
+
531
+ def wait(self, nsamples, nevents, timeout):
532
+ request = struct.pack('HHIIII', VERSION, WAIT_DAT,
533
+ 12, int(nsamples), int(nevents), int(timeout))
534
+ self.sendRaw(request)
535
+
536
+ (status, bufsize, resp_buf) = self.receiveResponse()
537
+
538
+ if status != WAIT_OK or bufsize < 8:
539
+ raise IOError('Wait request failed.')
540
+
541
+ return struct.unpack('II', resp_buf[0:8])
542
+
543
+ if __name__ == "__main__":
544
+ # Just a small demo for testing purposes...
545
+ # This should be moved to a separate file at some point
546
+ import sys
547
+
548
+ hostname = 'localhost'
549
+ port = 1972
550
+
551
+ if len(sys.argv) > 1:
552
+ hostname = sys.argv[1]
553
+ if len(sys.argv) > 2:
554
+ try:
555
+ port = int(sys.argv[2])
556
+ except:
557
+ print(('Error: second argument (%s) must be a valid (=integer)'
558
+ ' port number' % sys.argv[2]))
559
+ sys.exit(1)
560
+
561
+ ftc = Client()
562
+
563
+ print('Trying to connect to buffer on %s:%i ...' % (hostname, port))
564
+ ftc.connect(hostname, port)
565
+
566
+ print('\nConnected - trying to read header...')
567
+ H = ftc.getHeader()
568
+
569
+ if H is None:
570
+ print('Failed!')
571
+ else:
572
+ print(H)
573
+ print(H.labels)
574
+
575
+ if H.nSamples > 0:
576
+ print('\nTrying to read last sample...')
577
+ index = H.nSamples - 1
578
+ D = ftc.getData([index, index])
579
+ print(D)
580
+
581
+ if H.nEvents > 0:
582
+ print('\nTrying to read (all) events...')
583
+ E = ftc.getEvents()
584
+ for e in E:
585
+ print(e)
586
+
587
+ print(ftc.poll())
588
+
589
+ ftc.disconnect()