-
Notifications
You must be signed in to change notification settings - Fork 20
add 2.5D WFS reference contour examples #184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
71415ba
ff48bbd
ca0e977
5391c0e
a50ca73
9e90e7e
f438743
6da4390
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,250 @@ | ||||||
| { | ||||||
| "cells": [ | ||||||
| { | ||||||
| "cell_type": "markdown", | ||||||
| "metadata": {}, | ||||||
| "source": [ | ||||||
| "# 2.5D WFS Referencing Schemes\n", | ||||||
| "\n", | ||||||
| "This notebook illustrates the usage of the SFS toolbox for the simulation of different 2.5D WFS referencing schemes.\n", | ||||||
| "A dedicated referencing scheme allows correct amplitude alongside a reference contour within the listening area.\n", | ||||||
| "For the theory please check\n", | ||||||
| "Ch 3.1-3.3 in <cite data-cite=\"Start1997\">(Start1997)</cite>,\n", | ||||||
| "Ch. 4.1.3 in <cite data-cite=\"Firtha2019\">(Firtha2019)</cite> and\n", | ||||||
| "<cite data-cite=\"Firtha2017\">(Firtha2017)</cite>." | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "code", | ||||||
| "execution_count": null, | ||||||
| "metadata": {}, | ||||||
| "outputs": [], | ||||||
| "source": [ | ||||||
| "import matplotlib.pyplot as plt\n", | ||||||
| "import numpy as np\n", | ||||||
| "import sfs" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "markdown", | ||||||
| "metadata": {}, | ||||||
| "source": [ | ||||||
| "## Circular loudspeaker arrays" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "code", | ||||||
| "execution_count": null, | ||||||
| "metadata": {}, | ||||||
| "outputs": [], | ||||||
| "source": [ | ||||||
| "R = 1.5 # radius [m] of circular loudspeaker array\n", | ||||||
| "array = sfs.array.circular(N=64, R=R) # with N loudspeakers\n", | ||||||
| "grid = sfs.util.xyz_grid([-2, 2], [-2, 2], 0, spacing=0.02)\n", | ||||||
| "\n", | ||||||
| "xs = -4, 0, 0 # virtual point source on negative x-axis\n", | ||||||
| "wavelength = 1 / 4 # m" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "code", | ||||||
| "execution_count": null, | ||||||
| "metadata": {}, | ||||||
| "outputs": [], | ||||||
| "source": [ | ||||||
| "def sound_field(d, selection, array, secondary_source, grid, xref):\n", | ||||||
| " p = sfs.fd.synthesize(d, selection, array, secondary_source, grid=grid)\n", | ||||||
| " fig, [ax_amp, ax_lvl] = plt.subplots(2, 1, sharex=True)\n", | ||||||
| " fig.set_figheight(fig.get_figwidth() * 3/2)\n", | ||||||
| " sfs.plot2d.amplitude(p, grid, vmax=2, vmin=-2, ax=ax_amp)\n", | ||||||
| " sfs.plot2d.level(p, grid, vmax=6, vmin=-6, ax=ax_lvl)\n", | ||||||
| " sfs.plot2d.level_contour(p, grid, levels=[0], colors='w', ax=ax_lvl)\n", | ||||||
| " for ax in ax_amp, ax_lvl:\n", | ||||||
| " sfs.plot2d.loudspeakers(array.x, array.n, selection, size=0.125, ax=ax)\n", | ||||||
| " ax_lvl.scatter(*xref[selection, :2].T, marker='o', s=20, c='lightsalmon',\n", | ||||||
| " zorder=3)\n", | ||||||
| " plt.tight_layout()\n", | ||||||
| " return p" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "code", | ||||||
| "execution_count": null, | ||||||
| "metadata": {}, | ||||||
| "outputs": [], | ||||||
| "source": [ | ||||||
| "xs = sfs.util.asarray_of_rows(xs)\n", | ||||||
| "frequency = sfs.default.c / wavelength # Hz\n", | ||||||
| "omega = 2 * np.pi * frequency # rad/s\n", | ||||||
| "normalize_gain = 4 * np.pi * np.linalg.norm(xs)" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "markdown", | ||||||
| "metadata": {}, | ||||||
| "source": [ | ||||||
| "### Line as reference contour\n", | ||||||
| "\n", | ||||||
| "The reference contour is calculated according to eqs. (24), (31), (52) in <cite data-cite=\"Firtha2017\">(Firtha2017)</cite>. \n", | ||||||
| "The code assumes a virtual point source on x-axis.\n", | ||||||
| "The reference contour is a straight line on y-axis." | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "code", | ||||||
| "execution_count": null, | ||||||
| "metadata": {}, | ||||||
| "outputs": [], | ||||||
| "source": [ | ||||||
| "xref_line = 0\n", | ||||||
| "cosbeta = (array.n @ [1, 0, 0]).reshape(-1, 1)\n", | ||||||
| "xref = array.x + \\\n", | ||||||
| " (xs - array.x) * (xref_line + R * cosbeta) / (xs[0, 0] + R * cosbeta)\n", | ||||||
| "\n", | ||||||
| "d, selection, secondary_source = sfs.fd.wfs.point_25d(\n", | ||||||
| " omega, array.x, array.n, xs, xref=xref)\n", | ||||||
| "p_line = sound_field(\n", | ||||||
| " d * normalize_gain, selection, array, secondary_source, grid, xref)" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "markdown", | ||||||
| "metadata": {}, | ||||||
| "source": [ | ||||||
| "The level plot includes a white 0 dB isobar curve.\n", | ||||||
| "The orange-like dots represent the stationary phase points at which amplitude correct synthesis is to be expected.\n", | ||||||
| "These dots shape the line reference contour.\n", | ||||||
| "Note that the isobar curve is not perfectly aligned along line reference contour due to diffraction artifacts." | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "markdown", | ||||||
| "metadata": {}, | ||||||
| "source": [ | ||||||
| "### Circle as reference contour\n", | ||||||
| "\n", | ||||||
| "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." | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "code", | ||||||
| "execution_count": null, | ||||||
| "metadata": {}, | ||||||
| "outputs": [], | ||||||
| "source": [ | ||||||
| "# reference contour is a circle with origin xs and radius |xs|\n", | ||||||
| "xref_dist = np.linalg.norm(xs)\n", | ||||||
| "# calc reference contour xref(x0), cf. [Firtha19, eq. (24), (31)]\n", | ||||||
| "xref = xs + xref_dist * sfs.util.normalize_rows(array.x - xs)\n", | ||||||
| "d, selection, secondary_source = sfs.fd.wfs.point_25d(\n", | ||||||
| " omega, array.x, array.n, xs, xref=xref)\n", | ||||||
| "p_circ = sound_field(\n", | ||||||
| " d * normalize_gain, selection, array, secondary_source, grid, xref)" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "markdown", | ||||||
| "metadata": {}, | ||||||
| "source": [ | ||||||
| "### Reference point" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "markdown", | ||||||
| "metadata": {}, | ||||||
| "source": [ | ||||||
| "The default handling in\n", | ||||||
| "`point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None)`\n", | ||||||
| "uses just a reference point xref, and more specifically this default point is the origin of the coordinate system.\n", | ||||||
| "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.\n", | ||||||
| "This handling is chosen due to convenience and practical relevance when working with circular loudspeaker arrays.\n", | ||||||
| "\n", | ||||||
| "The example below shows the resulting reference contour for the default case.\n", | ||||||
| "In the example it looks similar to the line reference contour, but is in general not exactly the same.\n", | ||||||
| "For example, please try a virtual point source that is far away from the array." | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "code", | ||||||
| "execution_count": null, | ||||||
| "metadata": {}, | ||||||
| "outputs": [], | ||||||
| "source": [ | ||||||
| "d, selection, secondary_source = sfs.fd.wfs.point_25d(\n", | ||||||
| " omega, array.x, array.n, xs)\n", | ||||||
| "p_point = sound_field(\n", | ||||||
| " d * normalize_gain, selection, array, secondary_source,\n", | ||||||
| " grid, np.array([[0, 0, 0]]*array.x.shape[0]))" | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "markdown", | ||||||
| "metadata": {}, | ||||||
| "source": [ | ||||||
| "Points with amplitude correct synthesis need to be stationary phase points, theoretically.\n", | ||||||
| "Within the listening area, these points are found on rays that start at the virtual point source and intersect with active loudspeakers.\n", | ||||||
| "The chosen points together shall shape a smooth contour, i.e. the reference contour.\n", | ||||||
| "\n", | ||||||
| "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.\n", | ||||||
| "\n", | ||||||
| "The single point referencing scheme results in 0 dB isobar curve that closely passes the chosen xref point.\n", | ||||||
| "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.)." | ||||||
| ] | ||||||
| }, | ||||||
| { | ||||||
| "cell_type": "code", | ||||||
| "execution_count": null, | ||||||
| "metadata": {}, | ||||||
| "outputs": [], | ||||||
| "source": [ | ||||||
| "xref = [0, 0.1175, 0] # intentionally no stationary phase point\n", | ||||||
|
||||||
| "xref = [0, 0.1175, 0] # intentionally no stationary phase point\n", | |
| "xref = 0, 0.1175, 0 # intentionally no stationary phase point\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without brackets -> ok
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| "normalize_gain = 4 * np.pi * np.linalg.norm(xs - np.array(xref))\n", | |
| "normalize_gain = 4 * np.pi * np.linalg.norm(xs - xref)\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
Uh oh!
There was an error while loading. Please reload this page.