sofar 1.2.1__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/Makefile +20 -0
- docs/api_reference.rst +20 -0
- docs/conf.py +167 -0
- docs/contributing.rst +1 -0
- docs/history.rst +1 -0
- docs/index.rst +4 -0
- docs/make.bat +36 -0
- docs/readme.rst +1 -0
- docs/resources/conventions.py +162 -0
- docs/resources/working_with_sofa_HRIR_lateral.png +0 -0
- docs/resources/working_with_sofa_source_horizontal.png +0 -0
- docs/resources/working_with_sofa_source_lateral.png +0 -0
- docs/sofar.rst +82 -0
- sofar/__init__.py +28 -0
- sofar/io.py +427 -0
- sofar/sofa.py +1835 -0
- sofar/sofa_conventions/VERSION +1 -0
- sofar/sofa_conventions/conventions/AnnotatedEmitterAudio_0.2.csv +46 -0
- sofar/sofa_conventions/conventions/AnnotatedEmitterAudio_0.2.json +353 -0
- sofar/sofa_conventions/conventions/AnnotatedReceiverAudio_0.2.csv +46 -0
- sofar/sofa_conventions/conventions/AnnotatedReceiverAudio_0.2.json +353 -0
- sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.csv +59 -0
- sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.json +444 -0
- sofar/sofa_conventions/conventions/FreeFieldHRIR_1.0.csv +43 -0
- sofar/sofa_conventions/conventions/FreeFieldHRIR_1.0.json +333 -0
- sofar/sofa_conventions/conventions/FreeFieldHRTF_1.0.csv +44 -0
- sofar/sofa_conventions/conventions/FreeFieldHRTF_1.0.json +340 -0
- sofar/sofa_conventions/conventions/GeneralFIR-E_2.0.csv +37 -0
- sofar/sofa_conventions/conventions/GeneralFIR-E_2.0.json +270 -0
- sofar/sofa_conventions/conventions/GeneralFIR_1.0.csv +40 -0
- sofar/sofa_conventions/conventions/GeneralFIR_1.0.json +295 -0
- sofar/sofa_conventions/conventions/GeneralSOS_1.0.csv +40 -0
- sofar/sofa_conventions/conventions/GeneralSOS_1.0.json +306 -0
- sofar/sofa_conventions/conventions/GeneralTF-E_1.0.csv +38 -0
- sofar/sofa_conventions/conventions/GeneralTF-E_1.0.json +277 -0
- sofar/sofa_conventions/conventions/GeneralTF_1.0.csv +38 -0
- sofar/sofa_conventions/conventions/GeneralTF_1.0.json +277 -0
- sofar/sofa_conventions/conventions/GeneralTF_2.0.csv +38 -0
- sofar/sofa_conventions/conventions/GeneralTF_2.0.json +277 -0
- sofar/sofa_conventions/conventions/SimpleFreeFieldHRIR_1.0.csv +47 -0
- sofar/sofa_conventions/conventions/SimpleFreeFieldHRIR_1.0.json +369 -0
- sofar/sofa_conventions/conventions/SimpleFreeFieldHRSOS_1.0.csv +43 -0
- sofar/sofa_conventions/conventions/SimpleFreeFieldHRSOS_1.0.json +349 -0
- sofar/sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.csv +44 -0
- sofar/sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.json +340 -0
- sofar/sofa_conventions/conventions/SimpleFreeFieldSOS_1.0.csv +43 -0
- sofar/sofa_conventions/conventions/SimpleFreeFieldSOS_1.0.json +349 -0
- sofar/sofa_conventions/conventions/SimpleHeadphoneIR_1.0.csv +51 -0
- sofar/sofa_conventions/conventions/SimpleHeadphoneIR_1.0.json +396 -0
- sofar/sofa_conventions/conventions/SingleRoomMIMOSRIR_1.0.csv +78 -0
- sofar/sofa_conventions/conventions/SingleRoomMIMOSRIR_1.0.json +601 -0
- sofar/sofa_conventions/conventions/SingleRoomSRIR_1.0.csv +78 -0
- sofar/sofa_conventions/conventions/SingleRoomSRIR_1.0.json +601 -0
- sofar/sofa_conventions/conventions/deprecated/AnnotatedEmitterAudio_0.1.csv +46 -0
- sofar/sofa_conventions/conventions/deprecated/AnnotatedEmitterAudio_0.1.json +351 -0
- sofar/sofa_conventions/conventions/deprecated/AnnotatedReceiverAudio_0.1.csv +46 -0
- sofar/sofa_conventions/conventions/deprecated/AnnotatedReceiverAudio_0.1.json +351 -0
- sofar/sofa_conventions/conventions/deprecated/FreeFieldDirectivityTF_1.0.csv +58 -0
- sofar/sofa_conventions/conventions/deprecated/FreeFieldDirectivityTF_1.0.json +437 -0
- sofar/sofa_conventions/conventions/deprecated/GeneralFIRE_1.0.csv +37 -0
- sofar/sofa_conventions/conventions/deprecated/GeneralFIRE_1.0.json +270 -0
- sofar/sofa_conventions/conventions/deprecated/MultiSpeakerBRIR_0.3.csv +48 -0
- sofar/sofa_conventions/conventions/deprecated/MultiSpeakerBRIR_0.3.json +376 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.csv +43 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.json +333 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.csv +44 -0
- 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/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.json +396 -0
- 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/conventions/deprecated/SingleRoomDRIR_0.3.csv +47 -0
- sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.3.json +360 -0
- sofar/sofa_conventions/conventions/deprecated/SingleTrackedAudio_0.1.csv +47 -0
- sofar/sofa_conventions/conventions/deprecated/SingleTrackedAudio_0.1.json +366 -0
- sofar/sofa_conventions/conventions/deprecated/SingleTrackedAudio_0.2.csv +51 -0
- sofar/sofa_conventions/conventions/deprecated/SingleTrackedAudio_0.2.json +397 -0
- sofar/sofa_conventions/rules/deprecations.json +13 -0
- sofar/sofa_conventions/rules/rules.json +819 -0
- sofar/sofa_conventions/rules/unit_aliases.json +11 -0
- sofar/sofa_conventions/rules/upgrade.json +226 -0
- sofar/sofa_conventions/write_upgrade_rules.py +139 -0
- sofar/sofa_conventions/write_verification_data.py +313 -0
- sofar/sofa_conventions/write_verification_rules.py +356 -0
- sofar/sofastream.py +301 -0
- sofar/update_conventions.py +449 -0
- sofar/utils.py +316 -0
- sofar-1.2.1.dist-info/LICENSE +22 -0
- sofar-1.2.1.dist-info/METADATA +136 -0
- sofar-1.2.1.dist-info/RECORD +105 -0
- sofar-1.2.1.dist-info/WHEEL +5 -0
- sofar-1.2.1.dist-info/top_level.txt +3 -0
- tests/__init__.py +0 -0
- tests/conftest.py +27 -0
- tests/test_deprecations.py +19 -0
- tests/test_io.py +349 -0
- tests/test_sofa.py +353 -0
- tests/test_sofa_upgrade_conventions.py +111 -0
- tests/test_sofa_verify.py +480 -0
- tests/test_sofastream.py +127 -0
- tests/test_utils.py +250 -0
tests/test_sofastream.py
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
from sofar import SofaStream
|
2
|
+
from tempfile import TemporaryDirectory
|
3
|
+
import pytest
|
4
|
+
import netCDF4
|
5
|
+
import numpy as np
|
6
|
+
import os
|
7
|
+
import sofar as sf
|
8
|
+
|
9
|
+
|
10
|
+
def test_sofastream_output(temp_sofa_file):
|
11
|
+
|
12
|
+
with SofaStream(temp_sofa_file) as file:
|
13
|
+
obj = file
|
14
|
+
var = file.Data_IR
|
15
|
+
var_data = var[:]
|
16
|
+
var_attr = file.Data_SamplingRate_Units
|
17
|
+
att_data = file.GLOBAL_RoomType
|
18
|
+
|
19
|
+
# check SofaStream instance
|
20
|
+
isinstance(obj, SofaStream)
|
21
|
+
# check returned variable type
|
22
|
+
isinstance(var, netCDF4._netCDF4.Variable)
|
23
|
+
# variable values
|
24
|
+
isinstance(var_data, np.ma.core.MaskedArray)
|
25
|
+
np.testing.assert_array_equal(var_data.squeeze(),
|
26
|
+
np.array([[0, 1], [2, 3], [4, 5]]))
|
27
|
+
assert var_attr == 'hertz'
|
28
|
+
# attribute values
|
29
|
+
isinstance(att_data, str)
|
30
|
+
assert att_data == "free field"
|
31
|
+
|
32
|
+
|
33
|
+
def test_sofastream_attribute_error(temp_sofa_file):
|
34
|
+
|
35
|
+
with SofaStream(temp_sofa_file) as file:
|
36
|
+
with pytest.raises(
|
37
|
+
AttributeError,
|
38
|
+
match="Wrong_Attribute is not contained in SOFA-file"):
|
39
|
+
_ = file.Wrong_Attribute
|
40
|
+
|
41
|
+
|
42
|
+
def test_sofastream_inspect(capfd, temp_sofa_file):
|
43
|
+
|
44
|
+
tempdir = TemporaryDirectory()
|
45
|
+
inspect_file = os.path.join(tempdir.name, "info.txt")
|
46
|
+
|
47
|
+
with SofaStream(temp_sofa_file) as file:
|
48
|
+
file.inspect(inspect_file)
|
49
|
+
out, _ = capfd.readouterr()
|
50
|
+
|
51
|
+
sofa = sf.read_sofa(temp_sofa_file)
|
52
|
+
sofa.inspect()
|
53
|
+
out_sofa, _ = capfd.readouterr()
|
54
|
+
|
55
|
+
assert out_sofa == out
|
56
|
+
|
57
|
+
# check text file
|
58
|
+
with open(inspect_file, "r") as out_inspect:
|
59
|
+
text = out_inspect.readlines()
|
60
|
+
assert out == "".join(text)
|
61
|
+
|
62
|
+
|
63
|
+
def test_list_dimensions(capfd, tmp_path_factory):
|
64
|
+
|
65
|
+
filename = tmp_path_factory.mktemp("data") / "test_sofastream_dim.sofa"
|
66
|
+
|
67
|
+
# test FIR Data
|
68
|
+
sofa = sf.Sofa("GeneralFIR")
|
69
|
+
sf.write_sofa(filename, sofa)
|
70
|
+
with SofaStream(filename) as file:
|
71
|
+
_ = file.list_dimensions
|
72
|
+
out, _ = capfd.readouterr()
|
73
|
+
assert "N = 1 samples" in out
|
74
|
+
|
75
|
+
# test TF Data
|
76
|
+
sofa = sf.Sofa("GeneralTF")
|
77
|
+
sf.write_sofa(filename, sofa)
|
78
|
+
with SofaStream(filename) as file:
|
79
|
+
_ = file.list_dimensions
|
80
|
+
out, _ = capfd.readouterr()
|
81
|
+
assert "N = 1 frequencies" in out
|
82
|
+
|
83
|
+
# test SOS Data
|
84
|
+
sofa = sf.Sofa("SimpleFreeFieldHRSOS")
|
85
|
+
sf.write_sofa(filename, sofa)
|
86
|
+
with SofaStream(filename) as file:
|
87
|
+
_ = file.list_dimensions
|
88
|
+
out, _ = capfd.readouterr()
|
89
|
+
assert "N = 6 SOS coefficients" in out
|
90
|
+
|
91
|
+
# test non spherical harmonics data
|
92
|
+
sofa = sf.Sofa("GeneralFIR")
|
93
|
+
sf.write_sofa(filename, sofa)
|
94
|
+
with SofaStream(filename) as file:
|
95
|
+
_ = file.list_dimensions
|
96
|
+
out, _ = capfd.readouterr()
|
97
|
+
assert "E = 1 emitter" in out
|
98
|
+
assert "R = 1 receiver" in out
|
99
|
+
|
100
|
+
# test spherical harmonics data
|
101
|
+
sofa.EmitterPosition_Type = "spherical harmonics"
|
102
|
+
sofa.ReceiverPosition_Type = "spherical harmonics"
|
103
|
+
sofa.EmitterPosition_Units = "degree, degree, metre"
|
104
|
+
sofa.ReceiverPosition_Units = "degree, degree, metre"
|
105
|
+
sf.write_sofa(filename, sofa)
|
106
|
+
with SofaStream(filename) as file:
|
107
|
+
_ = file.list_dimensions
|
108
|
+
out, _ = capfd.readouterr()
|
109
|
+
assert "E = 1 emitter spherical harmonics coefficients" in out
|
110
|
+
assert "R = 1 receiver spherical harmonics coefficients" in out
|
111
|
+
|
112
|
+
|
113
|
+
def test_get_dimensions(tmp_path_factory):
|
114
|
+
"""Test getting the size of dimensions."""
|
115
|
+
filename = tmp_path_factory.mktemp("data") / "test_sofastream_dim.sofa"
|
116
|
+
|
117
|
+
# test FIR Data
|
118
|
+
sofa = sf.Sofa("GeneralFIR")
|
119
|
+
sf.write_sofa(filename, sofa)
|
120
|
+
|
121
|
+
with SofaStream(filename) as file:
|
122
|
+
size = file.get_dimension('N')
|
123
|
+
assert size == 1
|
124
|
+
|
125
|
+
# test wrong dimension error
|
126
|
+
with pytest.raises(ValueError, match="Q is not a valid dimension"):
|
127
|
+
file.get_dimension("Q")
|
tests/test_utils.py
ADDED
@@ -0,0 +1,250 @@
|
|
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
|
+
import numpy as np
|
10
|
+
from copy import deepcopy
|
11
|
+
import warnings
|
12
|
+
|
13
|
+
|
14
|
+
def test_list_conventions(capfd):
|
15
|
+
|
16
|
+
# check output to console using pytest default fixture capfd
|
17
|
+
sf.list_conventions()
|
18
|
+
out, _ = capfd.readouterr()
|
19
|
+
assert "Available SOFA conventions:" in out
|
20
|
+
|
21
|
+
|
22
|
+
def test__get_conventions(capfd):
|
23
|
+
|
24
|
+
# check the return type
|
25
|
+
paths = _get_conventions(return_type="path")
|
26
|
+
assert isinstance(paths, list)
|
27
|
+
assert os.path.isfile(paths[0])
|
28
|
+
assert paths[0].endswith(".json")
|
29
|
+
out, _ = capfd.readouterr()
|
30
|
+
assert out == ""
|
31
|
+
|
32
|
+
paths = _get_conventions(return_type="path_source")
|
33
|
+
assert isinstance(paths, list)
|
34
|
+
assert os.path.isfile(paths[0])
|
35
|
+
assert paths[0].endswith(".csv")
|
36
|
+
out, _ = capfd.readouterr()
|
37
|
+
assert out == ""
|
38
|
+
|
39
|
+
names = _get_conventions(return_type="name")
|
40
|
+
assert isinstance(names, list)
|
41
|
+
assert not os.path.isfile(names[0])
|
42
|
+
|
43
|
+
names_versions = _get_conventions(return_type="name_version")
|
44
|
+
assert isinstance(names_versions, list)
|
45
|
+
assert isinstance(names_versions[0], tuple)
|
46
|
+
|
47
|
+
with pytest.raises(ValueError, match="return_type None is invalid"):
|
48
|
+
_get_conventions(return_type="None")
|
49
|
+
|
50
|
+
|
51
|
+
@pytest.mark.parametrize('branch', ['master', 'development'])
|
52
|
+
def test__congruency(capfd, branch):
|
53
|
+
"""
|
54
|
+
Check if conventions from SOFAToolbox and sofaconventions.org are
|
55
|
+
identical.
|
56
|
+
"""
|
57
|
+
out, _ = capfd.readouterr()
|
58
|
+
_check_congruency(branch=branch)
|
59
|
+
out, _ = capfd.readouterr()
|
60
|
+
if out != "":
|
61
|
+
warnings.warn(out, Warning, stacklevel=1)
|
62
|
+
|
63
|
+
|
64
|
+
def test_update_conventions(capfd):
|
65
|
+
|
66
|
+
# create temporary directory and copy existing conventions
|
67
|
+
temp_dir = TemporaryDirectory()
|
68
|
+
work_dir = os.path.join(temp_dir.name, "sofa_conventions", "conventions")
|
69
|
+
shutil.copytree(
|
70
|
+
os.path.join(
|
71
|
+
os.path.dirname(__file__), "..", "sofar", "sofa_conventions"),
|
72
|
+
os.path.join(temp_dir.name, "sofa_conventions"))
|
73
|
+
|
74
|
+
# delete standardized GeneralTF_2.0 to test adding
|
75
|
+
os.remove(os.path.join(work_dir, "GeneralTF_2.0.csv"))
|
76
|
+
os.remove(os.path.join(work_dir, "GeneralTF_2.0.json"))
|
77
|
+
# modify standardized GeneralFIR_1.0 to test updating
|
78
|
+
with open(os.path.join(work_dir, "GeneralFIR_1.0.csv"), "w") as fid:
|
79
|
+
fid.write("modified")
|
80
|
+
# move MultiSpeakerBRIR_0.3 to standardized to test deprecation
|
81
|
+
os.rename(
|
82
|
+
os.path.join(work_dir, "deprecated", "MultiSpeakerBRIR_0.3.csv"),
|
83
|
+
os.path.join(work_dir, "MultiSpeakerBRIR_0.3.csv"))
|
84
|
+
os.rename(
|
85
|
+
os.path.join(work_dir, "deprecated", "MultiSpeakerBRIR_0.3.json"),
|
86
|
+
os.path.join(work_dir, "MultiSpeakerBRIR_0.3.json"))
|
87
|
+
# modify deprecated convention to test updating
|
88
|
+
with open(os.path.join(work_dir, "deprecated",
|
89
|
+
"SimpleFreeFieldHRIR_0.4.csv"), "w") as fid:
|
90
|
+
fid.write("modified")
|
91
|
+
# delete deprecated GeneralTF_2.0 to test adding
|
92
|
+
os.remove(os.path.join(
|
93
|
+
work_dir, "deprecated", "SimpleFreeFieldTF_0.4.csv"))
|
94
|
+
os.remove(os.path.join(
|
95
|
+
work_dir, "deprecated", "SimpleFreeFieldTF_0.4.json"))
|
96
|
+
|
97
|
+
# first run to test if conventions were updated
|
98
|
+
sf.update_conventions(conventions_path=work_dir, assume_yes=True)
|
99
|
+
out, _ = capfd.readouterr()
|
100
|
+
assert "add convention: GeneralTF_2.0" in out
|
101
|
+
assert os.path.isfile(os.path.join(work_dir, "GeneralTF_2.0.csv"))
|
102
|
+
assert "update convention: GeneralFIR_1.0" in out
|
103
|
+
assert "deprecate 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 os.path.isfile(
|
109
|
+
os.path.join(work_dir, "deprecated", "MultiSpeakerBRIR_0.3.csv"))
|
110
|
+
assert os.path.isfile(
|
111
|
+
os.path.join(work_dir, "deprecated", "MultiSpeakerBRIR_0.3.json"))
|
112
|
+
assert "update deprecated convention: SimpleFreeFieldHRIR_0.4" in out
|
113
|
+
assert "add deprecated convention: SimpleFreeFieldTF_0.4" in out
|
114
|
+
assert os.path.isfile(
|
115
|
+
os.path.join(work_dir, "deprecated", "SimpleFreeFieldTF_0.4.csv"))
|
116
|
+
assert os.path.isfile(
|
117
|
+
os.path.join(work_dir, "deprecated", "SimpleFreeFieldTF_0.4.json"))
|
118
|
+
|
119
|
+
# second run to make sure that up to date conventions are not overwritten
|
120
|
+
sf.update_conventions(conventions_path=work_dir, assume_yes=True)
|
121
|
+
out, _ = capfd.readouterr()
|
122
|
+
assert "add" not in out
|
123
|
+
assert "update" not in out
|
124
|
+
assert "deprecate" not in out
|
125
|
+
assert "already up to date" in out
|
126
|
+
|
127
|
+
|
128
|
+
def test__compile_conventions():
|
129
|
+
"""Test compiling the json conventions from the csv files."""
|
130
|
+
|
131
|
+
# create temporary directory and copy existing source conventions
|
132
|
+
temp_dir = TemporaryDirectory()
|
133
|
+
shutil.copytree(
|
134
|
+
os.path.join(os.path.dirname(__file__), "..", "sofar",
|
135
|
+
"sofa_conventions", "conventions"),
|
136
|
+
os.path.join(temp_dir.name, "conventions"))
|
137
|
+
|
138
|
+
# compile conventions
|
139
|
+
_compile_conventions(os.path.join(temp_dir.name, "conventions"))
|
140
|
+
|
141
|
+
# get list of reference json files
|
142
|
+
paths_ref = _get_conventions("path")
|
143
|
+
paths_test = _get_conventions(
|
144
|
+
"path", os.path.join(temp_dir.name, "conventions"))
|
145
|
+
|
146
|
+
for path_ref, path_test in zip(paths_ref, paths_test):
|
147
|
+
|
148
|
+
# load reference conventions
|
149
|
+
with open(path_ref, "r") as file:
|
150
|
+
ref_data = json.load(file)
|
151
|
+
|
152
|
+
with open(path_test, "r") as file:
|
153
|
+
test_data = json.load(file)
|
154
|
+
|
155
|
+
# compare conventions
|
156
|
+
assert ref_data == test_data
|
157
|
+
|
158
|
+
|
159
|
+
def test_equals_global_parameters():
|
160
|
+
|
161
|
+
sofa_a = sf.Sofa("SimpleFreeFieldHRIR")
|
162
|
+
|
163
|
+
# check invalid
|
164
|
+
with pytest.raises(ValueError, match="exclude is"):
|
165
|
+
sf.equals(sofa_a, sofa_a, exclude="wrong")
|
166
|
+
|
167
|
+
# check identical objects
|
168
|
+
assert sf.equals(sofa_a, sofa_a)
|
169
|
+
|
170
|
+
# check different number of keys
|
171
|
+
sofa_b = deepcopy(sofa_a)
|
172
|
+
sofa_b.protected = False
|
173
|
+
delattr(sofa_b, "ReceiverPosition")
|
174
|
+
sofa_b.protected = True
|
175
|
+
with pytest.warns(UserWarning, match="not identical: sofa_a has"):
|
176
|
+
is_identical = sf.equals(sofa_a, sofa_b)
|
177
|
+
assert not is_identical
|
178
|
+
|
179
|
+
# check different keys
|
180
|
+
sofa_b = deepcopy(sofa_a)
|
181
|
+
sofa_b.protected = False
|
182
|
+
sofa_b.PositionReceiver = sofa_b.ReceiverPosition
|
183
|
+
delattr(sofa_b, "ReceiverPosition")
|
184
|
+
sofa_b.protected = True
|
185
|
+
with pytest.warns(UserWarning, match="not identical: sofa_a and sofa_b"):
|
186
|
+
is_identical = sf.equals(sofa_a, sofa_b)
|
187
|
+
assert not is_identical
|
188
|
+
|
189
|
+
# check mismatching data types
|
190
|
+
sofa_b = deepcopy(sofa_a)
|
191
|
+
sofa_b.protected = False
|
192
|
+
sofa_b._convention["ReceiverPosition"]["type"] = "int"
|
193
|
+
sofa_b.protected = True
|
194
|
+
with pytest.warns(UserWarning, match="not identical: ReceiverPosition"):
|
195
|
+
is_identical = sf.equals(sofa_a, sofa_b)
|
196
|
+
assert not is_identical
|
197
|
+
|
198
|
+
# check exclude GLOBAL attributes
|
199
|
+
sofa_b = deepcopy(sofa_a)
|
200
|
+
sofa_b.protected = False
|
201
|
+
delattr(sofa_b, "GLOBAL_Version")
|
202
|
+
sofa_b.protected = True
|
203
|
+
is_identical = sf.equals(sofa_a, sofa_b, exclude="GLOBAL")
|
204
|
+
assert is_identical
|
205
|
+
|
206
|
+
# check exclude Date attributes
|
207
|
+
sofa_b = deepcopy(sofa_a)
|
208
|
+
sofa_b.protected = False
|
209
|
+
delattr(sofa_b, "GLOBAL_DateModified")
|
210
|
+
sofa_b.protected = True
|
211
|
+
is_identical = sf.equals(sofa_a, sofa_b, exclude="DATE")
|
212
|
+
assert is_identical
|
213
|
+
|
214
|
+
# check exclude Date attributes
|
215
|
+
sofa_b = deepcopy(sofa_a)
|
216
|
+
sofa_b.protected = False
|
217
|
+
delattr(sofa_b, "GLOBAL_DateModified")
|
218
|
+
sofa_b.protected = True
|
219
|
+
is_identical = sf.equals(sofa_a, sofa_b, exclude="ATTR")
|
220
|
+
assert is_identical
|
221
|
+
|
222
|
+
|
223
|
+
@pytest.mark.parametrize(("value_a", "value_b", "attribute", "fails"), [
|
224
|
+
("1", "2", "GLOBAL_SOFAConventionsVersion", True),
|
225
|
+
([[1, 2]], [1, 2], "Data_IR", False),
|
226
|
+
([[1, 2]], [1, 3], "Data_IR", True),
|
227
|
+
("HD 650", ["HD 650"], "SourceModel", False),
|
228
|
+
("HD 650", np.array(["HD 650"], dtype="U"), "SourceModel", False),
|
229
|
+
("HD 650", np.array(["HD 650"], dtype="S"), "SourceModel", False),
|
230
|
+
("HD 650", "HD-650", "SourceModel", True),
|
231
|
+
])
|
232
|
+
def test_equals_attribute_values(value_a, value_b, attribute, fails):
|
233
|
+
|
234
|
+
# generate SOFA objects (SimpleHeadphoneIR has string variables)
|
235
|
+
sofa_a = sf.Sofa("SimpleHeadphoneIR")
|
236
|
+
sofa_a.protected = False
|
237
|
+
sofa_b = deepcopy(sofa_a)
|
238
|
+
|
239
|
+
# set parameters
|
240
|
+
setattr(sofa_a, attribute, value_a)
|
241
|
+
sofa_a.protected = True
|
242
|
+
setattr(sofa_b, attribute, value_b)
|
243
|
+
sofa_b.protected = True
|
244
|
+
|
245
|
+
# compare
|
246
|
+
if fails:
|
247
|
+
with pytest.warns(UserWarning):
|
248
|
+
assert not sf.equals(sofa_a, sofa_b)
|
249
|
+
else:
|
250
|
+
assert sf.equals(sofa_a, sofa_b)
|