|
| 1 | +""" |
| 2 | + Modal analysis and extrapolation of a monochromatic sound field |
| 3 | + in the cricular harmonics domain using an open circular array |
| 4 | +""" |
| 5 | +import numpy as np |
| 6 | +import micarray |
| 7 | +from micarray.util import db |
| 8 | +import scipy.special as special |
| 9 | +import matplotlib.pyplot as plt |
| 10 | +import matplotlib |
| 11 | +matplotlib.rcParams['contour.negative_linestyle'] = 'solid' |
| 12 | + |
| 13 | +# Constants |
| 14 | +c = 343 # speed of sound [m/s] |
| 15 | + |
| 16 | +# 2-dimensional grid |
| 17 | +spacing = 0.01 |
| 18 | +x = np.expand_dims(np.arange(-1, 1, spacing), axis=0) |
| 19 | +y = np.expand_dims(np.arange(-1, 1, spacing), axis=1) |
| 20 | +r = np.sqrt(x**2+y**2).astype(complex) |
| 21 | +phi = np.arctan2(y, x) |
| 22 | + |
| 23 | +# Incident plane wave |
| 24 | +phi_pw = 0.5*np.pi # incoming direction |
| 25 | +f = 1500 # temporal frequency |
| 26 | +k = micarray.util.asarray_1d(2*np.pi*f/c) # corresponding wave number |
| 27 | +s0 = np.exp(1j*k*r*np.cos(phi-phi_pw)) # incident sound field |
| 28 | + |
| 29 | +# Microphone array and modal analysis |
| 30 | +N = 15 # maximum order |
| 31 | +order = np.roll(np.arange(-N, N+1), -N) |
| 32 | +threshold = 1e5 # regulaization parameter |
| 33 | +R = 0.5 # radius |
| 34 | +Phi, weights = micarray.modal.angular.grid_equal_polar_angle(N) # array |
| 35 | +p = np.exp(1j*k*R*np.cos(Phi-phi_pw)) # captured signal |
| 36 | +bn = micarray.modal.radial.circ_radial_weights(N, k*R, setup='open') |
| 37 | +dn, _ = micarray.modal.radial.regularize(1/bn, threshold, 'softclip') |
| 38 | +pm = dn * np.fft.ifft(p) |
| 39 | + |
| 40 | +# Sound field extrapolation |
| 41 | +basis = special.jn(order[:, np.newaxis, np.newaxis], k * r[np.newaxis, :, :]) \ |
| 42 | + * np.exp(-1j*order[:, np.newaxis, np.newaxis] * phi[np.newaxis, :, :]) |
| 43 | +s = np.tensordot(pm, basis, axes=[0, 0]) |
| 44 | + |
| 45 | + |
| 46 | +# Plots |
| 47 | +plt.figure(figsize=(4, 4)) |
| 48 | +plt.pcolormesh(x, y, np.real(s), cmap='coolwarm') |
| 49 | +plt.plot(R*np.cos(Phi), R*np.sin(Phi), 'k.') |
| 50 | +plt.axis('scaled') |
| 51 | +plt.axis([-1, 1, -1, 1]) |
| 52 | +cb = plt.colorbar(fraction=0.046, pad=0.04) |
| 53 | +plt.clim(-1, 1) |
| 54 | +plt.xlabel('$x$ / m') |
| 55 | +plt.ylabel('$y$ / m') |
| 56 | +plt.title('Extrapolated Sound Field') |
| 57 | +plt.savefig('extrapolation_open_circ_mono.png') |
| 58 | + |
| 59 | +plt.figure(figsize=(4, 4)) |
| 60 | +plt.pcolormesh(x, y, db(s0-s), cmap='Blues', vmin=-60) |
| 61 | +plt.plot(R*np.cos(Phi), R*np.sin(Phi), 'k.') |
| 62 | +plt.axis('scaled') |
| 63 | +plt.axis([-1, 1, -1, 1]) |
| 64 | +cb = plt.colorbar(fraction=0.046, pad=0.04) |
| 65 | +cb.set_label('dB') |
| 66 | +plt.clim(-60, 0) |
| 67 | +plt.xlabel('$x$ / m') |
| 68 | +plt.ylabel('$y$ / m') |
| 69 | +xx, yy = np.meshgrid(x, y) |
| 70 | +cs = plt.contour(xx, yy, db(s0-s), np.arange(-60, 20, 20), colors='orange') |
| 71 | +plt.clabel(cs, fontsize=9, inline=1, fmt='%1.0f') |
| 72 | +plt.title('Extrapolation Error') |
| 73 | +plt.savefig('extrapolation_error_open_circ_mono.png') |
0 commit comments