This page was generated from doc/examples/wfs-referencing.ipynb. Interactive online version: Binder badge

2.5D WFS Referencing Schemes

This notebook illustrates the usage of the SFS toolbox for the simulation of different 2.5D WFS referencing schemes. A dedicated referencing scheme allows correct amplitude alongside a reference contour within the listening area. For the theory please check Ch 3.1-3.3 in [Sta97], Ch. 4.1.3 in [Fir19] and [FFSS17].

[1]:
import matplotlib.pyplot as plt
import numpy as np
import sfs

Circular loudspeaker arrays

[2]:
R = 1.5  # radius [m] of circular loudspeaker array
N = 64  # loudspeakers
array = sfs.array.circular(N=N, R=R)
grid = sfs.util.xyz_grid([-2, 2], [-2, 2], 0, spacing=0.02)

xs = -4, 0, 0  # virtual point source on negative x-axis
wavelength = 1 / 4  # m
[3]:
def sound_field(d, selection, array, secondary_source, grid, xref):
    p = sfs.fd.synthesize(d, selection, array, secondary_source, grid=grid)
    fig, [ax_amp, ax_lvl] = plt.subplots(2, 1, sharex=True)
    fig.set_figheight(fig.get_figwidth() * 3/2)
    sfs.plot2d.amplitude(p, grid, vmax=2, vmin=-2, ax=ax_amp)
    sfs.plot2d.level(p, grid, vmax=12, vmin=-12, ax=ax_lvl)
    sfs.plot2d.level_contour(p, grid, levels=[0], colors='w', ax=ax_lvl)
    xref = np.broadcast_to(xref, array.x.shape)
    for ax in ax_amp, ax_lvl:
        sfs.plot2d.loudspeakers(array.x, array.n, selection, size=0.125, ax=ax)
    ax_lvl.scatter(*xref[selection, :2].T, marker='o', s=20, c='lightsalmon',
                   zorder=3)
    plt.tight_layout()
    return p
[4]:
xs = sfs.util.asarray_of_rows(xs)
frequency = sfs.default.c / wavelength  # Hz
omega = 2 * np.pi * frequency  # rad/s
normalize_gain = 4 * np.pi * np.linalg.norm(xs)

Line as reference contour

The reference contour is calculated according to eqs. (24), (31), (52) in [FFSS17]. The code assumes a virtual point source on x-axis. The reference contour is a straight line on y-axis.

[5]:
xref_line = 0
cosbeta = (array.n @ [1, 0, 0]).reshape(-1, 1)
xref = array.x + \
    (xs - array.x) * (xref_line + R * cosbeta) / (xs[0, 0] + R * cosbeta)

d, selection, secondary_source = sfs.fd.wfs.point_25d(
    omega, array.x, array.n, xs, xref=xref)
p_line = sound_field(
    d * normalize_gain, selection, array, secondary_source, grid, xref)
../_images/examples_wfs-referencing_7_0.svg

The level plot includes a white 0 dB isobar curve. The orange-like dots represent the stationary phase points at which amplitude correct synthesis is to be expected. These dots shape the line reference contour. Note that the isobar curve is not perfectly aligned along line reference contour due to diffraction artifacts.

Circle as reference contour

This reference contour is a circle with its origin at xs and a radius |xs|. This contour is obtained with more straightforward vector calculus than the previous example.

[6]:
# reference contour is a circle with origin xs and radius |xs|
xref_dist = np.linalg.norm(xs)
# calc reference contour xref(x0), cf. [Firtha19, eq. (24), (31)]
xref = xs + xref_dist * sfs.util.normalize_rows(array.x - xs)
d, selection, secondary_source = sfs.fd.wfs.point_25d(
   omega, array.x, array.n, xs, xref=xref)
p_circ = sound_field(
    d * normalize_gain, selection, array, secondary_source, grid, xref)
../_images/examples_wfs-referencing_10_0.svg

Reference point

The default handling in point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None) uses just a reference point xref, and more specifically this default point is the origin of the coordinate system. This single point xref, the virtual source position xs and the loudspeaker array geometry together determine the reference contour without further user access to it. This handling is chosen due to convenience and practical relevance when working with circular loudspeaker arrays.

The example below shows the resulting reference contour for the default case. In the example it looks similar to the line reference contour, but is in general not exactly the same. For example, please try a virtual point source that is far away from the array.

[7]:
d, selection, secondary_source = sfs.fd.wfs.point_25d(
    omega, array.x, array.n, xs)
p_point = sound_field(
    d * normalize_gain, selection, array, secondary_source,
    grid, [0, 0, 0])
../_images/examples_wfs-referencing_13_0.svg

Points with amplitude correct synthesis need to be stationary phase points, theoretically. Within the listening area, these points are found on rays that start at the virtual point source and intersect with active loudspeakers. The chosen points together shall shape a smooth contour, i.e. the reference contour.

The example below shows a reference point xref that does not meet any ray (the gray lines in the level plot) alongside the stationary phase holds with its corresponding loudspeaker.

The single point referencing scheme results in 0 dB isobar curve that closely passes the chosen xref point. In practice this typically works with sufficient precision once the position of xref is appropriately chosen (i.e. not too close, not too far, not to off-center from the active loudspeakers etc.).

[8]:
xref = 0, 0.1175, 0  # intentionally no stationary phase point
#  we don't forget to normalize the point source's amplitude
# to this new reference point:
normalize_gain = 4 * np.pi * np.linalg.norm(xs - xref)
d, selection, secondary_source = sfs.fd.wfs.point_25d(
    omega, array.x, array.n, xs, xref=xref)
p_point = sound_field(
    d * normalize_gain, selection, array, secondary_source,
    grid, xref)

# plot stationary phase rays
# one ray connects the virtual source with one activate loudspeaker
spa = array.x + 3*R * sfs.util.normalize_rows(array.x - xs)
plt.plot(
   np.vstack((array.x[selection, 0], spa[selection, 0])),
   np.vstack((array.x[selection, 1], spa[selection, 1])),
   color='gray')
plt.xlim(-2, 2)
plt.ylim(-2, 2);
../_images/examples_wfs-referencing_15_0.svg

A plane wave like sound field, e.g. by setting xs = -100, 0, 0, for all above examples reveals some further interesting implications of the different referencing schemes.