Skip to content

Commit 258a3cb

Browse files
committed
Merge branch 'master' of github.com:LCAV/pyroomacoustics
2 parents db64876 + 58368d9 commit 258a3cb

File tree

3 files changed

+69
-114
lines changed

3 files changed

+69
-114
lines changed

CHANGELOG.rst

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Bugfix
3535
@hrosseel
3636
- Fixes a bug when setting the air absorption coefficients to custom values (#191),
3737
adds a test, and more details in the doc
38+
- Fixes a bug in the utilities.angle_function in the calculation of the colatitude (#329)
39+
by @fabiodimarco
3840

3941

4042
`0.7.3`_ - 2022-12-05

pyroomacoustics/tests/test_angle_function.py

+48-91
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,28 @@
1010
a1 = np.array([[0, 0, 1], [0, 0, 1], [0, 1, 1]])
1111
a2 = np.array([[0], [0], [1]])
1212
a3 = np.array([0, 0, 1]).T
13+
a4 = np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]])
1314

1415
b1 = np.array([[0], [0], [0]])
1516
b2 = np.array([1, -1, 1]).T
17+
b3 = np.array([1, 0, 0]).T
1618

1719

18-
a1_b1 = np.array([[0, 0, pi / 4], [0, 0, pi / 4]])
19-
a1_b2 = np.array([[3 * pi / 4, 3 * pi / 4, pi / 2], [3 * pi / 4, pi / 2, pi / 2]])
20+
a1_b1 = np.array([[0, 0, pi / 4], [0, 0, np.arctan(np.sqrt(2.0))]])
21+
a1_b2 = np.array(
22+
[
23+
[3 * pi / 4, 3 * pi / 4, pi / 2],
24+
[pi / 2 + np.arctan(1.0 / np.sqrt(2)), pi / 2, pi / 2],
25+
]
26+
)
2027
a2_b1 = np.array([[0], [0]])
2128
a2_b2 = np.array([[3 * pi / 4], [pi / 2]])
2229
a3_b1 = np.array([[0], [0]])
2330
a3_b2 = np.array([[3 * pi / 4], [pi / 2]])
31+
a4_b1 = np.array([[0, pi / 2, 0], [0, pi / 2, pi / 2]])
32+
a4_b3 = np.array([[pi, 3 * pi / 4, 0], [pi / 4, pi / 2, 0]])
33+
a2_b3 = np.array([[pi], [pi / 4]])
34+
a3_b3 = np.array([[pi], [pi / 4]])
2435

2536

2637
# for 2-D coordinates
@@ -40,92 +51,38 @@
4051
c3_d2 = np.array([[0], [pi / 2]])
4152

4253

43-
class TestAngleFunction(TestCase):
44-
def test_set_3d(self):
45-
self.assertTrue(angle_function(a1, b1).all() == a1_b1.all())
46-
self.assertTrue(angle_function(a1, b2).all() == a1_b2.all())
47-
48-
def test_point_3d_1(self):
49-
self.assertTrue(angle_function(a2, b1).all() == a2_b1.all())
50-
self.assertTrue(angle_function(a2, b2).all() == a2_b2.all())
51-
52-
def test_point_3d_2(self):
53-
self.assertTrue(angle_function(a3, b1).all() == a3_b1.all())
54-
self.assertTrue(angle_function(a3, b2).all() == a3_b2.all())
55-
56-
def test_set_2d(self):
57-
self.assertTrue(angle_function(c1, d1).all() == c1_d1.all())
58-
self.assertTrue(angle_function(c1, d2).all() == c1_d2.all())
59-
60-
def test_point_2d_1(self):
61-
self.assertTrue(angle_function(c2, d1).all() == c2_d1.all())
62-
self.assertTrue(angle_function(c2, d2).all() == c2_d2.all())
63-
64-
def test_point_2d_2(self):
65-
self.assertTrue(angle_function(c3, d1).all() == c3_d1.all())
66-
self.assertTrue(angle_function(c3, d2).all() == c3_d2.all())
67-
68-
69-
def find_error(type_coordinates):
70-
if type_coordinates == "3-D":
71-
print("-" * 40)
72-
print("type_coordinates = 3-D")
73-
print("-" * 40)
74-
75-
a_range = [a1, a2, a3]
76-
b_range = [b1, b2]
77-
a_b_range = [a1_b1, a1_b2, a2_b1, a2_b2, a3_b1, a3_b2]
78-
79-
a_b_index = 0
80-
for a in a_range:
81-
for b in b_range:
82-
error_azimuth = (angle_function(a, b) - a_b_range[a_b_index])[0]
83-
error_colatitude = (angle_function(a, b) - a_b_range[a_b_index])[1]
84-
85-
print("for points :")
86-
print(a)
87-
print(b)
88-
print(
89-
"error in azimuth calculation: {}".format(np.average(error_azimuth))
90-
)
91-
print(
92-
"error in colatitude calculation: {}".format(
93-
np.average(error_colatitude)
94-
)
95-
)
96-
print()
97-
a_b_index += 1
98-
99-
elif type_coordinates == "2-D":
100-
print("-" * 40)
101-
print("type_coordinates = 2-D")
102-
print("-" * 40)
103-
104-
c_range = [c1, c2, c3]
105-
d_range = [d1, d2]
106-
c_d_range = [c1_d1, c1_d2, c2_d1, c2_d2, c3_d1, c3_d2]
107-
108-
c_d_index = 0
109-
for c in c_range:
110-
for d in d_range:
111-
error_azimuth = (angle_function(c, d) - c_d_range[c_d_index])[0]
112-
error_colatitude = (angle_function(c, d) - c_d_range[c_d_index])[1]
113-
114-
print("for points :")
115-
print(c)
116-
print(d)
117-
print(
118-
"error in azimuth calculation: {}".format(np.average(error_azimuth))
119-
)
120-
print(
121-
"error in colatitude calculation: {}".format(
122-
np.average(error_colatitude)
123-
)
124-
)
125-
print()
126-
c_d_index += 1
127-
128-
129-
if __name__ == "__main__":
130-
find_error("3-D")
131-
find_error("2-D")
54+
def test_set_3d():
55+
assert np.allclose(angle_function(a1, b1), a1_b1)
56+
assert np.allclose(angle_function(a1, b2), a1_b2)
57+
58+
59+
def test_point_3d_1():
60+
assert np.allclose(angle_function(a2, b1), a2_b1)
61+
assert np.allclose(angle_function(a2, b2), a2_b2)
62+
assert np.allclose(angle_function(a2, b3), a2_b3)
63+
64+
65+
def test_point_3d_2():
66+
assert np.allclose(angle_function(a3, b1), a3_b1)
67+
assert np.allclose(angle_function(a3, b2), a3_b2)
68+
assert np.allclose(angle_function(a3, b3), a3_b3)
69+
70+
71+
def test_point_3d_3():
72+
assert np.allclose(angle_function(a4, b1), a4_b1)
73+
assert np.allclose(angle_function(a4, b3), a4_b3)
74+
75+
76+
def test_set_2d():
77+
assert np.allclose(angle_function(c1, d1), c1_d1)
78+
assert np.allclose(angle_function(c1, d2), c1_d2)
79+
80+
81+
def test_point_2d_1():
82+
assert np.allclose(angle_function(c2, d1), c2_d1)
83+
assert np.allclose(angle_function(c2, d2), c2_d2)
84+
85+
86+
def test_point_2d_2():
87+
assert np.allclose(angle_function(c3, d1), c3_d1)
88+
assert np.allclose(angle_function(c3, d2), c3_d2)

pyroomacoustics/utilities.py

+19-23
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from scipy import signal
3030
from scipy.io import wavfile
3131

32+
from .doa import cart2spher
3233
from .parameters import constants, eps
3334
from .sync import correlate
3435

@@ -774,7 +775,8 @@ def all_combinations(lst1, lst2):
774775

775776
def angle_function(s1, v2):
776777
"""
777-
Compute azimuth and colatitude angles in radians for a given set of points `s1` and a singular point `v2`.
778+
Compute azimuth and colatitude angles in radians for a given set of points `s1`
779+
with respect to a reference point `v2`.
778780
779781
Parameters
780782
-----------
@@ -786,8 +788,9 @@ def angle_function(s1, v2):
786788
Returns
787789
-----------
788790
numpy array
789-
2×N numpy array with azimuth and colatitude angles in radians.
790-
791+
2×N numpy array with azimuth and colatitude angles in radians in the
792+
first and second row, respectively.
793+
If the input vectors are 2-D, the colatitude is always fixed to pi/2.
791794
"""
792795

793796
if len(s1.shape) == 1:
@@ -797,26 +800,19 @@ def angle_function(s1, v2):
797800

798801
assert s1.shape[0] == v2.shape[0]
799802

800-
x_vals = s1[0]
801-
y_vals = s1[1]
802-
x2 = v2[0]
803-
y2 = v2[1]
804-
805-
# colatitude calculation for 3-D coordinates
806-
if s1.shape[0] == 3 and v2.shape[0] == 3:
807-
z2 = v2[2]
808-
z_vals = s1[2]
809-
810-
colatitude = np.arctan2(
811-
((x_vals - x2) ** 2 + (y_vals - y2) ** 2) ** 1 / 2, (z_vals - z2)
812-
)
803+
ndim = s1.shape[0]
804+
if ndim == 2:
805+
s1 = np.concatenate((s1, np.zeros((1, s1.shape[1]))), axis=0)
806+
v2 = np.concatenate((v2, np.zeros((1, v2.shape[1]))), axis=0)
813807

814-
# colatitude calculation for 2-D coordinates
815-
elif s1.shape[0] == 2 and v2.shape[0] == 2:
816-
num_points = s1.shape[1]
817-
colatitude = np.ones(num_points) * np.pi / 2
808+
# this is slightly wasteful for 2d points, but is safer as we are relying
809+
# on tried and tested code
810+
az, co, r = cart2spher(s1 - v2)
818811

819-
# azimuth calculation (same for 2-D and 3-D)
820-
azimuth = np.arctan2((y_vals - y2), (x_vals - x2))
812+
if ndim == 2:
813+
# this is only necessary to handle correctly the case s1 - v2 = 0
814+
# in this case cart2spher returns zero, but we would like to have
815+
# colatitude = pi/2 for consistency
816+
co[:] = np.pi / 2
821817

822-
return np.vstack((azimuth, colatitude))
818+
return np.vstack((az, co))

0 commit comments

Comments
 (0)