|
6 | 6 | "source": [ |
7 | 7 | "# 2.5D WFS Referencing Schemes\n", |
8 | 8 | "\n", |
9 | | - "Illustrates the usage of the SFS toolbox for the simulation of different 2.5D WFS referencing schemes.\n", |
10 | | - "This so called referencing controls contours alongside correct amplitude is obtained within the listening area,\n", |
11 | | - "cf. Ch 3.1-3.3 in <cite data-cite=\"Start1997\">(Start1997)</cite>, Ch. 4.1.3 in <cite data-cite=\"Firtha2019\">(Firtha2019)</cite> and <cite data-cite=\"Firtha2017\">(Firtha2017)</cite> for the theory." |
| 9 | + "This notebook illustrates the usage of the SFS toolbox for the simulation of different 2.5D WFS referencing schemes.\n", |
| 10 | + "A dedicated referencing scheme allows correct amplitude alongside a reference contour within the listening area.\n", |
| 11 | + "For the theory please check\n", |
| 12 | + "Ch 3.1-3.3 in <cite data-cite=\"Start1997\">(Start1997)</cite>,\n", |
| 13 | + "Ch. 4.1.3 in <cite data-cite=\"Firtha2019\">(Firtha2019)</cite> and\n", |
| 14 | + "<cite data-cite=\"Firtha2017\">(Firtha2017)</cite>." |
12 | 15 | ] |
13 | 16 | }, |
14 | 17 | { |
|
35 | 38 | "metadata": {}, |
36 | 39 | "outputs": [], |
37 | 40 | "source": [ |
38 | | - "R = 1.5 # Radius of circular loudspeaker array, in m\n", |
| 41 | + "R = 1.5 # radius [m] of circular loudspeaker array\n", |
39 | 42 | "array = sfs.array.circular(N=64, R=R) # with N loudspeakers\n", |
40 | 43 | "grid = sfs.util.xyz_grid([-2, 2], [-2, 2], 0, spacing=0.02)\n", |
41 | 44 | "\n", |
42 | | - "xs = -4, 0, 0 # point source on negative x-axis\n", |
| 45 | + "xs = -4, 0, 0 # virtual point source on negative x-axis\n", |
43 | 46 | "wavelength = 1 / 4 # m" |
44 | 47 | ] |
45 | 48 | }, |
|
74 | 77 | "outputs": [], |
75 | 78 | "source": [ |
76 | 79 | "xs = sfs.util.asarray_of_rows(xs)\n", |
77 | | - "f = sfs.default.c / wavelength # Hz\n", |
78 | | - "omega = 2 * np.pi * f # rad/s\n", |
| 80 | + "frequency = sfs.default.c / wavelength # Hz\n", |
| 81 | + "omega = 2 * np.pi * frequency # rad/s\n", |
79 | 82 | "normalize_gain = 4 * np.pi * np.linalg.norm(xs)" |
80 | 83 | ] |
81 | 84 | }, |
|
94 | 97 | "metadata": {}, |
95 | 98 | "outputs": [], |
96 | 99 | "source": [ |
97 | | - "# reference contour is a straight line\n", |
| 100 | + "# reference contour is a straight line on y-axis\n", |
98 | 101 | "xref_line = 0\n", |
99 | 102 | "# calc reference contour xref(x0), cf. [Firtha2017, eq. (24), (31), (52)]\n", |
100 | 103 | "# this code assumes virtual point source on x-axis\n", |
|
112 | 115 | "cell_type": "markdown", |
113 | 116 | "metadata": {}, |
114 | 117 | "source": [ |
115 | | - "The level plot includes a white 0 dB isobar curve, which nicely demonstrates the intended amplitude correct synthesis. Note that this isobar is not perfectly aligned along the intended reference contour (orange-like dots) due to diffraction artifacts, which cannot really be compensated." |
| 118 | + "The level plot includes a white 0 dB isobar curve.\n", |
| 119 | + "The orange-like dots represent the stationary phase points at which amplitude correct synthesis is to be expected.\n", |
| 120 | + "These dots shape the line reference contour.\n", |
| 121 | + "Note that the isobar curve is not perfectly aligned along line reference contour due to diffraction artifacts." |
116 | 122 | ] |
117 | 123 | }, |
118 | 124 | { |
|
121 | 127 | "source": [ |
122 | 128 | "### Circle as reference contour\n", |
123 | 129 | "\n", |
124 | | - "This reference contour is a circle with origin xs and a radius, such that the origin is on this circle. This contour is obtained with straightforward vector calculus." |
| 130 | + "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." |
125 | 131 | ] |
126 | 132 | }, |
127 | 133 | { |
|
130 | 136 | "metadata": {}, |
131 | 137 | "outputs": [], |
132 | 138 | "source": [ |
133 | | - "# reference contour is a circle with origin xs\n", |
| 139 | + "# reference contour is a circle with origin xs and radius |xs|\n", |
134 | 140 | "xref_dist = np.linalg.norm(xs)\n", |
135 | 141 | "# calc reference contour xref(x0), cf. [Firtha19, eq. (24), (31)]\n", |
136 | 142 | "x0xs = array.x - xs\n", |
|
148 | 154 | "cell_type": "markdown", |
149 | 155 | "metadata": {}, |
150 | 156 | "source": [ |
151 | | - "The (complex-valued) difference between the two sound fields tells us about the difference of both approaches in terms of amplitude." |
| 157 | + "The (complex-valued) difference between the two sound fields (linear vs. circular reference contour) tells us about the difference of both approaches in terms of amplitude." |
152 | 158 | ] |
153 | 159 | }, |
154 | 160 | { |
|
166 | 172 | "cell_type": "markdown", |
167 | 173 | "metadata": {}, |
168 | 174 | "source": [ |
169 | | - "The (complex-valued) ratio between the two sound fields tells us about the difference of both approaches in terms of dB. Note, that again a white 0 dB isobar curve is overlaid as contour plot." |
| 175 | + "The (complex-valued) ratio between the two sound fields (linear vs. circular reference contour) tells us about the difference of both approaches in terms of dB. Note, that again a white 0 dB isobar curve is overlaid as contour plot." |
170 | 176 | ] |
171 | 177 | }, |
172 | 178 | { |
|
195 | 201 | "source": [ |
196 | 202 | "The default handling in\n", |
197 | 203 | "`point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None)`\n", |
198 | | - "uses just a reference point xref, and more specifically this point is in the origin.\n", |
199 | | - "This single point, the virtual source position and the loudspeaker array then determine the reference contour without further user access to it.\n", |
| 204 | + "uses just a reference point xref, and more specifically this default point is the origin of the coordinate system.\n", |
| 205 | + "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", |
200 | 206 | "This handling is chosen due to convenience and practical relevance using circular loudspeaker arrays.\n", |
201 | 207 | "The example below shows the resulting reference contour for the default case.\n", |
202 | | - "In the example it looks similar to the line referencing contour, but is in general not exactly the same." |
| 208 | + "In the example it looks similar to the line reference contour, but is in general not exactly the same." |
203 | 209 | ] |
204 | 210 | }, |
205 | 211 | { |
|
212 | 218 | "normalize_gain = 4 * np.pi * np.linalg.norm(xs - np.array(xref))\n", |
213 | 219 | "d, selection, secondary_source = sfs.fd.wfs.point_25d(\n", |
214 | 220 | " omega, array.x, array.n, xs, xref=xref)\n", |
215 | | - "p_circ = sound_field(d * normalize_gain, np.array([xref]*array.x.shape[0]),\n", |
216 | | - " selection, secondary_source, array, grid)" |
| 221 | + "p_point = sound_field(d * normalize_gain, np.array([xref]*array.x.shape[0]),\n", |
| 222 | + " selection, secondary_source, array, grid)" |
217 | 223 | ] |
218 | 224 | }, |
219 | 225 | { |
220 | 226 | "cell_type": "markdown", |
221 | 227 | "metadata": {}, |
222 | 228 | "source": [ |
223 | 229 | "From an academic viewpoint, the reference point handling in most cases will fail, as it is rather unlikely to set up the loudspeaker array and the virtual source and the reference point, such that the latter exactly fulfills the stationary phase requirement with one of the active loudspeakers.\n", |
224 | | - "\n", |
225 | 230 | "In the example above this worked, but only because it was intentionally chosen to do so.\n", |
226 | 231 | "\n", |
227 | | - "The example below shows the failing case. The point xref does not match any ray (gray lines) alongside the stationary phase holds with its corresponding loudspeaker. \n", |
228 | | - "The obtained reference level at xref point is however close to 0 dB, as the reference contour (white 0 dB isobar) is very very close to it.\n", |
229 | | - "So, in practice this handling will likely work with sufficient precision." |
| 232 | + "The example below shows a point xref that does not meet any ray (gray lines) alongside the stationary phase holds with its corresponding loudspeaker.\n", |
| 233 | + "The obtained level at the xref point however closely matches the desired reference level, because the white 0 dB isobar curve is very close to it.\n", |
| 234 | + "So, in practice this single point reference scheme will likely work with sufficient precision once the position of xref is appropriately chosen (i.e. not too close, not too far, not to off-center etc.)." |
230 | 235 | ] |
231 | 236 | }, |
232 | 237 | { |
|
241 | 246 | "normalize_gain = 4 * np.pi * np.linalg.norm(xs - np.array(xref))\n", |
242 | 247 | "d, selection, secondary_source = sfs.fd.wfs.point_25d(\n", |
243 | 248 | " omega, array.x, array.n, xs, xref=xref)\n", |
244 | | - "p_circ = sound_field(d * normalize_gain, np.array([xref]*array.x.shape[0]),\n", |
245 | | - " selection, secondary_source, array, grid)\n", |
| 249 | + "p_point = sound_field(d * normalize_gain, np.array([xref]*array.x.shape[0]),\n", |
| 250 | + " selection, secondary_source, array, grid)\n", |
246 | 251 | "\n", |
247 | | - "spa_unit_path = array.x - xs # path along the stationary phase holds\n", |
| 252 | + "spa_unit_path = array.x - xs # path along which the stationary phase holds\n", |
248 | 253 | "spa_unit_path /= np.linalg.norm(spa_unit_path, axis=1, keepdims=1)\n", |
249 | 254 | "spa = array.x + R * spa_unit_path\n", |
250 | 255 | "for spa1, spa2 in zip(array.x[selection, :2], spa[selection, :2]):\n", |
|
256 | 261 | "cell_type": "markdown", |
257 | 262 | "metadata": {}, |
258 | 263 | "source": [ |
259 | | - "A plane wave like sound field, e.g. by setting `xs = -100, 0, 0`, for all above examples reveals some more implications of the different referencing schemes." |
| 264 | + "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." |
260 | 265 | ] |
261 | 266 | } |
262 | 267 | ], |
|
0 commit comments