From 3ec03d92a528376349918291204ece285ff4631d Mon Sep 17 00:00:00 2001 From: Frank Schultz Date: Wed, 13 Mar 2019 16:41:54 +0100 Subject: [PATCH 1/2] 2.5D Unified WFS and Far/HF NFC-InfOA Driving Functions for DAGA2019 --- sfs/mono/nfchoa.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++ sfs/mono/wfs.py | 79 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/sfs/mono/nfchoa.py b/sfs/mono/nfchoa.py index 6677d229..e2d56c1b 100644 --- a/sfs/mono/nfchoa.py +++ b/sfs/mono/nfchoa.py @@ -234,3 +234,90 @@ def plane_25d(omega, x0, r0, n=[0, 1, 0], max_order=None, c=None): d += (-1j)**abs(m) / (k * hn2[abs(m)]) * np.exp(1j * m * (phi0 - phi)) selection = util.source_selection_all(len(x0)) return 2*1j / r0 * d, selection, secondary_source_point(omega, c) + + +def point_25d_HF(omega, x0, r0, xs, M=None, c=None, + DirichletKernelFlag=True): + """Compute point source driving function for NFC-InfiniteOA. + + This only theoretically useful driving function is used in + + https://github.com/spatialaudio/sfs-with-local-wavenumber-vector-concept + + For a continous SSD only one secondary source is active. + + Examples + -------- + .. plot:: + :context: close-figs + + d, selection, secondary_source = sfs.mono.nfchoa.point_25d_HF( + omega, array.x, R, xs) + plot(d, selection, secondary_source) + + """ + x0 = util.asarray_of_rows(x0) + k = util.wavenumber(omega, c) + xs = util.asarray_1d(xs) + phi, _, r = util.cart2sph(*xs) + phi0 = util.cart2sph(*x0.T)[0] + if M is None: + M = util.max_order_circular_harmonics(len(x0)) + d = 0 + + if DirichletKernelFlag is True: + for m in range(-M, M + 1): + d += np.exp(-1j * k * r) / np.exp(-1j * k * r0) * \ + r0 / r * \ + np.exp(-1j * m * (phi0 - phi)) + d = d / (2 * np.pi * r0) + else: # this is the analytic solution for m=oo and requires an exact + # stationary phase source within phi0, i.e. phi==phi0 in exactly one + # index + d = (np.exp(-1j * k * r) / np.exp(-1j * k * r0) * + r0 / r / r0 * + len(x0)/2/np.pi * + (phi == phi0)) # choose exactly one sec source + selection = util.source_selection_all(len(x0)) + return d, selection, secondary_source_point(omega, c) + + +def plane_25d_HF(omega, x0, r0, n=[0, 1, 0], M=None, c=None, + DirichletKernelFlag=True): + """Compute plane wave driving function for NFC-InfiniteOA. + + This only theoretically useful driving function is used in + + https://github.com/spatialaudio/sfs-with-local-wavenumber-vector-concept + + For a continous SSD only one secondary source is active. + + Examples + -------- + .. plot:: + :context: close-figs + + d, selection, secondary_source = sfs.mono.nfchoa.plane_25d_HF( + omega, array.x, R, xs) + plot(d, selection, secondary_source) + + """ + x0 = util.asarray_of_rows(x0) + k = util.wavenumber(omega, c) + n = util.normalize_vector(n) + phi, _, r = util.cart2sph(*n) + phi0 = util.cart2sph(*x0.T)[0] + if M is None: + M = util.max_order_circular_harmonics(len(x0)) + d = 0 + + if DirichletKernelFlag is True: + for m in range(-M, M + 1): + d += np.exp(1j*np.pi*m) * np.exp(1j*m*(phi0 - phi)) + d *= (2/r0) * (r0/np.exp(-1j*k*r0)) + else: + d = (1/r0 * 4*np.pi*r0 / np.exp(-1j*k*r0) * + len(x0)/2/np.pi * + (phi == (phi0-np.pi))) # choose exactly one sec source + selection = util.source_selection_all(len(x0)) + return d, selection, secondary_source_point(omega, c) diff --git a/sfs/mono/wfs.py b/sfs/mono/wfs.py index a05aa0fa..efb17b24 100644 --- a/sfs/mono/wfs.py +++ b/sfs/mono/wfs.py @@ -217,6 +217,85 @@ def point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None): return d, selection, secondary_source_point(omega, c) +def point_25d_Unified_WIP(omega, x0, n0, xs, xref=[0, 0, 0], + c=None, omalias=None): + r"""Driving function for 2.5-dimensional WFS of a virtual point source. + + Parameters + ---------- + omega : float + Angular frequency of point source. + x0 : (N, 3) array_like + Sequence of secondary source positions. + n0 : (N, 3) array_like + Sequence of normal vectors of secondary sources. + xs : (3,) array_like + Position of virtual point source. + xref : (3,) array_like, optional + Reference point xref or contour xref(x0) for amplitude correct synthesis. + c : float, optional + Speed of sound in m/s. + omalias : float, optional + Cut angular frequency for prefilter. + + Returns + ------- + d : (N,) numpy.ndarray + Complex weights of secondary sources. + selection : (N,) numpy.ndarray + Boolean array containing ``True`` or ``False`` depending on + whether the corresponding secondary source is "active" or not. + secondary_source_function : callable + A function that can be used to create the sound field of a + single secondary source. See `sfs.mono.synthesize()`. + + Notes + ----- + Eq. (3.10), (3.11) Start, E.W. (1997): "Direct sound enhancement by wave + field synthesis", doctoral thesis, TU Delft + + Eq. (2.137) from Schultz, F. (2016): https://doi.org/10.18453/rosdok_id00001765 + + .. math:: + + D(\x_0,\w) = \sqrt{8 \pi \, \i\wc} + \sqrt{\frac{|\x_\text{ref}-\x_0| \cdot + |\x_0-\x_\text{s}|}{|\x_\text{ref}-\x_0| + |\x_0-\x_\text{s}|}} + \scalarprod{\frac{\x_0-\x_\text{s}}{|\x_0-\x_\text{s}|}}{\n_0} + \frac{\e{-\i\wc |\x_0-\x_\text{s}|}}{4\pi\,|\x_0-\x_\text{s}|} + + Examples + -------- + .. plot:: + :context: close-figs + + d, selection, secondary_source = ( + sfs.mono.wfs.point_25d_Unified_WIP( + omega, array.x, array.n, xs)) + plot(4*np.pi*np.linalg.norm(xs) * d, selection, secondary_source) + + """ + x0 = util.asarray_of_rows(x0) + n0 = util.asarray_of_rows(n0) + xs = util.asarray_1d(xs) + xref = util.asarray_1d(xref) + k = util.wavenumber(omega, c) + + ds = x0 - xs + dr = xref - x0 + s = np.linalg.norm(ds, axis=1) + r = np.linalg.norm(dr, axis=1) + + d = ( + preeq_25d(omega, omalias, c) * + np.sqrt(8 * np.pi) * + np.sqrt((r * s) / (r + s)) * + inner1d(n0, ds) / s * + np.exp(-1j * k * s) / (4 * np.pi * s)) + selection = util.source_selection_point(n0, x0, xs) + return d, selection, secondary_source_point(omega, c) + + point_3d = _point From c67db62da81d0e63515bbe86bdda548684851edd Mon Sep 17 00:00:00 2001 From: Frank Schultz Date: Thu, 14 Mar 2019 00:01:13 +0100 Subject: [PATCH 2/2] FIX UP: bugfix docstring example --- sfs/mono/nfchoa.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sfs/mono/nfchoa.py b/sfs/mono/nfchoa.py index e2d56c1b..6d6dc373 100644 --- a/sfs/mono/nfchoa.py +++ b/sfs/mono/nfchoa.py @@ -253,7 +253,7 @@ def point_25d_HF(omega, x0, r0, xs, M=None, c=None, d, selection, secondary_source = sfs.mono.nfchoa.point_25d_HF( omega, array.x, R, xs) - plot(d, selection, secondary_source) + plot(d * np.linalg.norm(xs)*4*np.pi, selection, secondary_source) """ x0 = util.asarray_of_rows(x0) @@ -298,7 +298,7 @@ def plane_25d_HF(omega, x0, r0, n=[0, 1, 0], M=None, c=None, :context: close-figs d, selection, secondary_source = sfs.mono.nfchoa.plane_25d_HF( - omega, array.x, R, xs) + omega, array.x, R, npw) plot(d, selection, secondary_source) """