|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "metadata": {}, |
| 6 | + "source": [ |
| 7 | + "# WFS Referencing Schemes\n", |
| 8 | + "\n", |
| 9 | + "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](https://last.hit.bme.hu/download/firtha/PhD_thesis/firtha_phd_thesis.pdf) of 2019.\n", |
| 10 | + "\n" |
| 11 | + ] |
| 12 | + }, |
| 13 | + { |
| 14 | + "cell_type": "markdown", |
| 15 | + "metadata": {}, |
| 16 | + "source": [ |
| 17 | + "## Circular loudspeaker arrays" |
| 18 | + ] |
| 19 | + }, |
| 20 | + { |
| 21 | + "cell_type": "code", |
| 22 | + "execution_count": null, |
| 23 | + "metadata": {}, |
| 24 | + "outputs": [], |
| 25 | + "source": [ |
| 26 | + "import matplotlib.pyplot as plt\n", |
| 27 | + "import numpy as np\n", |
| 28 | + "import sfs\n", |
| 29 | + "\n", |
| 30 | + "R = 1.5 # Radius of circular loudspeaker array\n", |
| 31 | + "array = sfs.array.circular(N=64, R=R)\n", |
| 32 | + "grid = sfs.util.xyz_grid([-2, 2], [-2, 2], 0, spacing=0.02)\n", |
| 33 | + "\n", |
| 34 | + "xs = -4, 0, 0 # point source on negative x-axis\n", |
| 35 | + "normalize_gain = 4 * np.pi * np.linalg.norm(xs)\n", |
| 36 | + "lmb = 1 / 4 # m\n", |
| 37 | + "f = sfs.default.c / lmb # Hz\n", |
| 38 | + "omega = 2 * np.pi * f # rad/s" |
| 39 | + ] |
| 40 | + }, |
| 41 | + { |
| 42 | + "cell_type": "code", |
| 43 | + "execution_count": null, |
| 44 | + "metadata": {}, |
| 45 | + "outputs": [], |
| 46 | + "source": [ |
| 47 | + "def sound_field(d, xref, selection,\n", |
| 48 | + " secondary_source, array, grid, tapering=True):\n", |
| 49 | + " if tapering:\n", |
| 50 | + " tapering_window = sfs.tapering.kaiser(selection, beta=1)\n", |
| 51 | + " else:\n", |
| 52 | + " tapering_window = sfs.tapering.none(selection)\n", |
| 53 | + "\n", |
| 54 | + " p = sfs.fd.synthesize(d, tapering_window,\n", |
| 55 | + " array, secondary_source, grid=grid)\n", |
| 56 | + "\n", |
| 57 | + " fig, axs = plt.subplots(1, 2, figsize=(10, 8))\n", |
| 58 | + " sfs.plot2d.amplitude(p, grid, vmax=2, vmin=-2, ax=axs[0])\n", |
| 59 | + " sfs.plot2d.level(p, grid, vmax=6, vmin=-6, ax=axs[1],\n", |
| 60 | + " cmap='seismic', colorbar_kwargs={'label': 'dB'})\n", |
| 61 | + " for i in range(axs.shape[0]):\n", |
| 62 | + " sfs.plot2d.loudspeakers(array.x, array.n,\n", |
| 63 | + " tapering_window,\n", |
| 64 | + " size=0.125, ax=axs[i])\n", |
| 65 | + " axs[i].plot(xref[:, 0][selection],\n", |
| 66 | + " xref[:, 1][selection], 'C5o', ms=4)\n", |
| 67 | + " axs[i].grid(True)\n", |
| 68 | + " plt.tight_layout()" |
| 69 | + ] |
| 70 | + }, |
| 71 | + { |
| 72 | + "cell_type": "markdown", |
| 73 | + "metadata": {}, |
| 74 | + "source": [ |
| 75 | + "### Line as reference contour\n", |
| 76 | + "\n", |
| 77 | + "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." |
| 78 | + ] |
| 79 | + }, |
| 80 | + { |
| 81 | + "cell_type": "code", |
| 82 | + "execution_count": null, |
| 83 | + "metadata": {}, |
| 84 | + "outputs": [], |
| 85 | + "source": [ |
| 86 | + "# reference contour is a straight line\n", |
| 87 | + "xref_line = 0\n", |
| 88 | + "# calc reference contour xref(x0):\n", |
| 89 | + "x0_tmp = array.x.T[np.newaxis, :]\n", |
| 90 | + "xs_tmp = np.array(xs)[np.newaxis, :, np.newaxis]\n", |
| 91 | + "x0xs = x0_tmp - xs_tmp\n", |
| 92 | + "x0xs_length = np.linalg.norm(x0xs, axis=1)\n", |
| 93 | + "x0xs_unit = x0xs / x0xs_length\n", |
| 94 | + "n0_ = array.n.T[np.newaxis, :]\n", |
| 95 | + "xref = np.zeros_like(x0_tmp)\n", |
| 96 | + "# code assumes that virtual point source is on x-axis:\n", |
| 97 | + "for i in range(array.x.shape[0]):\n", |
| 98 | + " cosbeta = np.dot(-n0_[0, :, i], [-1, 0, 0]) # use outward SSD normal\n", |
| 99 | + " tmp = x0xs_unit[0, :, i]\n", |
| 100 | + " tmp *= -x0xs_length[0, i]\n", |
| 101 | + " tmp *= xref_line + R * cosbeta\n", |
| 102 | + " tmp /= xs_tmp[0, 0, 0] + R * cosbeta\n", |
| 103 | + " xref[0, :, i] = x0_tmp[0, :, i] + tmp\n", |
| 104 | + "xref = np.squeeze(xref).T\n", |
| 105 | + "\n", |
| 106 | + "d, selection, secondary_source = sfs.fd.wfs.point_25d(\n", |
| 107 | + " omega, array.x, array.n, xs, xref=xref)\n", |
| 108 | + "sound_field(d * normalize_gain, xref, selection,\n", |
| 109 | + " secondary_source, array, grid, tapering=False)" |
| 110 | + ] |
| 111 | + }, |
| 112 | + { |
| 113 | + "cell_type": "markdown", |
| 114 | + "metadata": {}, |
| 115 | + "source": [ |
| 116 | + "### Circle as reference contour\n", |
| 117 | + "\n", |
| 118 | + "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." |
| 119 | + ] |
| 120 | + }, |
| 121 | + { |
| 122 | + "cell_type": "code", |
| 123 | + "execution_count": null, |
| 124 | + "metadata": {}, |
| 125 | + "outputs": [], |
| 126 | + "source": [ |
| 127 | + "# reference contour is a circle with origin xs\n", |
| 128 | + "xref_dist = np.linalg.norm(xs)\n", |
| 129 | + "# calc reference contour xref(x0):\n", |
| 130 | + "x0_tmp = array.x.T[np.newaxis, :]\n", |
| 131 | + "xs_tmp = np.array(xs)[np.newaxis, :, np.newaxis]\n", |
| 132 | + "x0xs = x0_tmp - xs_tmp\n", |
| 133 | + "x0xs_length = np.linalg.norm(x0xs, axis=1)\n", |
| 134 | + "x0xs_unit = x0xs / x0xs_length\n", |
| 135 | + "xref = x0_tmp + (xref_dist - x0xs_length) * x0xs_unit\n", |
| 136 | + "xref = np.squeeze(xref).T\n", |
| 137 | + "\n", |
| 138 | + "d, selection, secondary_source = sfs.fd.wfs.point_25d(\n", |
| 139 | + " omega, array.x, array.n, xs, xref=xref)\n", |
| 140 | + "sound_field(d * normalize_gain, xref, selection,\n", |
| 141 | + " secondary_source, array, grid, tapering=False)" |
| 142 | + ] |
| 143 | + } |
| 144 | + ], |
| 145 | + "metadata": { |
| 146 | + "kernelspec": { |
| 147 | + "display_name": "sfs", |
| 148 | + "language": "python", |
| 149 | + "name": "python3" |
| 150 | + }, |
| 151 | + "language_info": { |
| 152 | + "codemirror_mode": { |
| 153 | + "name": "ipython", |
| 154 | + "version": 3 |
| 155 | + }, |
| 156 | + "file_extension": ".py", |
| 157 | + "mimetype": "text/x-python", |
| 158 | + "name": "python", |
| 159 | + "nbconvert_exporter": "python", |
| 160 | + "pygments_lexer": "ipython3", |
| 161 | + "version": "3.13.7" |
| 162 | + } |
| 163 | + }, |
| 164 | + "nbformat": 4, |
| 165 | + "nbformat_minor": 2 |
| 166 | +} |
0 commit comments