sofar 0.3.1__py2.py3-none-any.whl → 1.1.0__py2.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.
- sofar/__init__.py +13 -7
- sofar/io.py +423 -0
- sofar/sofa.py +1795 -0
- sofar/sofa_conventions/VERSION +1 -0
- sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.csv +59 -0
- sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.json +444 -0
- sofar/{conventions/source → sofa_conventions/conventions}/FreeFieldHRIR_1.0.csv +3 -3
- sofar/{conventions → sofa_conventions/conventions}/FreeFieldHRIR_1.0.json +3 -3
- sofar/{conventions/source → sofa_conventions/conventions}/FreeFieldHRTF_1.0.csv +2 -2
- sofar/{conventions → sofa_conventions/conventions}/FreeFieldHRTF_1.0.json +3 -3
- sofar/{conventions/source → sofa_conventions/conventions}/GeneralFIR-E_2.0.csv +2 -2
- sofar/{conventions → sofa_conventions/conventions}/GeneralFIR-E_2.0.json +2 -2
- sofar/{conventions/source/GeneralFIR_2.0.csv → sofa_conventions/conventions/GeneralFIR_1.0.csv} +2 -2
- sofar/{conventions/GeneralFIR_2.0.json → sofa_conventions/conventions/GeneralFIR_1.0.json} +2 -2
- sofar/{conventions/source/GeneralFIR_1.0.csv → sofa_conventions/conventions/GeneralSOS_1.0.csv} +11 -11
- sofar/{conventions/GeneralFIR_1.0.json → sofa_conventions/conventions/GeneralSOS_1.0.json} +48 -37
- sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF-E_1.0.csv +3 -3
- sofar/{conventions → sofa_conventions/conventions}/GeneralTF-E_1.0.json +4 -4
- sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF_1.0.csv +1 -1
- sofar/{conventions → sofa_conventions/conventions}/GeneralTF_1.0.json +1 -1
- sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF_2.0.csv +4 -4
- sofar/{conventions → sofa_conventions/conventions}/GeneralTF_2.0.json +4 -4
- sofar/sofa_conventions/conventions/SimpleFreeFieldHRIR_1.0.csv +47 -0
- sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldHRIR_1.0.json +1 -1
- sofar/{conventions/source → sofa_conventions/conventions}/SimpleFreeFieldHRSOS_1.0.csv +1 -1
- sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldHRSOS_1.0.json +1 -1
- sofar/{conventions/source/SimpleFreeFieldHRTF_2.0.csv → sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.csv} +3 -3
- sofar/{conventions/SimpleFreeFieldHRTF_2.0.json → sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.json} +4 -4
- sofar/{conventions/source → sofa_conventions/conventions}/SimpleHeadphoneIR_1.0.csv +9 -9
- sofar/sofa_conventions/conventions/SimpleHeadphoneIR_1.0.json +396 -0
- sofar/{conventions/source → sofa_conventions/conventions}/SingleRoomMIMOSRIR_1.0.csv +18 -8
- sofar/{conventions → sofa_conventions/conventions}/SingleRoomMIMOSRIR_1.0.json +124 -50
- sofar/{conventions/source → sofa_conventions/conventions}/SingleRoomSRIR_1.0.csv +18 -8
- sofar/{conventions → sofa_conventions/conventions}/SingleRoomSRIR_1.0.json +124 -50
- sofar/{conventions/source → sofa_conventions/conventions/deprecated}/FreeFieldDirectivityTF_1.0.csv +2 -2
- sofar/{conventions → sofa_conventions/conventions/deprecated}/FreeFieldDirectivityTF_1.0.json +2 -2
- sofar/sofa_conventions/conventions/deprecated/MultiSpeakerBRIR_0.3.csv +48 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.csv +43 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.json +333 -0
- sofar/{conventions/source/SimpleFreeFieldHRIR_1.0.csv → sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.csv} +15 -18
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.json +340 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_1.0.csv +44 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_1.0.json +340 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.1.csv +51 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.1.json +396 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.csv +51 -0
- sofar/{conventions/SimpleHeadphoneIR_1.0.json → sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.json} +3 -3
- sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.2.csv +47 -0
- sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.2.json +360 -0
- sofar/sofa_conventions/rules/deprecations.json +12 -0
- sofar/sofa_conventions/rules/rules.json +800 -0
- sofar/sofa_conventions/rules/unit_aliases.json +11 -0
- sofar/sofa_conventions/rules/upgrade.json +190 -0
- sofar/update_conventions.py +427 -0
- sofar/utils.py +315 -0
- {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/AUTHORS.rst +1 -0
- sofar-1.1.0.dist-info/METADATA +89 -0
- sofar-1.1.0.dist-info/RECORD +75 -0
- {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/WHEEL +1 -1
- {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/test_deprecations.py +19 -0
- tests/test_io.py +344 -0
- tests/test_sofa.py +354 -0
- tests/test_sofa_upgrade_conventions.py +102 -0
- tests/test_sofa_verify.py +472 -0
- tests/test_utils.py +241 -0
- sofar/conventions/source/MultiSpeakerBRIR_0.3.csv +0 -48
- sofar/sofar.py +0 -2531
- sofar-0.3.1.dist-info/METADATA +0 -69
- sofar-0.3.1.dist-info/RECORD +0 -46
- /sofar/{conventions/source → sofa_conventions/conventions}/SimpleFreeFieldSOS_1.0.csv +0 -0
- /sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldSOS_1.0.json +0 -0
- /sofar/{conventions/source → sofa_conventions/conventions/deprecated}/GeneralFIRE_1.0.csv +0 -0
- /sofar/{conventions → sofa_conventions/conventions/deprecated}/GeneralFIRE_1.0.json +0 -0
- /sofar/{conventions → sofa_conventions/conventions/deprecated}/MultiSpeakerBRIR_0.3.json +0 -0
- /sofar/{conventions/source → sofa_conventions/conventions/deprecated}/SingleRoomDRIR_0.3.csv +0 -0
- /sofar/{conventions → sofa_conventions/conventions/deprecated}/SingleRoomDRIR_0.3.json +0 -0
- {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/LICENSE +0 -0
tests/test_utils.py
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
import shutil
|
2
|
+
import sofar as sf
|
3
|
+
from sofar.utils import _get_conventions
|
4
|
+
from sofar.update_conventions import _compile_conventions, _check_congruency
|
5
|
+
import os
|
6
|
+
import json
|
7
|
+
from tempfile import TemporaryDirectory
|
8
|
+
import pytest
|
9
|
+
from pytest import raises
|
10
|
+
import numpy as np
|
11
|
+
from copy import deepcopy
|
12
|
+
import warnings
|
13
|
+
|
14
|
+
|
15
|
+
def test_list_conventions(capfd):
|
16
|
+
|
17
|
+
# check output to console using pytest default fixture capfd
|
18
|
+
sf.list_conventions()
|
19
|
+
out, _ = capfd.readouterr()
|
20
|
+
assert "Available SOFA conventions:" in out
|
21
|
+
|
22
|
+
|
23
|
+
def test__get_conventions(capfd):
|
24
|
+
|
25
|
+
# check the return type
|
26
|
+
paths = _get_conventions(return_type="path")
|
27
|
+
assert isinstance(paths, list)
|
28
|
+
assert os.path.isfile(paths[0])
|
29
|
+
assert paths[0].endswith(".json")
|
30
|
+
out, _ = capfd.readouterr()
|
31
|
+
assert out == ""
|
32
|
+
|
33
|
+
paths = _get_conventions(return_type="path_source")
|
34
|
+
assert isinstance(paths, list)
|
35
|
+
assert os.path.isfile(paths[0])
|
36
|
+
assert paths[0].endswith(".csv")
|
37
|
+
out, _ = capfd.readouterr()
|
38
|
+
assert out == ""
|
39
|
+
|
40
|
+
names = _get_conventions(return_type="name")
|
41
|
+
assert isinstance(names, list)
|
42
|
+
assert not os.path.isfile(names[0])
|
43
|
+
|
44
|
+
names_versions = _get_conventions(return_type="name_version")
|
45
|
+
assert isinstance(names_versions, list)
|
46
|
+
assert isinstance(names_versions[0], tuple)
|
47
|
+
|
48
|
+
with raises(ValueError, match="return_type None is invalid"):
|
49
|
+
_get_conventions(return_type="None")
|
50
|
+
|
51
|
+
|
52
|
+
@pytest.mark.parametrize('branch', ['master', 'development'])
|
53
|
+
def test__congruency(capfd, branch):
|
54
|
+
"""
|
55
|
+
Check if conventions from SOFAToolbox and sofaconventions.org are
|
56
|
+
identical.
|
57
|
+
"""
|
58
|
+
out, _ = capfd.readouterr()
|
59
|
+
_check_congruency(branch=branch)
|
60
|
+
out, _ = capfd.readouterr()
|
61
|
+
if out != "":
|
62
|
+
warnings.warn(out, Warning)
|
63
|
+
|
64
|
+
|
65
|
+
def test_update_conventions(capfd):
|
66
|
+
|
67
|
+
# create temporary directory and copy existing conventions
|
68
|
+
temp_dir = TemporaryDirectory()
|
69
|
+
work_dir = os.path.join(temp_dir.name, "sofa_conventions", "conventions")
|
70
|
+
shutil.copytree(
|
71
|
+
os.path.join(
|
72
|
+
os.path.dirname(__file__), "..", "sofar", "sofa_conventions"),
|
73
|
+
os.path.join(temp_dir.name, "sofa_conventions"))
|
74
|
+
|
75
|
+
# delete standardized GeneralTF_2.0 to test adding
|
76
|
+
os.remove(os.path.join(work_dir, "GeneralTF_2.0.csv"))
|
77
|
+
os.remove(os.path.join(work_dir, "GeneralTF_2.0.json"))
|
78
|
+
# modify standardized GeneralFIR_1.0 to test updating
|
79
|
+
with open(os.path.join(work_dir, "GeneralFIR_1.0.csv"), "w") as fid:
|
80
|
+
fid.write("modified")
|
81
|
+
# move MultiSpeakerBRIR_0.3 to standardized to test deprecation
|
82
|
+
os.rename(
|
83
|
+
os.path.join(work_dir, "deprecated", "MultiSpeakerBRIR_0.3.csv"),
|
84
|
+
os.path.join(work_dir, "MultiSpeakerBRIR_0.3.csv"))
|
85
|
+
os.rename(
|
86
|
+
os.path.join(work_dir, "deprecated", "MultiSpeakerBRIR_0.3.json"),
|
87
|
+
os.path.join(work_dir, "MultiSpeakerBRIR_0.3.json"))
|
88
|
+
# modify deprecated convention to test updating
|
89
|
+
with open(os.path.join(work_dir, "deprecated",
|
90
|
+
"SimpleFreeFieldHRIR_0.4.csv"), "w") as fid:
|
91
|
+
fid.write("modified")
|
92
|
+
# delete deprecated GeneralTF_2.0 to test adding
|
93
|
+
os.remove(os.path.join(
|
94
|
+
work_dir, "deprecated", "SimpleFreeFieldTF_0.4.csv"))
|
95
|
+
os.remove(os.path.join(
|
96
|
+
work_dir, "deprecated", "SimpleFreeFieldTF_0.4.json"))
|
97
|
+
|
98
|
+
# first run to test if conventions were updated
|
99
|
+
sf.update_conventions(conventions_path=work_dir, assume_yes=True)
|
100
|
+
out, _ = capfd.readouterr()
|
101
|
+
assert "added convention: GeneralTF_2.0" in out
|
102
|
+
assert "updated convention: GeneralFIR_1.0" in out
|
103
|
+
assert "deprecated convention: MultiSpeakerBRIR_0.3" in out
|
104
|
+
assert not os.path.isfile(
|
105
|
+
os.path.join(work_dir, "MultiSpeakerBRIR_0.3.csv"))
|
106
|
+
assert not os.path.isfile(
|
107
|
+
os.path.join(work_dir, "MultiSpeakerBRIR_0.3.json"))
|
108
|
+
assert "updated deprecated convention: SimpleFreeFieldHRIR_0.4" in out
|
109
|
+
assert "added deprecated convention: SimpleFreeFieldTF_0.4" in out
|
110
|
+
|
111
|
+
# second run to make sure that up to date conventions are not overwritten
|
112
|
+
sf.update_conventions(conventions_path=work_dir, assume_yes=True)
|
113
|
+
out, _ = capfd.readouterr()
|
114
|
+
assert "added" not in out
|
115
|
+
assert "updated" not in out
|
116
|
+
assert "deprecated" not in out
|
117
|
+
|
118
|
+
|
119
|
+
def test__compile_conventions():
|
120
|
+
"""Test compiling the json conventions from the csv files."""
|
121
|
+
|
122
|
+
# create temporary directory and copy existing source conventions
|
123
|
+
temp_dir = TemporaryDirectory()
|
124
|
+
shutil.copytree(
|
125
|
+
os.path.join(os.path.dirname(__file__), "..", "sofar",
|
126
|
+
"sofa_conventions", "conventions"),
|
127
|
+
os.path.join(temp_dir.name, "conventions"))
|
128
|
+
|
129
|
+
# compile conventions
|
130
|
+
_compile_conventions(os.path.join(temp_dir.name, "conventions"))
|
131
|
+
|
132
|
+
# get list of reference json files
|
133
|
+
paths_ref = _get_conventions("path")
|
134
|
+
paths_test = _get_conventions(
|
135
|
+
"path", os.path.join(temp_dir.name, "conventions"))
|
136
|
+
|
137
|
+
for path_ref, path_test in zip(paths_ref, paths_test):
|
138
|
+
|
139
|
+
# load reference conventions
|
140
|
+
with open(path_ref, "r") as file:
|
141
|
+
ref_data = json.load(file)
|
142
|
+
|
143
|
+
with open(path_test, "r") as file:
|
144
|
+
test_data = json.load(file)
|
145
|
+
|
146
|
+
# compare conventions
|
147
|
+
assert ref_data == test_data
|
148
|
+
|
149
|
+
|
150
|
+
def test_equals_global_parameters():
|
151
|
+
|
152
|
+
sofa_a = sf.Sofa("SimpleFreeFieldHRIR")
|
153
|
+
|
154
|
+
# check invalid
|
155
|
+
with raises(ValueError, match="exclude is"):
|
156
|
+
sf.equals(sofa_a, sofa_a, exclude="wrong")
|
157
|
+
|
158
|
+
# check identical objects
|
159
|
+
assert sf.equals(sofa_a, sofa_a)
|
160
|
+
|
161
|
+
# check different number of keys
|
162
|
+
sofa_b = deepcopy(sofa_a)
|
163
|
+
sofa_b.protected = False
|
164
|
+
delattr(sofa_b, "ReceiverPosition")
|
165
|
+
sofa_b.protected = True
|
166
|
+
with pytest.warns(UserWarning, match="not identical: sofa_a has"):
|
167
|
+
is_identical = sf.equals(sofa_a, sofa_b)
|
168
|
+
assert not is_identical
|
169
|
+
|
170
|
+
# check different keys
|
171
|
+
sofa_b = deepcopy(sofa_a)
|
172
|
+
sofa_b.protected = False
|
173
|
+
sofa_b.PositionReceiver = sofa_b.ReceiverPosition
|
174
|
+
delattr(sofa_b, "ReceiverPosition")
|
175
|
+
sofa_b.protected = True
|
176
|
+
with pytest.warns(UserWarning, match="not identical: sofa_a and sofa_b"):
|
177
|
+
is_identical = sf.equals(sofa_a, sofa_b)
|
178
|
+
assert not is_identical
|
179
|
+
|
180
|
+
# check mismatching data types
|
181
|
+
sofa_b = deepcopy(sofa_a)
|
182
|
+
sofa_b.protected = False
|
183
|
+
sofa_b._convention["ReceiverPosition"]["type"] = "int"
|
184
|
+
sofa_b.protected = True
|
185
|
+
with pytest.warns(UserWarning, match="not identical: ReceiverPosition"):
|
186
|
+
is_identical = sf.equals(sofa_a, sofa_b)
|
187
|
+
assert not is_identical
|
188
|
+
|
189
|
+
# check exclude GLOBAL attributes
|
190
|
+
sofa_b = deepcopy(sofa_a)
|
191
|
+
sofa_b.protected = False
|
192
|
+
delattr(sofa_b, "GLOBAL_Version")
|
193
|
+
sofa_b.protected = True
|
194
|
+
is_identical = sf.equals(sofa_a, sofa_b, exclude="GLOBAL")
|
195
|
+
assert is_identical
|
196
|
+
|
197
|
+
# check exclude Date attributes
|
198
|
+
sofa_b = deepcopy(sofa_a)
|
199
|
+
sofa_b.protected = False
|
200
|
+
delattr(sofa_b, "GLOBAL_DateModified")
|
201
|
+
sofa_b.protected = True
|
202
|
+
is_identical = sf.equals(sofa_a, sofa_b, exclude="DATE")
|
203
|
+
assert is_identical
|
204
|
+
|
205
|
+
# check exclude Date attributes
|
206
|
+
sofa_b = deepcopy(sofa_a)
|
207
|
+
sofa_b.protected = False
|
208
|
+
delattr(sofa_b, "GLOBAL_DateModified")
|
209
|
+
sofa_b.protected = True
|
210
|
+
is_identical = sf.equals(sofa_a, sofa_b, exclude="ATTR")
|
211
|
+
assert is_identical
|
212
|
+
|
213
|
+
|
214
|
+
@pytest.mark.parametrize("value_a, value_b, attribute, fails", [
|
215
|
+
("1", "2", "GLOBAL_SOFAConventionsVersion", True),
|
216
|
+
([[1, 2]], [1, 2], "Data_IR", False),
|
217
|
+
([[1, 2]], [1, 3], "Data_IR", True),
|
218
|
+
("HD 650", ["HD 650"], "SourceModel", False),
|
219
|
+
("HD 650", np.array(["HD 650"], dtype="U"), "SourceModel", False),
|
220
|
+
("HD 650", np.array(["HD 650"], dtype="S"), "SourceModel", False),
|
221
|
+
("HD 650", "HD-650", "SourceModel", True)
|
222
|
+
])
|
223
|
+
def test_equals_attribute_values(value_a, value_b, attribute, fails):
|
224
|
+
|
225
|
+
# generate SOFA objects (SimpleHeadphoneIR has string variables)
|
226
|
+
sofa_a = sf.Sofa("SimpleHeadphoneIR")
|
227
|
+
sofa_a.protected = False
|
228
|
+
sofa_b = deepcopy(sofa_a)
|
229
|
+
|
230
|
+
# set parameters
|
231
|
+
setattr(sofa_a, attribute, value_a)
|
232
|
+
sofa_a.protected = True
|
233
|
+
setattr(sofa_b, attribute, value_b)
|
234
|
+
sofa_b.protected = True
|
235
|
+
|
236
|
+
# compare
|
237
|
+
if fails:
|
238
|
+
with pytest.warns(UserWarning):
|
239
|
+
assert not sf.equals(sofa_a, sofa_b)
|
240
|
+
else:
|
241
|
+
assert sf.equals(sofa_a, sofa_b)
|
@@ -1,48 +0,0 @@
|
|
1
|
-
Name Default Flags Dimensions Type Comment
|
2
|
-
GLOBAL:Conventions SOFA rm attribute
|
3
|
-
GLOBAL:Version 1.0 rm attribute
|
4
|
-
GLOBAL:SOFAConventions MultiSpeakerBRIR rm attribute This convention is for BRIRs recorded in reverberant conditions from multiple loudspeaker sources at a number of listener orientations.
|
5
|
-
GLOBAL:SOFAConventionsVersion 0.3 rm attribute
|
6
|
-
GLOBAL:APIName rm attribute
|
7
|
-
GLOBAL:APIVersion rm attribute
|
8
|
-
GLOBAL:ApplicationName attribute
|
9
|
-
GLOBAL:ApplicationVersion attribute
|
10
|
-
GLOBAL:AuthorContact m attribute
|
11
|
-
GLOBAL:Comment m attribute
|
12
|
-
GLOBAL:DataType FIRE rm attribute We use FIR datatype which in addition depends on Emitters (E)
|
13
|
-
GLOBAL:History attribute
|
14
|
-
GLOBAL:License No license provided, ask the author for permission m attribute
|
15
|
-
GLOBAL:Organization m attribute
|
16
|
-
GLOBAL:References attribute
|
17
|
-
GLOBAL:RoomType reverberant m attribute
|
18
|
-
GLOBAL:Origin attribute
|
19
|
-
GLOBAL:DateCreated m attribute
|
20
|
-
GLOBAL:DateModified m attribute
|
21
|
-
GLOBAL:Title m attribute
|
22
|
-
ListenerPosition [0 0 0] m IC, MC double
|
23
|
-
ListenerPosition:Type cartesian m attribute
|
24
|
-
ListenerPosition:Units metre m attribute
|
25
|
-
ReceiverPosition [0 0.09 0; 0 -0.09 0] m rCI, rCM double
|
26
|
-
ReceiverPosition:Type cartesian m attribute
|
27
|
-
ReceiverPosition:Units metre m attribute
|
28
|
-
SourcePosition [0 0 1] m IC, MC double
|
29
|
-
SourcePosition:Type spherical m attribute
|
30
|
-
SourcePosition:Units degree, degree, metre m attribute
|
31
|
-
EmitterPosition [0 0 0] m eCI, eCM double Each speaker is represented as an emitter. Use EmitterPosition to represent the position of a particular speaker. Size of EmitterPosition determines E
|
32
|
-
EmitterPosition:Type cartesian m attribute
|
33
|
-
EmitterPosition:Units metre m attribute
|
34
|
-
GLOBAL:DatabaseName m attribute name of the database to which these data belong
|
35
|
-
GLOBAL:ListenerShortName m attribute ID of the subject from the database
|
36
|
-
GLOBAL:RoomDescription attribute narrative description of the room
|
37
|
-
ListenerUp [0 0 1] m IC, MC double
|
38
|
-
ListenerView [1 0 0] m IC, MC double
|
39
|
-
ListenerView:Type cartesian m attribute
|
40
|
-
ListenerView:Units metre m attribute
|
41
|
-
EmitterUp [0 0 1] ECI, ECM double When EmitterUp provided, EmitterView must be provided as well
|
42
|
-
EmitterView [1 0 0] ECI, ECM double When EmitterView provided, EmitterUp must be provided as well
|
43
|
-
EmitterView:Type cartesian attribute
|
44
|
-
EmitterView:Units metre attribute
|
45
|
-
Data.IR [1 1] m mREn double
|
46
|
-
Data.SamplingRate 48000 m I double
|
47
|
-
Data.SamplingRate:Units hertz m attribute
|
48
|
-
Data.Delay [0 0] m IRE, MRE double
|