This page was generated from /home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/184/doc/examples/wfs-referencing.ipynb.
Interactive online version:
WFS Referencing Schemes
Illustrates the usage of the SFS toolbox for the simulation of different sound fields using 2.5D WFS and referencing schemes for contours that exhibit amplitude correct synthesis, cf. Ch. 4.1.3 in Gergely Firtha’s doctoral thesis A Generalized Wave Field Synthesis Framework with Application for Moving Virtual Sources of 2019.
Circular loudspeaker arrays
[1]:
import matplotlib.pyplot as plt
import numpy as np
import sfs
R = 1.5 # Radius of circular loudspeaker array
array = sfs.array.circular(N=64, R=R)
grid = sfs.util.xyz_grid([-2, 2], [-2, 2], 0, spacing=0.02)
xs = -4, 0, 0 # point source on negative x-axis
normalize_gain = 4 * np.pi * np.linalg.norm(xs)
wavelength = 1 / 4 # m
f = sfs.default.c / wavelength # Hz
omega = 2 * np.pi * f # rad/s
[2]:
def sound_field(d, xref, selection,
secondary_source, array, grid, tapering=True):
if tapering:
tapering_window = sfs.tapering.kaiser(selection, beta=1)
else:
tapering_window = sfs.tapering.none(selection)
p = sfs.fd.synthesize(d, tapering_window,
array, secondary_source, grid=grid)
fig, axs = plt.subplots(1, 2, figsize=(10, 8))
sfs.plot2d.amplitude(p, grid, vmax=2, vmin=-2, ax=axs[0])
sfs.plot2d.level(p, grid, vmax=6, vmin=-6, ax=axs[1],
cmap='seismic', colorbar_kwargs={'label': 'dB'})
for i in range(axs.shape[0]):
sfs.plot2d.loudspeakers(array.x, array.n,
tapering_window,
size=0.125, ax=axs[i])
axs[i].plot(xref[:, 0][selection],
xref[:, 1][selection], 'C5o', ms=4)
axs[i].grid(True)
plt.tight_layout()
Line as reference contour
The reference contour is calculated according to https://github.com/spatialaudio/wfs_chapter_hda/blob/master/python/wfs25d_circSSD.py#L91 for a virtual point source on x-axis.
[3]:
# reference contour is a straight line
xref_line = 0
# calc reference contour xref(x0):
x0_tmp = array.x.T[np.newaxis, :]
xs_tmp = np.array(xs)[np.newaxis, :, np.newaxis]
x0xs = x0_tmp - xs_tmp
x0xs_length = np.linalg.norm(x0xs, axis=1)
x0xs_unit = x0xs / x0xs_length
n0_ = array.n.T[np.newaxis, :]
xref = np.zeros_like(x0_tmp)
# code assumes that virtual point source is on x-axis:
for i in range(array.x.shape[0]):
cosbeta = np.dot(-n0_[0, :, i], [-1, 0, 0]) # use outward SSD normal
tmp = x0xs_unit[0, :, i]
tmp *= -x0xs_length[0, i]
tmp *= xref_line + R * cosbeta
tmp /= xs_tmp[0, 0, 0] + R * cosbeta
xref[0, :, i] = x0_tmp[0, :, i] + tmp
xref = np.squeeze(xref).T
d, selection, secondary_source = sfs.fd.wfs.point_25d(
omega, array.x, array.n, xs, xref=xref)
sound_field(d * normalize_gain, xref, selection,
secondary_source, array, grid, tapering=False)
Circle as reference contour
This reference contour is a circle with origin xs and a radius, such that the origin is on this circle. This contour is straightforward with some obvious vector calculus.
[4]:
# reference contour is a circle with origin xs
xref_dist = np.linalg.norm(xs)
# calc reference contour xref(x0):
x0_tmp = array.x.T[np.newaxis, :]
xs_tmp = np.array(xs)[np.newaxis, :, np.newaxis]
x0xs = x0_tmp - xs_tmp
x0xs_length = np.linalg.norm(x0xs, axis=1)
x0xs_unit = x0xs / x0xs_length
xref = x0_tmp + (xref_dist - x0xs_length) * x0xs_unit
xref = np.squeeze(xref).T
d, selection, secondary_source = sfs.fd.wfs.point_25d(
omega, array.x, array.n, xs, xref=xref)
sound_field(d * normalize_gain, xref, selection,
secondary_source, array, grid, tapering=False)