py-neuromodulation 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/build/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
- docs/build/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +233 -0
- docs/build/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/build/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
- docs/build/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/build/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
- docs/build/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
- docs/build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +239 -0
- docs/build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
- docs/build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
- docs/build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/source/_build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +76 -0
- docs/source/_build/html/_downloads/0d0d0a76e8f648d5d3cbc47da6351932/plot_real_time_demo.py +97 -0
- docs/source/_build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +240 -0
- docs/source/_build/html/_downloads/5d73cadc59a8805c47e3b84063afc157/plot_example_BIDS.py +233 -0
- docs/source/_build/html/_downloads/7660317fa5a6bfbd12fcca9961457fc4/plot_example_rmap_computing.py +63 -0
- docs/source/_build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/source/_build/html/_downloads/839e5b319379f7fd9e867deb00fd797f/plot_example_gridPointProjection.py +210 -0
- docs/source/_build/html/_downloads/ae8be19afe5e559f011fc9b138968ba0/plot_first_demo.py +192 -0
- docs/source/_build/html/_downloads/b8b06cacc17969d3725a0b6f1d7741c5/plot_example_sharpwave_analysis.py +219 -0
- docs/source/_build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +121 -0
- docs/source/_build/html/_downloads/c31a86c0b68cb4167d968091ace8080d/plot_example_add_feature.py +68 -0
- docs/source/_build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/source/_build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +189 -0
- docs/source/_build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/source/auto_examples/plot_0_first_demo.py +189 -0
- docs/source/auto_examples/plot_1_example_BIDS.py +240 -0
- docs/source/auto_examples/plot_2_example_add_feature.py +76 -0
- docs/source/auto_examples/plot_3_example_sharpwave_analysis.py +219 -0
- docs/source/auto_examples/plot_4_example_gridPointProjection.py +210 -0
- docs/source/auto_examples/plot_5_example_rmap_computing.py +64 -0
- docs/source/auto_examples/plot_6_real_time_demo.py +121 -0
- docs/source/conf.py +105 -0
- examples/plot_0_first_demo.py +189 -0
- examples/plot_1_example_BIDS.py +240 -0
- examples/plot_2_example_add_feature.py +76 -0
- examples/plot_3_example_sharpwave_analysis.py +219 -0
- examples/plot_4_example_gridPointProjection.py +210 -0
- examples/plot_5_example_rmap_computing.py +64 -0
- examples/plot_6_real_time_demo.py +121 -0
- packages/realtime_decoding/build/lib/realtime_decoding/__init__.py +4 -0
- packages/realtime_decoding/build/lib/realtime_decoding/decoder.py +104 -0
- packages/realtime_decoding/build/lib/realtime_decoding/features.py +163 -0
- packages/realtime_decoding/build/lib/realtime_decoding/helpers.py +15 -0
- packages/realtime_decoding/build/lib/realtime_decoding/run_decoding.py +345 -0
- packages/realtime_decoding/build/lib/realtime_decoding/trainer.py +54 -0
- packages/tmsi/build/lib/TMSiFileFormats/__init__.py +37 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/__init__.py +36 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/lsl_stream_writer.py +200 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_file_writer.py +496 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_to_edf_converter.py +236 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/xdf_file_writer.py +977 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/__init__.py +35 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/edf_reader.py +116 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/poly5reader.py +294 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/xdf_reader.py +229 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_writer.py +102 -0
- packages/tmsi/build/lib/TMSiPlotters/__init__.py +2 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/__init__.py +39 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/_plotter_gui.py +234 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/plotting_gui.py +440 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/__init__.py +44 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/hd_emg_plotter.py +446 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/impedance_plotter.py +589 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/signal_plotter.py +1326 -0
- packages/tmsi/build/lib/TMSiSDK/__init__.py +54 -0
- packages/tmsi/build/lib/TMSiSDK/device.py +588 -0
- packages/tmsi/build/lib/TMSiSDK/devices/__init__.py +34 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/TMSi_Device_API.py +1764 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/__init__.py +34 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_device.py +1366 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_types.py +520 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/xml_saga_config.py +165 -0
- packages/tmsi/build/lib/TMSiSDK/error.py +95 -0
- packages/tmsi/build/lib/TMSiSDK/sample_data.py +63 -0
- packages/tmsi/build/lib/TMSiSDK/sample_data_server.py +99 -0
- packages/tmsi/build/lib/TMSiSDK/settings.py +45 -0
- packages/tmsi/build/lib/TMSiSDK/tmsi_device.py +111 -0
- packages/tmsi/build/lib/__init__.py +4 -0
- packages/tmsi/build/lib/apex_sdk/__init__.py +34 -0
- packages/tmsi/build/lib/apex_sdk/device/__init__.py +41 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API.py +1009 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_enums.py +239 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_structures.py +668 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_device.py +1611 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_dongle.py +38 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_event_reader.py +57 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_channel.py +44 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_config.py +150 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_const.py +36 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_impedance_channel.py +48 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_info.py +108 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/dongle_info.py +39 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/download_measurement.py +77 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/eeg_measurement.py +150 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/impedance_measurement.py +129 -0
- packages/tmsi/build/lib/apex_sdk/device/threads/conversion_thread.py +59 -0
- packages/tmsi/build/lib/apex_sdk/device/threads/sampling_thread.py +57 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_channel.py +83 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_device.py +201 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_device_enums.py +103 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_dongle.py +43 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_event_reader.py +50 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_measurement.py +118 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/__init__.py +33 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/event_data.py +44 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data.py +50 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data_server.py +136 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_errors/error.py +126 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_sdk.py +113 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/apex/apex_structure_generator.py +134 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/decorators.py +60 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/logger_filter.py +42 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/singleton.py +42 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/support_functions.py +72 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/tmsi_logger.py +98 -0
- py_neuromodulation/{helper.py → _write_example_dataset_helper.py} +1 -1
- py_neuromodulation/nm_EpochStream.py +2 -3
- py_neuromodulation/nm_IO.py +43 -70
- py_neuromodulation/nm_RMAP.py +308 -11
- py_neuromodulation/nm_analysis.py +1 -1
- py_neuromodulation/nm_artifacts.py +25 -0
- py_neuromodulation/nm_bispectra.py +64 -29
- py_neuromodulation/nm_bursts.py +44 -30
- py_neuromodulation/nm_coherence.py +2 -1
- py_neuromodulation/nm_features.py +4 -2
- py_neuromodulation/nm_filter.py +63 -32
- py_neuromodulation/nm_filter_preprocessing.py +91 -0
- py_neuromodulation/nm_fooof.py +47 -29
- py_neuromodulation/nm_mne_connectivity.py +1 -1
- py_neuromodulation/nm_normalization.py +50 -74
- py_neuromodulation/nm_oscillatory.py +151 -31
- py_neuromodulation/nm_plots.py +13 -10
- py_neuromodulation/nm_rereference.py +10 -8
- py_neuromodulation/nm_run_analysis.py +28 -13
- py_neuromodulation/nm_sharpwaves.py +103 -136
- py_neuromodulation/nm_stats.py +44 -30
- py_neuromodulation/nm_stream_abc.py +18 -10
- py_neuromodulation/nm_stream_offline.py +181 -40
- py_neuromodulation/utils/_logging.py +24 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/METADATA +182 -142
- py_neuromodulation-0.0.3.dist-info/RECORD +188 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/WHEEL +2 -1
- py_neuromodulation-0.0.3.dist-info/top_level.txt +5 -0
- tests/__init__.py +0 -0
- tests/conftest.py +117 -0
- tests/test_all_examples.py +10 -0
- tests/test_all_features.py +63 -0
- tests/test_bispectra.py +70 -0
- tests/test_bursts.py +105 -0
- tests/test_feature_sampling_rates.py +143 -0
- tests/test_fooof.py +16 -0
- tests/test_initalization_offline_stream.py +41 -0
- tests/test_multiprocessing.py +58 -0
- tests/test_nan_values.py +29 -0
- tests/test_nm_filter.py +95 -0
- tests/test_nm_resample.py +63 -0
- tests/test_normalization_settings.py +146 -0
- tests/test_notch_filter.py +31 -0
- tests/test_osc_features.py +424 -0
- tests/test_preprocessing_filter.py +151 -0
- tests/test_rereference.py +171 -0
- tests/test_sampling.py +57 -0
- tests/test_settings_change_after_init.py +76 -0
- tests/test_sharpwave.py +165 -0
- tests/test_target_channel_add.py +100 -0
- tests/test_timing.py +80 -0
- py_neuromodulation/data/README +0 -6
- py_neuromodulation/data/dataset_description.json +0 -8
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/MOV_aligned_features_ch_ECOG_RIGHT_0_all.png +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/all_feature_plt.pdf +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_FEATURES.csv +0 -182
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_LM_ML_RES.p +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SETTINGS.json +0 -273
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SIDECAR.json +0 -6
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_decoding_performance.png +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_nm_channels.csv +0 -11
- py_neuromodulation/data/participants.json +0 -32
- py_neuromodulation/data/participants.tsv +0 -2
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +0 -5
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +0 -11
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +0 -11
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +0 -18
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +0 -35
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +0 -13
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +0 -2
- py_neuromodulation/grid_cortex.tsv +0 -40
- py_neuromodulation/grid_subcortex.tsv +0 -1429
- py_neuromodulation/nm_settings.json +0 -290
- py_neuromodulation/plots/STN_surf.mat +0 -0
- py_neuromodulation/plots/Vertices.mat +0 -0
- py_neuromodulation/plots/faces.mat +0 -0
- py_neuromodulation/plots/grid.mat +0 -0
- py_neuromodulation/py_neuromodulation.egg-info/PKG-INFO +0 -104
- py_neuromodulation/py_neuromodulation.egg-info/dependency_links.txt +0 -1
- py_neuromodulation/py_neuromodulation.egg-info/requires.txt +0 -26
- py_neuromodulation/py_neuromodulation.egg-info/top_level.txt +0 -1
- py_neuromodulation-0.0.2.dist-info/RECORD +0 -73
- /py_neuromodulation/{py_neuromodulation.egg-info/SOURCES.txt → utils/__init__.py} +0 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,520 @@
|
|
|
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 ${saga_types.py}
|
|
26
|
+
* @brief SAGA Device Types.
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from ...device import (
|
|
34
|
+
DeviceInterfaceType,
|
|
35
|
+
DeviceState,
|
|
36
|
+
DeviceConfig,
|
|
37
|
+
ChannelType,
|
|
38
|
+
DeviceChannel,
|
|
39
|
+
DeviceSensor,
|
|
40
|
+
ReferenceMethod,
|
|
41
|
+
ReferenceSwitch,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
_TMSI_DEVICE_ID_NONE = 0xFFFF
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SagaConst:
|
|
48
|
+
TMSI_DEVICE_ID_NONE = 0xFFFF
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SagaInfo:
|
|
52
|
+
"""'DeviceInfo' holds the static device information. It has the next properties:
|
|
53
|
+
|
|
54
|
+
ds_interface : 'DeviceInterfaceType' Indicates interface-type between the PC
|
|
55
|
+
and the docking station.
|
|
56
|
+
|
|
57
|
+
dr_interface : 'DeviceInterfaceType' Indicates interface-type between the
|
|
58
|
+
PC / docking station and the data recorder.
|
|
59
|
+
|
|
60
|
+
ds_serial_number : 'int' The serial number of the docking station.
|
|
61
|
+
|
|
62
|
+
dr_serial_number : 'int' The serial number of the data recorder.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
ds_interface=DeviceInterfaceType.none,
|
|
69
|
+
dr_interface=DeviceInterfaceType.none,
|
|
70
|
+
):
|
|
71
|
+
self.ds_interface = ds_interface
|
|
72
|
+
self.dr_interface = dr_interface
|
|
73
|
+
self.id = _TMSI_DEVICE_ID_NONE
|
|
74
|
+
self.state = DeviceState.disconnected
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class SagaConfig(DeviceConfig):
|
|
78
|
+
"""'DeviceConfig' holds the actual device configuration"""
|
|
79
|
+
|
|
80
|
+
def __init__(self):
|
|
81
|
+
self._parent = None
|
|
82
|
+
self._base_sample_rate = 0 # base sample reate in Hz
|
|
83
|
+
self._configured_interface = DeviceInterfaceType.none
|
|
84
|
+
self._triggers = 0 # 0= Disabled, 1= external triggers enabled
|
|
85
|
+
self._reference_method = 0 # 0= Common reference, 1=average reference
|
|
86
|
+
self._auto_reference_method = 0 # 0= fixed method, 1= autoswitch to average reference when CREF is out of range
|
|
87
|
+
self._dr_sync_out_divider = (
|
|
88
|
+
-1
|
|
89
|
+
) # SetBaseSampleRateHz/SyncOutDiv, -1 = marker button
|
|
90
|
+
self._dr_sync_out_duty_cycle = 0 # DR Sync dutycycle
|
|
91
|
+
self._repair_logging = 0 # 0 = Disabled, 1 = BackupLogging enabled,
|
|
92
|
+
self._num_channels = 0
|
|
93
|
+
# Total number of channels : active and inactive
|
|
94
|
+
self._channels = [] # Total channel list : active and inactive
|
|
95
|
+
self._sample_rates = [] # List with sample_rate per channel-type
|
|
96
|
+
self._num_sensors = 0 # Number of sensors
|
|
97
|
+
self._interface_bandwidth = 0
|
|
98
|
+
# Data bandwidth in Mbits/s for current interface.
|
|
99
|
+
for chan_type in ChannelType:
|
|
100
|
+
self._sample_rates.append(SagaSampleRate(chan_type))
|
|
101
|
+
|
|
102
|
+
def get_sample_rate(self, chan_type):
|
|
103
|
+
"""'int' the sample-rate of the specified channel-type-group.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
channel_type : 'ChannelType' The channel-type-group.
|
|
107
|
+
"""
|
|
108
|
+
return self._sample_rates[chan_type.value].sample_rate
|
|
109
|
+
|
|
110
|
+
def set_sample_rate(self, chan_type, bsr_div):
|
|
111
|
+
"""Sets the sample-rate of the specified channel-type-group.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
channel_type : 'ChannelType' The channel-type-group of which the
|
|
115
|
+
sample-rate must be updated.
|
|
116
|
+
It is possible to set all channels to the same sample-rate within one
|
|
117
|
+
call. Then 'ChannelType.all_types' must be used.
|
|
118
|
+
|
|
119
|
+
base_sample_rate_divider: 'int' The divider to indicate to what fraction
|
|
120
|
+
of the active base-sample-rate the sample-rate of the channel-type-group
|
|
121
|
+
must be set. Only the values 1, 2, 4 and 8 are possible.
|
|
122
|
+
For example if the base-sample-rate=4000 and base_sample_rate_divider=8,
|
|
123
|
+
the sample-rate will become 4000/8 = 500 Hz
|
|
124
|
+
|
|
125
|
+
Note:
|
|
126
|
+
Upon a change of the sample-rate of a channel-type-group, automatically
|
|
127
|
+
the sample-rate of the channels of that channel-type are updated.
|
|
128
|
+
|
|
129
|
+
It is advised to "refresh" the applications' local "variables"
|
|
130
|
+
after the sample-rate of a channel-type-group has been updated.
|
|
131
|
+
"""
|
|
132
|
+
if (bsr_div == 1) or (bsr_div == 2) or (bsr_div == 4) or (bsr_div == 8):
|
|
133
|
+
bsr_shift = 0
|
|
134
|
+
while bsr_div > 1:
|
|
135
|
+
bsr_shift += 1
|
|
136
|
+
bsr_div /= 2
|
|
137
|
+
for ch in self._channels:
|
|
138
|
+
if (
|
|
139
|
+
(ch.type == chan_type)
|
|
140
|
+
or (chan_type == ChannelType.all_types)
|
|
141
|
+
and (ch.chan_divider != -1)
|
|
142
|
+
):
|
|
143
|
+
# Only update the chan_divider of active channels
|
|
144
|
+
if ch.chan_divider != -1:
|
|
145
|
+
ch.chan_divider = bsr_shift
|
|
146
|
+
if self._parent != None:
|
|
147
|
+
self._parent._update_config()
|
|
148
|
+
else:
|
|
149
|
+
print(
|
|
150
|
+
"\nProvided base_sample_rate_divider is invalid. Sample rate can not be updated.\n"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def base_sample_rate(self):
|
|
155
|
+
"""'int' The active base-sample-rate of the device"""
|
|
156
|
+
return self._base_sample_rate
|
|
157
|
+
|
|
158
|
+
@base_sample_rate.setter
|
|
159
|
+
def base_sample_rate(self, var):
|
|
160
|
+
"""Sets the base-sample-rate of the device.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
sample_rate : 'int' new base sample rate
|
|
164
|
+
|
|
165
|
+
Note:
|
|
166
|
+
Upon a change of the base-sample-rate, automatically the sample-rate
|
|
167
|
+
of the channel-type-groups (and thus also the sample-rate of every channel)
|
|
168
|
+
changes.
|
|
169
|
+
|
|
170
|
+
It is advised to "refresh" the applications' local "variables"
|
|
171
|
+
after the base-sample-rate has been updated.
|
|
172
|
+
|
|
173
|
+
"""
|
|
174
|
+
if (var == 4000) or (var == 4096):
|
|
175
|
+
if self._base_sample_rate != var:
|
|
176
|
+
# The device does not support a configuration with different base
|
|
177
|
+
# sample rates for sample rate and sync out. Hence sync out config
|
|
178
|
+
# has to be update as well
|
|
179
|
+
sync_out_freq = (
|
|
180
|
+
self._base_sample_rate / self._dr_sync_out_divider
|
|
181
|
+
)
|
|
182
|
+
self._base_sample_rate = var
|
|
183
|
+
self.set_sync_out_config(freq=sync_out_freq)
|
|
184
|
+
else:
|
|
185
|
+
print(
|
|
186
|
+
"\nProvided base_sample_rate is invalid. Sample rate can not be updated.\n"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def sample_rate(self):
|
|
191
|
+
"""<int> The rate of the current configuration, with which
|
|
192
|
+
sample-sets are sent during a measurement. This always
|
|
193
|
+
equals the sample-rate of the COUNTER-channel.
|
|
194
|
+
"""
|
|
195
|
+
return self._sample_rates[ChannelType.counter.value].sample_rate
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def configured_interface(self):
|
|
199
|
+
return self._configured_interface
|
|
200
|
+
|
|
201
|
+
def set_interface_type(self, dr_interface_type):
|
|
202
|
+
"""Changes the configured interface type of the device.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
dr_interface_type: 'DeviceInterfaceType' The interface type that needs
|
|
206
|
+
to be updated.
|
|
207
|
+
|
|
208
|
+
Note:
|
|
209
|
+
The interface type switch applies to the DR-DS interface type.
|
|
210
|
+
Interface types that can be configured on SAGA are docked,
|
|
211
|
+
optical and wifi.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
if dr_interface_type.value != self._configured_interface:
|
|
215
|
+
print("DR-DS interface is changed to: ")
|
|
216
|
+
print(dr_interface_type)
|
|
217
|
+
self._configured_interface = dr_interface_type.value
|
|
218
|
+
|
|
219
|
+
if self._parent != None:
|
|
220
|
+
self._parent._update_config()
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def num_channels(self):
|
|
224
|
+
"""'int' The total number of channels (enabled and disabled)"""
|
|
225
|
+
return self._num_channels
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def channels(self):
|
|
229
|
+
"""'list DeviceChannel' The total list of channels (enabled and disabled)"""
|
|
230
|
+
chan_list = []
|
|
231
|
+
for ch in self._channels:
|
|
232
|
+
sensor = ch.sensor
|
|
233
|
+
if ch.sensor != None:
|
|
234
|
+
sensor = DeviceSensor(
|
|
235
|
+
ch.sensor.idx_total_channel_list,
|
|
236
|
+
ch.sensor.id,
|
|
237
|
+
ch.sensor.serial_nr,
|
|
238
|
+
ch.sensor.product_id,
|
|
239
|
+
ch.sensor.name,
|
|
240
|
+
ch.sensor.unit_name,
|
|
241
|
+
ch.sensor.exp,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
dev_ch = DeviceChannel(
|
|
245
|
+
ch.type,
|
|
246
|
+
ch.sample_rate,
|
|
247
|
+
ch.alt_name,
|
|
248
|
+
ch.unit_name,
|
|
249
|
+
(ch.chan_divider != -1),
|
|
250
|
+
sensor,
|
|
251
|
+
)
|
|
252
|
+
chan_list.append(dev_ch)
|
|
253
|
+
return chan_list
|
|
254
|
+
|
|
255
|
+
@channels.setter
|
|
256
|
+
def channels(self, var):
|
|
257
|
+
"""Updates the channel lists in the device.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
ch_list : 'list DeviceChannel'
|
|
261
|
+
|
|
262
|
+
The channel-properties 'enabled' and 'name' can be modified. The other
|
|
263
|
+
channel-properties are read-only.
|
|
264
|
+
|
|
265
|
+
Note:
|
|
266
|
+
'ch_list' must contain all channels (enabled and disabled), in the
|
|
267
|
+
same sequence as retrieved with 'Device.config.channels'
|
|
268
|
+
|
|
269
|
+
It is advised to "refresh" the applications' local "variables"
|
|
270
|
+
after the channel list has been updated.
|
|
271
|
+
"""
|
|
272
|
+
for idx, channel in enumerate(var):
|
|
273
|
+
if self._channels[idx].type.value < ChannelType.status.value:
|
|
274
|
+
self._channels[idx].alt_name = channel.name
|
|
275
|
+
if channel.enabled == True:
|
|
276
|
+
if (
|
|
277
|
+
self._sample_rates[
|
|
278
|
+
self._channels[idx].type.value
|
|
279
|
+
].sample_rate
|
|
280
|
+
!= 0
|
|
281
|
+
):
|
|
282
|
+
self._channels[idx].sample_rate = self._sample_rates[
|
|
283
|
+
self._channels[idx].type.value
|
|
284
|
+
].sample_rate
|
|
285
|
+
self._channels[idx].chan_divider = self._sample_rates[
|
|
286
|
+
self._channels[idx].type.value
|
|
287
|
+
].chan_divider
|
|
288
|
+
else:
|
|
289
|
+
self._sample_rates[
|
|
290
|
+
self._channels[idx].type.value
|
|
291
|
+
].sample_rate = self._sample_rates[
|
|
292
|
+
ChannelType.counter.value
|
|
293
|
+
].sample_rate
|
|
294
|
+
self._sample_rates[
|
|
295
|
+
self._channels[idx].type.value
|
|
296
|
+
].chan_divider = self._sample_rates[
|
|
297
|
+
ChannelType.counter.value
|
|
298
|
+
].chan_divider
|
|
299
|
+
self._channels[idx].sample_rate = self._sample_rates[
|
|
300
|
+
self._channels[idx].type.value
|
|
301
|
+
].sample_rate
|
|
302
|
+
self._channels[idx].chan_divider = self._sample_rates[
|
|
303
|
+
self._channels[idx].type.value
|
|
304
|
+
].chan_divider
|
|
305
|
+
else:
|
|
306
|
+
self._channels[idx].chan_divider = -1
|
|
307
|
+
if self._parent != None:
|
|
308
|
+
self._parent._update_config()
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def reference_method(self):
|
|
312
|
+
"""'ReferenceMethod' the reference method applied to the UNI channels,
|
|
313
|
+
'ReferenceSwitch' the switching of reference mode when common reference is disconnected"""
|
|
314
|
+
return (
|
|
315
|
+
ReferenceMethod(self._reference_method).name,
|
|
316
|
+
ReferenceSwitch(self._auto_reference_method).name,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
@reference_method.setter
|
|
320
|
+
def reference_method(self, reference_type):
|
|
321
|
+
"""Sets the reference method for the UNI channels
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
reference_type: 'ReferenceMethod' the type of reference method that
|
|
325
|
+
should be used for measuring the UNI channels
|
|
326
|
+
'ReferenceSwitch' the switching of reference mode when common reference is disconnected
|
|
327
|
+
|
|
328
|
+
"""
|
|
329
|
+
if not type(reference_type) is tuple:
|
|
330
|
+
if isinstance(reference_type, ReferenceMethod):
|
|
331
|
+
self._reference_method = reference_type.value
|
|
332
|
+
elif isinstance(reference_type, ReferenceSwitch):
|
|
333
|
+
self._auto_reference_method = reference_type.value
|
|
334
|
+
|
|
335
|
+
else:
|
|
336
|
+
for ind in range(len(reference_type)):
|
|
337
|
+
if isinstance(reference_type[ind], ReferenceMethod):
|
|
338
|
+
self._reference_method = reference_type[ind].value
|
|
339
|
+
elif isinstance(reference_type[ind], ReferenceSwitch):
|
|
340
|
+
self._auto_reference_method = reference_type[ind].value
|
|
341
|
+
|
|
342
|
+
if self._parent != None:
|
|
343
|
+
self._parent._update_config()
|
|
344
|
+
|
|
345
|
+
@property
|
|
346
|
+
def triggers(self):
|
|
347
|
+
"""'Boolean', true when triggers are enabled or false when disabled"""
|
|
348
|
+
return bool(self._triggers)
|
|
349
|
+
|
|
350
|
+
@triggers.setter
|
|
351
|
+
def triggers(self, enable_triggers):
|
|
352
|
+
"""Sets the triggers to enabled or disabled"""
|
|
353
|
+
self._triggers = enable_triggers
|
|
354
|
+
if self._parent != None:
|
|
355
|
+
self._parent._update_config()
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def repair_logging(self):
|
|
359
|
+
"""Boolean, true in case samples are logged on Recorder for repair in case of data loss"""
|
|
360
|
+
return self._repair_logging
|
|
361
|
+
|
|
362
|
+
@repair_logging.setter
|
|
363
|
+
def repair_logging(self, enable_logging):
|
|
364
|
+
"""Sets repair logging to enabled or disabled"""
|
|
365
|
+
self._repair_logging = enable_logging
|
|
366
|
+
if self._parent != None:
|
|
367
|
+
self._parent._update_config()
|
|
368
|
+
|
|
369
|
+
def get_sync_out_config(self):
|
|
370
|
+
"""Sync out configuration, shows whether sync out is in marker mode or square wave mode with corresponding frequency and duty cycle"""
|
|
371
|
+
if self._dr_sync_out_divider == -1:
|
|
372
|
+
freq = -1
|
|
373
|
+
else:
|
|
374
|
+
freq = self._base_sample_rate / self._dr_sync_out_divider
|
|
375
|
+
return (
|
|
376
|
+
self._dr_sync_out_divider == -1,
|
|
377
|
+
freq,
|
|
378
|
+
self._dr_sync_out_duty_cycle / 10,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
def set_sync_out_config(self, marker=False, freq=None, duty_cycle=None):
|
|
382
|
+
"""Set sync out to marker mode or square wave mode with corresponding frequency and duty cycle"""
|
|
383
|
+
if marker:
|
|
384
|
+
self._dr_sync_out_divider = -1
|
|
385
|
+
else:
|
|
386
|
+
if freq:
|
|
387
|
+
self._dr_sync_out_divider = round(self._base_sample_rate / freq)
|
|
388
|
+
if duty_cycle:
|
|
389
|
+
self._dr_sync_out_duty_cycle = duty_cycle * 10
|
|
390
|
+
|
|
391
|
+
if self._parent != None:
|
|
392
|
+
self._parent._update_config()
|
|
393
|
+
|
|
394
|
+
@property
|
|
395
|
+
def interface_bandwidth(self):
|
|
396
|
+
return self._interface_bandwidth
|
|
397
|
+
|
|
398
|
+
@interface_bandwidth.setter
|
|
399
|
+
def interface_bandwidth(self, bandwidth):
|
|
400
|
+
"""Sets the data bandwidth in Mbits/s for current interface."""
|
|
401
|
+
self._interface_bandwidth = bandwidth
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
class SagaSensor:
|
|
405
|
+
"""<SagaSensor> represents the sensor-data of a channel. It has the next properties:
|
|
406
|
+
|
|
407
|
+
offset : <float> Offset for the seonsor-channel.
|
|
408
|
+
|
|
409
|
+
gain : <float> Value to convert the sensor value to the correct unit value
|
|
410
|
+
|
|
411
|
+
exp : <int> Exponent for the unit, e.g. milli = -3 this gives for a
|
|
412
|
+
unit_name V a result mV.
|
|
413
|
+
|
|
414
|
+
name : <string> The name of the seonsor-channel.
|
|
415
|
+
|
|
416
|
+
unit_name : <string> The name of the unit (e.g. 'μVolt) of the sensor-data.
|
|
417
|
+
|
|
418
|
+
"""
|
|
419
|
+
|
|
420
|
+
def __init__(self):
|
|
421
|
+
self.idx_total_channel_list = -1
|
|
422
|
+
self.id = -1
|
|
423
|
+
self.manufacturer_id = 0
|
|
424
|
+
self.serial_nr = 0
|
|
425
|
+
self.product_id = 0
|
|
426
|
+
self.offset = 0
|
|
427
|
+
self.gain = 1
|
|
428
|
+
self.exp = 0
|
|
429
|
+
self.__name = ""
|
|
430
|
+
self.__unit_name = ""
|
|
431
|
+
|
|
432
|
+
@property
|
|
433
|
+
def name(self):
|
|
434
|
+
"""'string' The name of the unit (e.g. 'μVolt) of the sample-data of the channel."""
|
|
435
|
+
return self.__name
|
|
436
|
+
|
|
437
|
+
@name.setter
|
|
438
|
+
def name(self, bname):
|
|
439
|
+
self.__name = ""
|
|
440
|
+
new_name = bytearray()
|
|
441
|
+
for i in range(len(bname)):
|
|
442
|
+
if bname[i] > 127:
|
|
443
|
+
new_name.append(194) # 0xC2
|
|
444
|
+
new_name.append(bname[i])
|
|
445
|
+
if len(new_name) > 0:
|
|
446
|
+
self.__name = new_name.decode("utf8").rstrip("\x00")
|
|
447
|
+
|
|
448
|
+
@property
|
|
449
|
+
def unit_name(self):
|
|
450
|
+
"""'string' The name of the unit (e.g. 'μVolt) of the sample-data of the channel."""
|
|
451
|
+
return self.__unit_name
|
|
452
|
+
|
|
453
|
+
@unit_name.setter
|
|
454
|
+
def unit_name(self, bname):
|
|
455
|
+
# A unit-name can start with the micro-character (0xB5). In that case must
|
|
456
|
+
# the micro-character be converted to it;s utf8-representation : 0xC2B5
|
|
457
|
+
self.__unit_name = ""
|
|
458
|
+
new_name = bytearray()
|
|
459
|
+
for i in range(len(bname)):
|
|
460
|
+
if bname[i] > 127:
|
|
461
|
+
new_name.append(194) # 0xC2
|
|
462
|
+
new_name.append(bname[i])
|
|
463
|
+
if len(new_name) > 0:
|
|
464
|
+
self.__unit_name = new_name.decode("utf8").rstrip("\x00")
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
class SagaChannel:
|
|
468
|
+
"""<DeviceChannel> represents a device channel. It has the next properties:
|
|
469
|
+
|
|
470
|
+
type : <ChannelType> Indicates the group-type of a channel.
|
|
471
|
+
|
|
472
|
+
sample_rate : <int> The curring sampling rate of the channel during a 'normal' measurement.
|
|
473
|
+
|
|
474
|
+
name : <string> The name of the channel.
|
|
475
|
+
|
|
476
|
+
unit_name : <string> The name of the unit (e.g. 'μVolt) of the sample-data of the channel.
|
|
477
|
+
|
|
478
|
+
enabled : <bool> Indicates if a channel is enavled for measuring
|
|
479
|
+
|
|
480
|
+
Note: The properties <name> and <anabled> can be modified. The other properties
|
|
481
|
+
are read-only.
|
|
482
|
+
"""
|
|
483
|
+
|
|
484
|
+
def __init__(self):
|
|
485
|
+
self.type = 0
|
|
486
|
+
self.format = 0
|
|
487
|
+
self.sample_rate = 0
|
|
488
|
+
self.chan_divider = -1
|
|
489
|
+
self.imp_divider = -1
|
|
490
|
+
self.exp = 1
|
|
491
|
+
self.__unit_name = "-"
|
|
492
|
+
self.def_name = "-"
|
|
493
|
+
self.alt_name = "-"
|
|
494
|
+
self.sensor = None
|
|
495
|
+
|
|
496
|
+
@property
|
|
497
|
+
def unit_name(self):
|
|
498
|
+
"""'string' The name of the unit (e.g. 'μVolt) of the sample-data of the channel."""
|
|
499
|
+
return self.__unit_name
|
|
500
|
+
|
|
501
|
+
@unit_name.setter
|
|
502
|
+
def unit_name(self, name):
|
|
503
|
+
# A unit-name can start with the micro-character (0xB5). In that case must
|
|
504
|
+
# the micro-character be converted to it;s utf8-representation : 0xC2B5
|
|
505
|
+
self.__unit_name = ""
|
|
506
|
+
bname = name.encode("windows-1252")
|
|
507
|
+
new_name = bytearray()
|
|
508
|
+
for i in range(len(bname)):
|
|
509
|
+
if bname[i] > 127:
|
|
510
|
+
new_name.append(194) # 0xC2
|
|
511
|
+
new_name.append(bname[i])
|
|
512
|
+
if len(new_name) > 0:
|
|
513
|
+
self.__unit_name = new_name.decode("utf8")
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
class SagaSampleRate:
|
|
517
|
+
def __init__(self, type):
|
|
518
|
+
self.type = type
|
|
519
|
+
self.sample_rate = 0
|
|
520
|
+
self.chan_divider = -1
|
|
@@ -0,0 +1,165 @@
|
|
|
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 ${xml_saga_config.py}
|
|
26
|
+
* @brief Reading/Writing SAGA configuration via XML
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from xml.dom import minidom
|
|
34
|
+
import xml.etree.ElementTree as ET
|
|
35
|
+
|
|
36
|
+
from .saga_types import SagaConfig, SagaChannel
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def __prettify(elem):
|
|
40
|
+
"""Return a pretty-printed XML string for the Element."""
|
|
41
|
+
rough_string = ET.tostring(elem, "utf-8")
|
|
42
|
+
reparsed = minidom.parseString(rough_string)
|
|
43
|
+
return reparsed.toprettyxml(indent=" ")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def xml_write_config(filename, saga_config):
|
|
47
|
+
"""Writes a device configuration to the specified file.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
'filename' Location of the xml-file to write
|
|
51
|
+
|
|
52
|
+
'saga_config' device configuration to write
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
True if successfull, False otherwise.
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
root = ET.Element("DeviceConfig")
|
|
59
|
+
|
|
60
|
+
# 1. Write the device configuration properties
|
|
61
|
+
xml_device = ET.SubElement(root, "Device")
|
|
62
|
+
|
|
63
|
+
ET.SubElement(xml_device, "BaseSampleRateHz").text = str(
|
|
64
|
+
saga_config._base_sample_rate
|
|
65
|
+
)
|
|
66
|
+
ET.SubElement(xml_device, "ConfiguredInterface").text = str(
|
|
67
|
+
saga_config._configured_interface
|
|
68
|
+
)
|
|
69
|
+
ET.SubElement(xml_device, "Triggers").text = str(saga_config._triggers)
|
|
70
|
+
ET.SubElement(xml_device, "ReferenceMethod").text = str(
|
|
71
|
+
saga_config._reference_method
|
|
72
|
+
)
|
|
73
|
+
ET.SubElement(xml_device, "AutoReferenceMethod").text = str(
|
|
74
|
+
saga_config._auto_reference_method
|
|
75
|
+
)
|
|
76
|
+
ET.SubElement(xml_device, "DRSyncOutDiv").text = str(
|
|
77
|
+
saga_config._dr_sync_out_divider
|
|
78
|
+
)
|
|
79
|
+
ET.SubElement(xml_device, "DRSyncOutDutyCycl").text = str(
|
|
80
|
+
saga_config._dr_sync_out_duty_cycle
|
|
81
|
+
)
|
|
82
|
+
ET.SubElement(xml_device, "RepairLogging").text = str(
|
|
83
|
+
saga_config._repair_logging
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# 2. Write the channel list
|
|
87
|
+
xml_channels = ET.SubElement(root, "Channels")
|
|
88
|
+
for idx, saga_channel in enumerate(saga_config._channels):
|
|
89
|
+
|
|
90
|
+
xml_channel = ET.SubElement(xml_channels, "Channel")
|
|
91
|
+
|
|
92
|
+
ET.SubElement(xml_channel, "ChanNr").text = str(idx)
|
|
93
|
+
ET.SubElement(xml_channel, "ChanDivider").text = str(
|
|
94
|
+
saga_channel.chan_divider
|
|
95
|
+
)
|
|
96
|
+
ET.SubElement(
|
|
97
|
+
xml_channel, "AltChanName"
|
|
98
|
+
).text = saga_channel.alt_name
|
|
99
|
+
|
|
100
|
+
xml_data = __prettify(root)
|
|
101
|
+
|
|
102
|
+
xml_file = open(filename, "w")
|
|
103
|
+
xml_file.write(xml_data)
|
|
104
|
+
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
except:
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def xml_read_config(filename):
|
|
112
|
+
"""Read a device configuration from the specified file.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
'filename' Location of the xml-file to read
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
if successfull :
|
|
119
|
+
True and a 'SagaConfig'-object with the read configuration.
|
|
120
|
+
otherwise :
|
|
121
|
+
False and a default 'SagaConfig'-object
|
|
122
|
+
"""
|
|
123
|
+
result = True
|
|
124
|
+
saga_config = SagaConfig()
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
tree = ET.parse(filename)
|
|
128
|
+
root = tree.getroot()
|
|
129
|
+
|
|
130
|
+
for elem in root:
|
|
131
|
+
# print(len(elem))
|
|
132
|
+
for subelem in elem:
|
|
133
|
+
if elem.tag == "Device":
|
|
134
|
+
if subelem.tag == "BaseSampleRateHz":
|
|
135
|
+
saga_config._base_sample_rate = int(subelem.text)
|
|
136
|
+
if subelem.tag == "ConfiguredInterface":
|
|
137
|
+
saga_config._configured_interface = int(subelem.text)
|
|
138
|
+
if subelem.tag == "Triggers":
|
|
139
|
+
saga_config._triggers = int(subelem.text)
|
|
140
|
+
if subelem.tag == "ReferenceMethod":
|
|
141
|
+
saga_config._reference_method = int(subelem.text)
|
|
142
|
+
if subelem.tag == "AutoReferenceMethod":
|
|
143
|
+
saga_config._auto_reference_method = int(subelem.text)
|
|
144
|
+
if subelem.tag == "DRSyncOutDiv":
|
|
145
|
+
saga_config._dr_sync_out_divider = int(subelem.text)
|
|
146
|
+
if subelem.tag == "DRSyncOutDutyCycl":
|
|
147
|
+
saga_config._dr_sync_out_duty_cycle = int(subelem.text)
|
|
148
|
+
if subelem.tag == "RepairLogging":
|
|
149
|
+
saga_config._repair_logging = int(subelem.text)
|
|
150
|
+
if elem.tag == "Channels":
|
|
151
|
+
if subelem.tag == "Channel":
|
|
152
|
+
saga_config._num_channels += 1
|
|
153
|
+
channel = SagaChannel()
|
|
154
|
+
for subsubelem in subelem:
|
|
155
|
+
if subsubelem.tag == "ChanDivider":
|
|
156
|
+
channel.chan_divider = int(subsubelem.text)
|
|
157
|
+
if subsubelem.tag == "AltChanName":
|
|
158
|
+
channel.alt_name = subsubelem.text
|
|
159
|
+
|
|
160
|
+
saga_config._channels.append(channel)
|
|
161
|
+
except:
|
|
162
|
+
result = False
|
|
163
|
+
saga_config = None
|
|
164
|
+
|
|
165
|
+
return result, saga_config
|