Skip to content

Commit c3d5d3b

Browse files
authored
Merge pull request #8 from spatialaudio/consistent_usage_of_transform_matrices
Fix inconsistent usage of transform matrices
2 parents 234bbdd + c1c0e07 commit c3d5d3b

5 files changed

+30
-20
lines changed

examples/modal_beamforming_open_array.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def dot_product_sph(v, u):
3535
# compute microphone signals for an incident broad-band plane wave
3636
p = np.exp(1j * k[:, np.newaxis]*r * dot_product_sph((azi, elev), pw_angle))
3737
# compute the plane wave dcomposition
38-
A_pwd = np.matmul(np.matmul(np.conj(Y_q.T), D), Y_p)
38+
A_pwd = np.matmul(np.matmul(Y_q, D), np.conj(Y_p.T))
3939
q_pwd = np.squeeze(np.matmul(A_pwd, np.expand_dims(p, 2)))
4040
q_pwd_t = np.fft.fftshift(np.fft.irfft(q_pwd, axis=0), axes=0)
4141

@@ -46,11 +46,11 @@ def dot_product_sph(v, u):
4646
plt.xlabel(r'$kr$')
4747
plt.ylabel(r'$\phi / \pi$')
4848
plt.title('Plane wave docomposition by modal beamformer (frequency domain)')
49-
plt.savefig('modal_open_beamformer_pwd_fd.png')
49+
plt.savefig('spherical_modal_open_beamformer_pwd_fd.png')
5050

5151
plt.figure()
5252
plt.pcolormesh(range(2*len(k)-2), azi_pwd/np.pi, 20*np.log10(np.abs(q_pwd_t.T)), vmin=-40)
5353
plt.colorbar()
5454
plt.ylabel(r'$\phi / \pi$')
5555
plt.title('Plane wave docomposition by modal beamformer (time domain)')
56-
plt.savefig('modal_open_beamformer_pwd_td.png')
56+
plt.savefig('spherical_modal_open_beamformer_pwd_td.png')

examples/modal_beamforming_open_circular_array.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
# get uniform grid (microphone positions) of order N
1919
pol, weights = micarray.modal.angular.grid_equal_polar_angle(N)
2020

21-
# pressure on the surface of a rigid cylinder for an incident plane wave
21+
# pressure on the surface of an open cylinder for an incident plane wave
2222
Bn = micarray.modal.radial.circular_pw(Nsf, k, r, setup='open')
2323
D = micarray.modal.radial.circ_diagonal_mode_mat(Bn)
2424
Psi_p = micarray.modal.angular.cht_matrix(Nsf, pol)
2525
Psi_pw = micarray.modal.angular.cht_matrix(Nsf, pw_angle)
26-
p = np.matmul(np.matmul(np.conj(Psi_p.T), D), Psi_pw)
26+
p = np.matmul(np.matmul(Psi_p, D), np.conj(Psi_pw.T))
2727
p = np.squeeze(p)
2828

2929
# incident plane wave exhibiting infinite spatial bandwidth
@@ -35,7 +35,7 @@
3535
D = micarray.modal.radial.circ_diagonal_mode_mat(Dn)
3636
Psi_p = micarray.modal.angular.cht_matrix(N, pol, weights)
3737
Psi_q = micarray.modal.angular.cht_matrix(N, pol_pwd)
38-
A_pwd = np.matmul(np.matmul(np.conj(Psi_q.T), D), Psi_p)
38+
A_pwd = np.matmul(np.matmul(Psi_q, D), np.conj(Psi_p.T))
3939
q_pwd = np.squeeze(np.matmul(A_pwd, np.expand_dims(p, 2)))
4040
q_pwd_t = np.fft.fftshift(np.fft.irfft(q_pwd, axis=0), axes=0)
4141

examples/modal_beamforming_rigid_array.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
D = micarray.modal.radial.diagonal_mode_mat(bn)
2323
Y_p = micarray.modal.angular.sht_matrix(N, azi, elev)
2424
Y_pw = micarray.modal.angular.sht_matrix(N, azi_pw, np.pi/2)
25-
p = np.matmul(np.matmul(np.conj(Y_pw.T), D), Y_p)
25+
p = np.matmul(np.matmul(Y_p, D), np.conj(Y_pw.T))
2626
p = np.squeeze(p)
2727

2828
# plane wave decomposition using modal beamforming
@@ -35,7 +35,7 @@
3535
dn, _ = micarray.modal.radial.regularize(1/bn, 100, 'softclip')
3636
D = micarray.modal.radial.diagonal_mode_mat(dn)
3737
# compute the PWD
38-
A_mb = np.matmul(np.matmul(np.conj(Y_q.T), D), Y_p)
38+
A_mb = np.matmul(np.matmul(Y_q, D), np.conj(Y_p.T))
3939
q_mb = np.squeeze(np.matmul(A_mb, np.expand_dims(p, 2)))
4040
q_mb_t = np.fft.fftshift(np.fft.irfft(q_mb, axis=0), axes=0)
4141

examples/modal_beamforming_rigid_circular_array.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
D = micarray.modal.radial.circ_diagonal_mode_mat(Bn)
2424
Psi_p = micarray.modal.angular.cht_matrix(Nsf, pol)
2525
Psi_pw = micarray.modal.angular.cht_matrix(Nsf, pw_angle)
26-
p = np.matmul(np.matmul(np.conj(Psi_p.T), D), Psi_pw)
26+
p = np.matmul(np.matmul(Psi_p, D), np.conj(Psi_pw.T))
2727
p = np.squeeze(p)
2828

2929
# plane wave decomposition using modal beamforming
@@ -32,7 +32,7 @@
3232
Bn = micarray.modal.radial.circular_pw(N, k, r, setup='rigid')
3333
Dn, _ = micarray.modal.radial.regularize(1/Bn, 100, 'softclip')
3434
D = micarray.modal.radial.circ_diagonal_mode_mat(Dn)
35-
A_pwd = np.matmul(np.matmul(np.conj(Psi_q.T), D), Psi_p)
35+
A_pwd = np.matmul(np.matmul(Psi_q, D), np.conj(Psi_p.T))
3636
q_pwd = np.squeeze(np.matmul(A_pwd, np.expand_dims(p, 2)))
3737
q_pwd_t = np.fft.fftshift(np.fft.irfft(q_pwd, axis=0), axes=0)
3838

micarray/modal/angular.py

+20-10
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def sht_matrix(N, azi, elev, weights=None):
1212
1313
.. math::
1414
15-
\mathbf{Y} = \left[ \begin{array}{cccccc}
15+
\mathbf{Y} = \left[ \begin{array}{cccccc}
1616
Y_0^0(\theta[0], \phi[0]) & Y_1^{-1}(\theta[0], \phi[0]) & Y_1^0(\theta[0], \phi[0]) & Y_1^1(\theta[0], \phi[0]) & \dots & Y_N^N(\theta[0], \phi[0]) \\
1717
Y_0^0(\theta[1], \phi[1]) & Y_1^{-1}(\theta[1], \phi[1]) & Y_1^0(\theta[1], \phi[1]) & Y_1^1(\theta[1], \phi[1]) & \dots & Y_N^N(\theta[1], \phi[1]) \\
1818
\vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\
@@ -25,6 +25,10 @@ def sht_matrix(N, azi, elev, weights=None):
2525
2626
Y_n^m(\theta, \phi) = \sqrt{\frac{2n + 1}{4 \pi} \frac{(n-m)!}{(n+m)!}} P_n^m(\cos \theta) e^{i m \phi}
2727
28+
29+
(Note: :math:`\mathbf{Y}` is interpreted as the inverse transform (or synthesis)
30+
matrix in examples and documentation.)
31+
2832
Parameters
2933
----------
3034
N : int
@@ -38,22 +42,23 @@ def sht_matrix(N, azi, elev, weights=None):
3842
3943
Returns
4044
-------
41-
Ymn : ((N+1)**2, Q) numpy.ndarray
45+
Ymn : (Q, (N+1)**2) numpy.ndarray
4246
Matrix of spherical harmonics.
47+
4348
"""
4449
azi = util.asarray_1d(azi)
4550
elev = util.asarray_1d(elev)
4651
if azi.ndim == 0:
47-
M = 1
52+
Q = 1
4853
else:
49-
M = len(azi)
54+
Q = len(azi)
5055
if weights is None:
51-
weights = np.ones(M)
52-
Ymn = np.zeros([(N+1)**2, M], dtype=complex)
56+
weights = np.ones(Q)
57+
Ymn = np.zeros([Q, (N+1)**2], dtype=complex)
5358
i = 0
5459
for n in range(N+1):
5560
for m in range(-n, n+1):
56-
Ymn[i, :] = weights * special.sph_harm(m, n, azi, elev)
61+
Ymn[:, i] = weights * special.sph_harm(m, n, azi, elev)
5762
i += 1
5863
return Ymn
5964

@@ -105,6 +110,10 @@ def cht_matrix(N, pol, weights=None):
105110
1 & e^{i\varphi[Q-1]} & \cdots & e^{iN\varphi[Q-1]} & e^{-iN\varphi[Q-1]} & \cdots & e^{-i\varphi[Q-1]}
106111
\end{array} \right]
107112
113+
(Note: :math:`\Psi` is interpreted as the inverse transform (or synthesis)
114+
matrix in examples and documentation.)
115+
116+
108117
Parameters
109118
----------
110119
N : int
@@ -116,8 +125,9 @@ def cht_matrix(N, pol, weights=None):
116125
117126
Returns
118127
-------
119-
Psi : (2N+1, Q) numpy.ndarray
128+
Psi : (Q, 2N+1) numpy.ndarray
120129
Matrix of circular harmonics.
130+
121131
"""
122132
pol = util.asarray_1d(pol)
123133
if pol.ndim == 0:
@@ -126,10 +136,10 @@ def cht_matrix(N, pol, weights=None):
126136
Q = len(pol)
127137
if weights is None:
128138
weights = np.ones(Q)
129-
Psi = np.zeros([(2*N+1), Q], dtype=complex)
139+
Psi = np.zeros([Q, (2*N+1)], dtype=complex)
130140
order = np.roll(np.arange(-N, N+1), -N)
131141
for i, n in enumerate(order):
132-
Psi[i, :] = weights * np.exp(1j * n * pol)
142+
Psi[:, i] = weights * np.exp(1j * n * pol)
133143
return Psi
134144

135145

0 commit comments

Comments
 (0)