├── LICENSE ├── README.md ├── TEST_ARRAY_SIMULATOR.m ├── cylModalCoeffs.m ├── cylindricalScatterer.m ├── dbesselj.m ├── dbessely.m ├── dhankel1.m ├── dhankel2.m ├── dsph_besselj.m ├── dsph_bessely.m ├── dsph_function.m ├── dsph_hankel1.m ├── dsph_hankel2.m ├── getArrayResponse.m ├── simulateCylArray.m ├── simulateSphArray.m ├── sphModalCoeffs.m ├── sph_besselj.m ├── sph_bessely.m ├── sph_function.m ├── sph_hankel1.m ├── sph_hankel2.m └── sphericalScatterer.m /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Archontis Politis 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Array-Response-Simulator nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sensor Array Response Simulator 2 | 3 | #### A library that simulates array responses for sensors with arbitrary geometry and directional characteristics. 4 | 5 | --- 6 | > 7 | > Archontis Politis, 2015 8 | > 9 | > Department of Signal Processing and Acoustics, Aalto University, Finland 10 | > 11 | > archontis.politis@aalto.fi 12 | > 13 | --- 14 | 15 | This Matlab/Octave library was developed during my doctoral research in the [Communication Acoustics Research Group] (http://spa.aalto.fi/en/research/research_groups/communication_acoustics/), Aalto University, Finland. If you would like to reference the code, you can refer to my dissertation published [here](https://aaltodoc.aalto.fi/handle/123456789/22499): 16 | 17 | Archontis Politis, Microphone array processing for parametric spatial audio techniques, 2016 18 | Doctoral Dissertation, Department of Signal Processing and Acoustics, Aalto University, Finland 19 | 20 | ## Description 21 | 22 | This is a collection of MATLAB routines for simulation of array responses of 23 | 24 | a) directional sensors and, 25 | 26 | b) sensors mounted on, or at a distance from, a rigid spherical/cylindrical 27 | scatterer. 28 | 29 | The computation of their frequency and impulse responses is 30 | based on the theoretical expansion of a scalar incident plane wave field to 31 | a series of wavenumber-dependent Bessel-family functions and 32 | direction-dependent Fourier or Legendre functions. 33 | 34 | A function for arbitrary open arrays of directional microphones is included 35 | not based on the expansion but directly on the steering vector formula of 36 | inter-sensor delays and sensor gains for directional patterns 37 | (e.g. arrays of cardioid microphones). 38 | 39 | Most of the functionality of the library is displayed at [http://research.spa.aalto.fi/projects/arraysim-lib/arraysim.html], 40 | or in the included script TEST_ARRAY_SIMULATOR.m 41 | 42 | For more information on the expansions, you can have a look on 43 | 44 | Earl G. Williams, "Fourier Acoustics: Sound Radiation and Nearfield 45 | Acoustical Holography", Academic Press, 1999 46 | 47 | Heinz Teutsch, "Modal Array Signal Processing: Principles and 48 | Applications of Acoustic Wavefield Decomposition", Springer, 2007 49 | 50 | and for example on 51 | [http://en.wikipedia.org/wiki/Plane_wave_expansion] and 52 | [http://en.wikipedia.org/wiki/Jacobi-Anger_expansion] 53 | 54 | For any questions, comments, corrections, or general feedback, please 55 | contact archontis.politis@aalto.fi 56 | 57 | --- 58 | 59 | For more details on using functions, check their help output in Matlab. 60 | 61 | ### List of MATLAB files: 62 | 63 | * sph_besselj.m : Various spherical Bessel-family functions and their 64 | * sph_bessely.m : derivatives, for the computation of the radial 65 | * sph_function.m : terms of the expansions (modal weights) 66 | * sph_hankel1.m 67 | * sph_hankel2.m 68 | * dbesselj.m 69 | * dbessely.m 70 | * dhankel1.m 71 | * dhankel2.m 72 | * dsph_besselj.m 73 | * dsph_bessely.m 74 | * dsph_function.m 75 | * dsph_hankel1.m 76 | * dsph_hankel2.m 77 | 78 | * simulateCylArray.m : Simulate cylindrical open arrays or arrays of sensors mounted on a rigid cylinder 79 | * simulateSphArray.m : Simulate spherical open arrays or arrays of sensors mounted on a rigid sphere 80 | * cylModalCoeffs.m : Compute the radial (modal) weights for a cylindrical scatterer 81 | * sphModalCoeffs.m : Compute the radial (modal) weights for a spherical scatterer 82 | 83 | * sphericalScatterer.m : Compute the response for a measurement point at an arbitrary distance from a rigid spherical scatterer 84 | * cylindricalScatterer.m : Compute the response for a measurement point at an arbitrary distance from a rigid cylindrical scatterer 85 | 86 | * getArrayResponse.m' : Simulate open arrays of arbitrary directional microphones with axisymmetric responses 87 | -------------------------------------------------------------------------------- /TEST_ARRAY_SIMULATOR.m: -------------------------------------------------------------------------------- 1 | %% SENSOR ARRAY RESPONSE SIMULATOR 2 | 3 | %% 4 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | % 6 | % Archontis Politis, 2015 7 | % Department of Signal Processing and Acoustics, Aalto University, Finland 8 | % archontis.politis@aalto.fi 9 | % 10 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 11 | 12 | %% 13 | % This is a collection of MATLAB routines for simulation of array responses of 14 | % 15 | % a) directional sensors and, 16 | % 17 | % b) sensors mounted on, or at a distance from, a rigid spherical/cylindrical 18 | % scatterer. 19 | % 20 | % The computation of their frequency and impulse responses is 21 | % based on the theoretical expansion of a scalar incident plane wave field to 22 | % a series of wavenumber-dependent Bessel-family functions and 23 | % direction-dependent Fourier or Legendre functions. 24 | % 25 | % Alternatively, a function for arbitrary open arrays of directional microphones 26 | % is included, not based on the expansion but directly on the steering vector 27 | % formula of inter-sensor delays and sensor gains for user-defined directional 28 | % patterns (e.g. arrays of cardioid microphones). 29 | % 30 | % For more information on the expansions, you can have a look on [ref.1] 31 | % and [ref.2] 32 | % 33 | % and for example on 34 | % and 35 | % 36 | % 37 | % For any questions, comments, corrections, or general feedback, please 38 | % contact archontis.politis@aalto.fi 39 | 40 | %% ARRAYS OF OMNIDIRECTIONAL MICROPHONES 41 | % 42 | % This is the simplest case and the array responses can be computed 43 | % either by simulateCylArray() for 2D, simulateSphArray() for 3D, or 44 | % getArrayResponse() for both. Since getArrayResponse does not involve a 45 | % spherical or cylindrical expansion, it is the easiest to use in this 46 | % case. 47 | 48 | % a uniform circular array of 5 elements at 10cm radius 49 | mic_azi = (0:360/5:360-360/5)'*pi/180; 50 | mic_elev = zeros(size(mic_azi)); 51 | R = 0.1; 52 | [R_mic(:,1), R_mic(:,2), R_mic(:,3)] = sph2cart(mic_azi, mic_elev, R); 53 | 54 | % plane wave direcitons to evaluate response 55 | doa_azi = (0:5:355)'*pi/180; 56 | doa_elev = zeros(size(doa_azi)); 57 | [U_doa(:,1), U_doa(:,2), U_doa(:,3)] = sph2cart(doa_azi, doa_elev, 1); 58 | 59 | % impulse response parameters 60 | fs = 40000; 61 | Lfilt = 2000; 62 | % simulate array using getArrayResponse() 63 | fDirectivity = @(angle) 1; % response of omnidirectional microphone 64 | [h_mic1, H_mic1] = getArrayResponse(U_doa, R_mic, [], fDirectivity, Lfilt, fs); % microphone orientation irrelevant in this case 65 | 66 | % simulate array using 2D propagation and cylindrical expansion 67 | N_order = 40; % order of expansion 68 | [h_mic2, H_mic2] = simulateCylArray(Lfilt, mic_azi, doa_azi, 'open', R, N_order, fs); 69 | 70 | % simulate array using 3D propagation and spherical expansion 71 | [h_mic3, H_mic3] = simulateSphArray(Lfilt, [mic_azi mic_elev], [doa_azi doa_elev], 'open', R, N_order, fs); 72 | 73 | % Plots 74 | Nfft = Lfilt; 75 | f = (0:Nfft/2)*fs/Nfft; 76 | 77 | figure 78 | subplot(231) 79 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic1(:,1,:))))) 80 | view(2) 81 | shading flat 82 | set(gca,'yscale','log') 83 | axis([0 355 50 20000]) 84 | caxis([-20 10]), colorbar, colormap(jet) 85 | ylabel('Frequency (Hz)') 86 | xlabel('Azimuth (radians)') 87 | title('Direct evaluation, magnitude') 88 | subplot(234) 89 | surf(doa_azi*180/pi, f, angle(squeeze(H_mic1(:,1,:)))) 90 | view(2) 91 | shading flat 92 | set(gca,'yscale','log') 93 | axis([0 355 50 20000]) 94 | colorbar 95 | xlabel('Azimuth (radians)') 96 | title('Direct evluation, phase') 97 | subplot(232) 98 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic2(:,1,:))))) 99 | view(2) 100 | shading flat 101 | set(gca,'yscale','log') 102 | axis([0 355 50 20000]) 103 | caxis([-20 10]), colorbar, colormap(jet) 104 | ylabel('Frequency (Hz)') 105 | xlabel('Azimuth (radians)') 106 | title('Circular expansion, magnitude') 107 | subplot(235) 108 | surf(doa_azi*180/pi, f, angle(squeeze(H_mic2(:,1,:)))) 109 | view(2) 110 | shading flat 111 | set(gca,'yscale','log') 112 | axis([0 355 50 20000]) 113 | colorbar 114 | xlabel('Azimuth (radians)') 115 | title('Circular expansion, phase') 116 | subplot(233) 117 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic3(:,1,:))))) 118 | view(2) 119 | shading flat 120 | set(gca,'yscale','log') 121 | axis([0 355 50 20000]) 122 | caxis([-20 10]), colorbar, colormap(jet) 123 | ylabel('Frequency (Hz)') 124 | xlabel('Azimuth (radians)') 125 | title('Spherical expansion, magnitude') 126 | subplot(236) 127 | surf(doa_azi*180/pi, f, angle(squeeze(H_mic3(:,1,:)))) 128 | view(2) 129 | shading flat 130 | set(gca,'yscale','log') 131 | axis([0 355 50 20000]) 132 | colorbar 133 | xlabel('Azimuth (radians)') 134 | title('Spherical expansion, phase') 135 | h = gcf; h.Position(3) = 2*h.Position(3); 136 | 137 | % plot the array impulse responses for all microphones and doa at 45deg 138 | figure 139 | plot(squeeze(h_mic1(:,:,45/5+1))) 140 | title('IRs, 5 element UCA of omnidirectional sensors, DOA at 45deg') 141 | 142 | 143 | %% ARRAY OF DIRECTIONAL MICROPHONES 144 | % 145 | % Simulate the same case but for first-order directional microphones 146 | % instead of omnidirectional ones. These can be evaluated using the 147 | % getArrayResponse() or by spherical expansion using simulateSphArray(). 148 | % At the moment circular expansion of directional microphones is not 149 | % included, and since there are two ways already to compute, it's probably 150 | % not needed. 151 | 152 | % first-order directivity coefficient, a + (1-a)cos(theta) 153 | % for a = 1, same as the previous omnidirectional case above 154 | a = 0.5; % cardioid response 155 | 156 | % simulate array using getArrayResponse() 157 | fDirectivity = @(angle) a + (1-a)*cos(angle); % response for cardioid microphone 158 | [h_mic1, H_mic1] = getArrayResponse(U_doa, R_mic, [], fDirectivity, Lfilt, fs); % microphone orientation radial 159 | 160 | % simulate array using spherical expansion 161 | arrayType = 'directional'; 162 | [h_mic2, H_mic2] = simulateSphArray(Lfilt, [mic_azi mic_elev], [doa_azi doa_elev], arrayType, R, N_order, fs, a); 163 | 164 | % Plots 165 | figure 166 | subplot(221) 167 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic1(:,1,:))))) 168 | view(2) 169 | shading flat 170 | set(gca,'yscale','log') 171 | axis([0 355 50 20000]) 172 | caxis([-20 10]), colorbar, colormap(jet) 173 | ylabel('Frequency (Hz)') 174 | xlabel('Azimuth (radians)') 175 | title('Direct evaluation, magnitude') 176 | subplot(223) 177 | surf(doa_azi*180/pi, f, angle(squeeze(H_mic1(:,1,:)))) 178 | view(2) 179 | shading flat 180 | set(gca,'yscale','log') 181 | axis([0 355 50 20000]) 182 | colorbar 183 | xlabel('Azimuth (radians)') 184 | title('Direct evluation, phase') 185 | subplot(222) 186 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic2(:,1,:))))) 187 | view(2) 188 | shading flat 189 | set(gca,'yscale','log') 190 | axis([0 355 50 20000]) 191 | caxis([-20 10]), colorbar, colormap(jet) 192 | ylabel('Frequency (Hz)') 193 | xlabel('Azimuth (radians)') 194 | title('Spherical expansion, magnitude') 195 | subplot(224) 196 | surf(doa_azi*180/pi, f, angle(squeeze(H_mic2(:,1,:)))) 197 | view(2) 198 | shading flat 199 | set(gca,'yscale','log') 200 | axis([0 355 50 20000]) 201 | colorbar 202 | xlabel('Azimuth (radians)') 203 | title('Spherical expansion, phase') 204 | h = gcf; h.Position(3) = 2*h.Position(3); 205 | 206 | % plot the array impulse responses for all microphones and doa at 45deg 207 | figure 208 | plot(squeeze(h_mic1(:,:,45/5+1))) 209 | title('IRs, 5 element UCA of cardioid sensors, DOA at 45deg') 210 | 211 | %% ARRAY OF HIGHER ORDER DIRECTIONAL MICROPHONES 212 | % 213 | % Higher-order directional patterns can be simulated using getArrayResponse(). 214 | % Appropriate functions should be defined, and different patterns can be 215 | % passed for each element of the array. Not that this approach will 216 | % ignore the frequency dependency that occurs in practice with the 217 | % acoustical processing generating these higher-order patterns. For 218 | % proper response modeling in such cases, one can simulate the response 219 | % of the elements of the subarray, and apply the associated processing for 220 | % pattern generation to the resulting IRs (see e.g. [ref.3]). 221 | 222 | % define an array of two differential patterns, one 2nd-order supercardioid 223 | % and a 3rd-order hypercardioid, oriented at front and at 10cm distance 224 | % between (weights from [ref.3]) 225 | a2_scard = [(3 - sqrt(7))/4; (3*sqrt(7) - 7)/2; (15 - (5*sqrt(7)))/4]; 226 | a3_hcard = [-3/32; -15/32; 15/32; 35/32]; 227 | fDir1 = @(angle) a2_scard(1) + a2_scard(2)*cos(angle) + a2_scard(3)*cos(angle).^2; 228 | fDir2 = @(angle) a3_hcard(1) + a3_hcard(2)*cos(angle) + a3_hcard(3)*cos(angle).^2 + a3_hcard(4)*cos(angle).^3; 229 | fDirHandles{1} = fDir1; 230 | fDirHandles{2} = fDir2; 231 | R_mic = 0.5*[0 1 0; 0 -1 0]; 232 | U_orient = [1 0 0; 1 0 0]; 233 | 234 | % simulate array using getArrayResponse() 235 | [~, H_mic] = getArrayResponse(U_doa, R_mic, U_orient, fDirHandles, Lfilt, fs); 236 | 237 | % Plots 238 | figure 239 | subplot(221) 240 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic(:,1,:))))) 241 | view(2) 242 | shading flat 243 | set(gca,'yscale','log') 244 | axis([0 355 50 20000]) 245 | caxis([-40 10]), colorbar, colormap(jet) 246 | ylabel('Frequency (Hz)') 247 | xlabel('Azimuth (radians)') 248 | title('2nd-order supercardioid, magnitude') 249 | subplot(223) 250 | surf(doa_azi*180/pi, f, angle(squeeze(H_mic(:,1,:)))) 251 | view(2) 252 | shading flat 253 | set(gca,'yscale','log') 254 | axis([0 355 50 20000]) 255 | colorbar 256 | xlabel('Azimuth (radians)') 257 | title('2nd-order supercardioid, phase') 258 | subplot(222) 259 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic(:,2,:))))) 260 | view(2) 261 | shading flat 262 | set(gca,'yscale','log') 263 | axis([0 355 50 20000]) 264 | caxis([-40 10]), colorbar, colormap(jet) 265 | ylabel('Frequency (Hz)') 266 | xlabel('Azimuth (radians)') 267 | title('3rd-order hypercardioid, magnitude') 268 | subplot(224) 269 | surf(doa_azi*180/pi, f, angle(squeeze(H_mic(:,2,:)))) 270 | view(2) 271 | shading flat 272 | set(gca,'yscale','log') 273 | axis([0 355 50 20000]) 274 | colorbar 275 | xlabel('Azimuth (radians)') 276 | title('3rd-order hypercardioid, phase') 277 | h = gcf; h.Position(3) = 2*h.Position(3); 278 | 279 | %% ARRAY OF MICROPHONES ON A CYLINDRICAL/SPHERICAL SCATTERER 280 | % 281 | % Microphones mounted on a spherical scatterer (for 3D processing), or on 282 | % a cylindrical scatterer (for 2D processing) exhibit some advantages over 283 | % open arrays of omnidirectional microphones, mainly due to the inherent 284 | % directionality of the scattering body, and the fact that they are more 285 | % suitable for working in the spatial Fourier transform domain (also 286 | % known as eigenbeam processing, or phase-mode processing in the 287 | % literature) in which case the open array exhibits resonant frequencies 288 | % for which the transformed signals vanish. 289 | % 290 | % The response of a plane wave scattered by these fundamental geometries 291 | % is known analytically, and it involves the special functions and their 292 | % derivatives, included in the library. 293 | 294 | % Simulate a single microphone mounted on a cylinder and on a sphere of radius 5cm 295 | R = 0.05; 296 | arrayType = 'rigid'; 297 | N_order = 40; % order of expansion 298 | mic_azi = 0; 299 | mic_dir = [0 0]; 300 | 301 | % Compute simulated impulse responses 302 | doa_azi = (0:5:355)'*pi/180; 303 | doa_dirs = [doa_azi zeros(size(doa_azi))]; 304 | [~, H_mic_cyl] = simulateCylArray(Lfilt, mic_azi, doa_azi, arrayType, R, N_order, fs); 305 | [~, H_mic_sph] = simulateSphArray(Lfilt, mic_dir, doa_dirs, arrayType, R, N_order, fs); 306 | 307 | % plots 308 | figure 309 | subplot(121) 310 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic_sph(:,1,:))))) 311 | view(2) 312 | shading flat 313 | set(gca,'yscale','log') 314 | axis([0 355 50 20000]) 315 | caxis([-20 10]), colorbar, colormap(jet) 316 | ylabel('Frequency (Hz)') 317 | xlabel('Azimuth (radians)') 318 | title('Spherical Array, mic\_dir = [0 0], R = 5cm') 319 | subplot(122) 320 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic_cyl(:,1,:))))) 321 | view(2) 322 | shading flat 323 | set(gca,'yscale','log') 324 | axis([0 355 50 20000]) 325 | caxis([-20 10]), colorbar, colormap(jet) 326 | xlabel('Azimuth (radians)') 327 | title('Cylindrical Array, mic\_dir = [0 0], R = 5cm') 328 | h = gcf; h.Position(3) = 2*h.Position(3); 329 | 330 | %% ARRAY OF MICROPHONES AT A DISTANCE FROM A CYLINDRICAL/SPHERICAL SCATTERER 331 | % 332 | % Apart from the functions that evaluate the responses on the surface of a 333 | % scatterer, functions are included that evaluate the response at some 334 | % distance away from the scatterer, for which the effect gradually 335 | % diminishes with distance. This case may be useful for example in simulating 336 | % arrays that place microphones at different radii for broadband eigenbeam 337 | % processing, or for simulating a hearing aid array from some small 338 | % distance of a spherical head. 339 | 340 | % Simulate a microphone mounted on a cylinder and on a sphere of radius 341 | % 5cm, and a second microphone at 5cm distance from the scatterer 342 | R = 0.05; 343 | N_order = 40; % order of expansion 344 | mic_azi = 0; 345 | mic_elev = 0; 346 | mic_dirs_sph = [mic_azi mic_elev R; mic_azi mic_elev 2*R]; 347 | mic_dirs_cyl = [mic_azi R; mic_azi 2*R]; 348 | 349 | % Compute simulated impulse responses 350 | doa_azi = (0:5:355)'*pi/180; 351 | doa_dirs = [doa_azi zeros(size(doa_azi))]; 352 | 353 | [~, H_mic_cyl] = cylindricalScatterer(mic_dirs_cyl, doa_azi, R, N_order, Lfilt, fs); 354 | [~, H_mic_sph] = sphericalScatterer(mic_dirs_sph, doa_dirs, R, N_order, Lfilt, fs); 355 | 356 | % plots 357 | figure 358 | subplot(221) 359 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic_sph(:,1,:))))) 360 | view(2) 361 | shading flat 362 | set(gca,'yscale','log') 363 | axis([0 355 50 20000]) 364 | caxis([-20 10]), colorbar, colormap(jet) 365 | ylabel('Frequency (Hz)') 366 | xlabel('Azimuth (radians)') 367 | title('Spherical Array, mic\_dir = [0 0], R_{scat} = 5cm, r_{mic} = 5cm') 368 | subplot(222) 369 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic_cyl(:,1,:))))) 370 | view(2) 371 | shading flat 372 | set(gca,'yscale','log') 373 | axis([0 355 50 20000]) 374 | caxis([-20 10]), colorbar, colormap(jet) 375 | xlabel('Azimuth (radians)') 376 | title('Cylindrical Array, mic\_dir = [0 0], R_{scat} = 5cm, r_{mic} = 5cm') 377 | subplot(223) 378 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic_sph(:,2,:))))) 379 | view(2) 380 | shading flat 381 | set(gca,'yscale','log') 382 | axis([0 355 50 20000]) 383 | caxis([-20 10]), colorbar, colormap(jet) 384 | ylabel('Frequency (Hz)') 385 | xlabel('Azimuth (radians)') 386 | title('Spherical Array, mic\_dir = [0 0], R_{scat} = 5cm, r_{mic} = 10cm') 387 | subplot(224) 388 | surf(doa_azi*180/pi, f, 20*log10(abs(squeeze(H_mic_cyl(:,2,:))))) 389 | view(2) 390 | shading flat 391 | set(gca,'yscale','log') 392 | axis([0 355 50 20000]) 393 | caxis([-20 10]), colorbar, colormap(jet) 394 | xlabel('Azimuth (radians)') 395 | title('Cylindrical Array, mic\_dir = [0 0], R_{scat} = 5cm, r_{mic} = 10cm') 396 | h = gcf; h.Position(3:4) = 2*h.Position(3:4); 397 | 398 | %% 3D UNIFORM SPHERICAL ARRAY EXAMPLE 399 | % Simulate a uniform spherical array with the specifications of the Eigenmike 400 | % array, suitable for up to 4th-order eigenbeam processing 401 | 402 | % Eigenmike angles, in [azimuth, elevation] form 403 | mic_dirs = ... 404 | [0 21; 405 | 32 0; 406 | 0 -21; 407 | 328 0; 408 | 0 58; 409 | 45 35; 410 | 69 0; 411 | 45 -35; 412 | 0 -58; 413 | 315 -35; 414 | 291 0; 415 | 315 35; 416 | 91 69; 417 | 90 32; 418 | 90 -31; 419 | 89 -69; 420 | 180 21; 421 | 212 0; 422 | 180 -21; 423 | 148 0; 424 | 180 58; 425 | 225 35; 426 | 249 0; 427 | 225 -35; 428 | 180 -58; 429 | 135 -35; 430 | 111 0; 431 | 135 35; 432 | 269 69; 433 | 270 32; 434 | 270 -32; 435 | 271 -69]; 436 | mic_dirs_rad = mic_dirs*pi/180; 437 | 438 | % Eigenmike radius 439 | R = 0.042; 440 | 441 | % Type and order of approximation 442 | arrayType = 'rigid'; 443 | N_order = 30; 444 | 445 | % Obtain responses for front, side and up DOAs 446 | src_dirs_rad = [0 0; pi 0; 0 pi/2]; 447 | [h_mic, H_mic] = simulateSphArray(Lfilt, mic_dirs_rad, src_dirs_rad, arrayType, R, N_order, fs); 448 | 449 | %% REFERENCES 450 | % 451 | % [1] Earl G. Williams, "Fourier Acoustics: Sound Radiation and Nearfield 452 | % Acoustical Holography", Academic Press, 1999 453 | % 454 | % [2] Heinz Teutsch, "Modal Array Signal Processing: Principles and 455 | % Applications of Acoustic Wavefield Decomposition", Springer, 2007 456 | % 457 | % [3] Elko, W. Gary, "Differential Microphone Arrays", 458 | % In Y. Huang & J. Benesty (Eds.), Audio signal processing for 459 | % next-generation multimedia communication systems (pp. 11?65). 460 | % Springer, 2004 461 | % -------------------------------------------------------------------------------- /cylModalCoeffs.m: -------------------------------------------------------------------------------- 1 | function b_N = cylModalCoeffs(N, kr, arrayType) 2 | %CYLMODALCOEFFS Modal coefficients for rigid or open cylindrical array 3 | % 4 | % N: maximum order 5 | % kr: wavenumber-radius product 6 | % arrayType: {'open','rigid'} open for open array of omnidirectional 7 | % sensors, rigid for sensors mounted on a rigid baffle 8 | % 9 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 10 | % 11 | % CYLMODALCOEFFS.M - 15/7/2013 12 | % Archontis Politis, archontis.politis@aalto.fi 13 | % 14 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | 16 | b_N = zeros(length(kr), N+1); 17 | 18 | for n=0:N 19 | 20 | if (isequal(arrayType, 'open')) 21 | b_N(:, n+1) = 1i^n * besselj(n, kr); 22 | 23 | elseif (isequal(arrayType,'rigid')) 24 | jn = besselj(n, kr); 25 | jnprime = dbesselj(n, kr); 26 | hn = besselh(n, 2, kr); 27 | hnprime = dhankel2(n, kr); 28 | 29 | temp = 1i^n * (jn-(jnprime./hnprime).*hn); 30 | if n==0 31 | temp(kr==0) = 1; 32 | else 33 | temp(kr==0) = 0; 34 | end 35 | b_N(:, n+1) = temp; 36 | 37 | else 38 | error('Wrong array type') 39 | end 40 | end 41 | 42 | % Avoid NaNs for very high orders, instead of very small values 43 | b_N(isnan(b_N)) = 0; 44 | -------------------------------------------------------------------------------- /cylindricalScatterer.m: -------------------------------------------------------------------------------- 1 | function [h_mic, H_mic] = cylindricalScatterer(mic_dirs_rad, src_azis_rad, R, N_order, N_filt, fs) 2 | %CYLINDRICALSCATTERER Compute the pressure due to a spherical scatterer 3 | % CYLINDRICALSCATTERER computes the impulse responses of the pressure 4 | % measured at some points in the field with a cylindrical rigid scatterer 5 | % centered at the origin and due to incident plane waves. 6 | % 7 | % mic_dirs_rad = [mic_azi1 mic_dist1; ...] directions and 8 | % distances of the measurement positions (in rads, 9 | % meters) 10 | % 11 | % src_azi_rad = [src_azi1; ...] DOAs of plane waves for evaluation (in rads) 12 | % 13 | % R = scatterer radius in m 14 | % N_order = maximum order of spherical approximation 15 | % fs = sample rate 16 | % 17 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 18 | % 19 | % CYLINDRICALSCATTERER.M - 12/5/2014 20 | % Archontis Politis, archontis.politis@aalto.fi 21 | % 22 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 23 | 24 | % Compute the frequency-dependent part of the microphone responses (radial 25 | % dependence) 26 | f = (0:N_filt/2)'*fs/N_filt; 27 | c = 343; 28 | kR = 2*pi*f*R/c; 29 | N_mic = size(mic_dirs_rad,1); 30 | N_pw = size(src_azis_rad,1); 31 | if any(mic_dirs_rad(:,2) 6 68 | error('Too many input arguments.') 69 | end 70 | 71 | Nmics = size(R_mic,1); 72 | if size(R_mic, 2) ~= 3 73 | error(['Size of R_mic should be Nmicsx3, where the columns are the cartesian ' ... 74 | 'coordinates of the vectors']) 75 | end 76 | 77 | Ndoa = size(U_doa,1); 78 | if size(U_doa, 2) ~= 3 79 | error(['Size of U_src should be Ndoax3, where the columns are the cartesian ' ... 80 | 'coordinates of the vectors']) 81 | end 82 | 83 | if ~exist('fs','var') || isempty(fs) 84 | fs = 48000; 85 | end 86 | 87 | % if no directivity coefficient is defined assume omnidirectional sensors 88 | if ~exist('fDir_handle','var') || isempty(fDir_handle) 89 | fDir_handle = @(angle) 1; 90 | end 91 | if length(fDir_handle) == 1 92 | if iscell(fDir_handle) 93 | fDir_handle = repmat(fDir_handle,1,Nmics); 94 | elseif isa(fDir_handle,'function_handle') 95 | fDir_handle = {fDir_handle}; 96 | fDir_handle = repmat(fDir_handle,1,Nmics); 97 | end 98 | elseif length(fDir_handle) ~= Nmics 99 | error('Size of fDir_handle should be 1xNmics') 100 | end 101 | 102 | % compute unit vectors of the microphone positions 103 | normR_mic = sqrt(sum(R_mic.^2,2)); 104 | U_mic = R_mic./(normR_mic*ones(1,3)); 105 | 106 | % if no orientation is defined then assume that the microphones are 107 | % oriented radially, similar to U_mic 108 | if ~exist('U_orient','var') || isempty(U_orient) 109 | U_orient = U_mic; 110 | elseif size(U_orient, 2) ~= 3 111 | error(['Size of U_orient should be 1x3, if all the micorphones are oriented '... 112 | 'on the same direction, or Nmicsx3, where the columns are the cartesian '... 113 | 'coordinates of the vectors']) 114 | elseif isrow(U_orient) 115 | U_orient = U_orient*ones(Nmics,1); 116 | elseif size(U_orient, 1) ~= Nmics 117 | error(['The number of rows of the orientation vectors should be equal '... 118 | 'to the number of microphones']) 119 | end 120 | 121 | 122 | %%%%%%%%%% COMPUTATIONS %%%%%%%%%% 123 | 124 | % speed of sound 125 | c = 343; 126 | % frequency vector 127 | Nfft = Lfilt; 128 | f = (0:Nfft/2+1)*fs/Nfft; 129 | K = Nfft/2+1; 130 | 131 | % unit vectors pointing to the evaluation points 132 | U_eval = zeros(Ndoa, Nmics, 3); 133 | U_eval(:,:,1) = U_doa(:,1) * ones(1, Nmics); 134 | U_eval(:,:,2) = U_doa(:,2) * ones(1, Nmics); 135 | U_eval(:,:,3) = U_doa(:,3) * ones(1, Nmics); 136 | 137 | % computation of time delays and attenuation for each evaluation point to 138 | % microphone, measured from the origin 139 | tempR_mic(:,:,1) = ones(Ndoa, 1) * R_mic(:,1)'; 140 | tempR_mic(:,:,2) = ones(Ndoa, 1) * R_mic(:,2)'; 141 | tempR_mic(:,:,3) = ones(Ndoa, 1) * R_mic(:,3)'; 142 | 143 | tempU_orient(:,:,1) = ones(Ndoa, 1) * U_orient(:,1)'; 144 | tempU_orient(:,:,2) = ones(Ndoa, 1) * U_orient(:,2)'; 145 | tempU_orient(:,:,3) = ones(Ndoa, 1) * U_orient(:,3)'; 146 | 147 | % cos-angles between DOAs and sensor orientations 148 | cosAngleU = dot(U_eval, tempU_orient, 3); 149 | % d*cos-angles between DOAs and sensor positions 150 | dcosAngleU = dot(U_eval, tempR_mic, 3); 151 | 152 | % attenuation due to directionality of the sensors 153 | B = zeros(Ndoa, Nmics); 154 | for nm=1:Nmics 155 | B(:,nm) = fDir_handle{nm}(acos(cosAngleU(:,nm))); 156 | end 157 | 158 | % create TFs for each microphone 159 | micTFs = zeros(K, Nmics, Ndoa); 160 | for kk = 1:K 161 | omega = 2*pi*f(kk); 162 | tempTF = B .* exp(1i*(omega/c)*dcosAngleU); 163 | micTFs(kk,:,:) = tempTF.'; 164 | end 165 | 166 | % create IRs for each microphone 167 | micIRs = zeros(Nfft, Nmics, Ndoa); 168 | for nd = 1:Ndoa 169 | tempTF = micTFs(:,:,nd); 170 | tempTF(end,:) = abs(tempTF(end,:)); 171 | tempTF = [tempTF; conj(tempTF(end-1:-1:2,:))]; 172 | micIRs(:,:,nd) = ifft(tempTF); 173 | micIRs(:,:,nd) = fftshift(micIRs(:,:,nd), 1); 174 | end 175 | -------------------------------------------------------------------------------- /simulateCylArray.m: -------------------------------------------------------------------------------- 1 | function [h_mic, H_mic] = simulateCylArray(N_filt, mic_dirs_rad, src_dirs_rad, arrayType, R, N_order, fs) 2 | %SIMULATECYLARRAY Simulate the impulse responses of a cylindrical array. 3 | % 4 | % SIMULATECYLARRAY computes the impulse responses of the microphones of a 5 | % cylindrical microphone array for the given directions of incident plane 6 | % waves. The array type can be either 'open' for omnidirectional 7 | % microphones in an open setup, or 'rigid' for omnidirectional 8 | % microphones mounted on a cylinder. 9 | % 10 | % mic_dirs_rad : [mic_azi1; ...] directions of microphone capsules 11 | % 12 | % src_dirs_rad : [src_azi1; ...] DOAs of plane waves for evaluation 13 | % 14 | % arrayType : 'open' or 'rigid' 15 | % R : array radius in m 16 | % N_order : maximum order of cylindrical expansion 17 | % fs : sample rate 18 | % 19 | % 20 | % H_mic: Output the frequency responses for M directions for the N 21 | % microphones. Only half the FFT spectrum is returned (up to Nyquist). 22 | % Last dimension is the DOA. 23 | % h_mic: Output the IRs for M directions for the N microphones. Last 24 | % dimension is the DOA. 25 | % 26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 27 | % 28 | % SIMULATECYLARRAY.M - 15/7/2013 29 | % Archontis Politis, archontis.politis@aalto.fi 30 | % 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | 33 | % Compute the radial part of the microphone responses 34 | f = (0:N_filt/2)'*fs/N_filt; 35 | c = 343; 36 | kR = 2*pi*f*R/c; 37 | b_N = cylModalCoeffs(N_order, kR, arrayType); 38 | %handle Nyquist for real impulse response 39 | temp = b_N; 40 | temp(end,:) = real(temp(end,:)); 41 | b_Nt = fftshift(ifft([temp; conj(temp(end-1:-1:2,:))]), 1); 42 | 43 | % Compute angular-dependent part of the microphone responses 44 | % unit vectors of DOAs and microphones 45 | N_doa = size(src_dirs_rad,1); 46 | N_mic = size(mic_dirs_rad,1); 47 | h_mic = zeros(N_filt, N_mic, N_doa); 48 | H_mic = zeros(N_filt/2+1, N_mic, N_doa); 49 | for i=1:N_doa 50 | angle = mic_dirs_rad - src_dirs_rad(i); 51 | 52 | C = zeros(N_order+1, N_mic); 53 | for n=0:N_order 54 | % Jacobi-Anger expansion 55 | if n==0 56 | C(n+1,:) = ones(size(angle)); 57 | else 58 | C(n+1,:) = 2*cos(n*angle); 59 | end 60 | end 61 | h_mic(:,:,i) = b_Nt * C; 62 | 63 | if nargout>1 64 | H_mic(:,:,i) = b_N * C; 65 | end 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /simulateSphArray.m: -------------------------------------------------------------------------------- 1 | function [h_mic, H_mic] = simulateSphArray(N_filt, mic_dirs_rad, src_dirs_rad, arrayType, R, N_order, fs, dirCoeff) 2 | %SIMULATESPHARRAY Simulate the impulse responses of a spherical array. 3 | % 4 | % SIMULATESPHARRAY computes the impulse responses of the microphones of a 5 | % spherical microphone array for the given directions of incident plane 6 | % waves. The array type can be either 'open' for omnidirectional 7 | % microphones in an open setup, or 'rigid' for omnidirectional 8 | % microphones mounted on a sphere. 9 | % 10 | % mic_dirs_rad : [mic_azi1 mic_elev1; ...] directions of microphone 11 | % capsules 12 | % 13 | % src_dirs_rad : [src_azi1 src_elev1; ...] DOAs of plane waves for 14 | % evaluation 15 | % 16 | % arrayType : 'open', 'rigid', or 'directional' 17 | % R : array radius in m 18 | % N_order : maximum order of spherical approximation 19 | % fs : sample rate 20 | % dirCoeff : if array consists of directional microphones, then 21 | % dirCoeff is the directivity coefficient from 0 to 1 (1 22 | % for omni, 0.5 cardioid, 0 dipole) 23 | % 24 | % 25 | % H_mic: Output the frequency responses for M directions for the N 26 | % microphones. Only half the FFT spectrum is returned (up to Nyquist). 27 | % Last dimension is the DOA. 28 | % h_mic: Output the IRs for M directions for the N microphones. Last 29 | % dimension is the DOA. 30 | % 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | % 33 | % SIMULATESPHARRAY.M - 15/7/2013 34 | % Archontis Politis, archontis.politis@aalto.fi 35 | % 36 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 37 | 38 | if nargin<8 39 | dirCoeff = []; 40 | end 41 | 42 | % Compute the frequency-dependent part of the microphone responses (radial 43 | % dependence) 44 | f = (0:N_filt/2)'*fs/N_filt; 45 | c = 343; 46 | kR = 2*pi*f*R/c; 47 | b_N = sphModalCoeffs(N_order, kR, arrayType, dirCoeff); 48 | %handle Nyquist for real impulse response 49 | temp = b_N; 50 | temp(end,:) = real(temp(end,:)); 51 | b_Nt = fftshift(ifft([temp; conj(temp(end-1:-1:2,:))]), 1); 52 | 53 | % Compute angular-dependent part of the microphone responses 54 | % unit vectors of DOAs and microphones 55 | N_doa = size(src_dirs_rad,1); 56 | N_mic = size(mic_dirs_rad,1); 57 | [U_mic(:,1), U_mic(:,2), U_mic(:,3)] = sph2cart(mic_dirs_rad(:,1), mic_dirs_rad(:,2), 1); 58 | [U_doa(:,1), U_doa(:,2), U_doa(:,3)] = sph2cart(src_dirs_rad(:,1), src_dirs_rad(:,2), 1); 59 | h_mic = zeros(N_filt, N_mic, N_doa); 60 | H_mic = zeros(N_filt/2+1, N_mic, N_doa); 61 | for i=1:N_doa 62 | cosangle = dot(U_mic, ones(N_mic,1)*U_doa(i,:), 2); 63 | 64 | P = zeros(N_order+1, N_mic); 65 | for n=0:N_order 66 | % The Legendre polynomial gives the angular dependency 67 | Pn = legendre(n, cosangle); 68 | P(n+1,:) = (2*n+1)/(4*pi) * Pn(1,:); 69 | end 70 | h_mic(:,:,i) = b_Nt * P; 71 | 72 | if nargout>1 73 | H_mic(:,:,i) = b_N * P; 74 | end 75 | end 76 | 77 | end 78 | -------------------------------------------------------------------------------- /sphModalCoeffs.m: -------------------------------------------------------------------------------- 1 | function b_N = sphModalCoeffs(N, kr, arrayType, dirCoeff) 2 | %SPHMODALCOEFFS Modal coefficients for rigid or open spherical array 3 | % 4 | % N: maximum order 5 | % kr: wavenumber-radius product 6 | % arrayType: {'open','rigid','directional'} open for open array of 7 | % omnidirectional sensors, rigid for sensors mounted on a 8 | % rigid baffle, directional for an array of first-order 9 | % directional microphones determined by the dirCoeff 10 | % dirCoeff: relevant only in the 'directional' type, dirCoeff ranges 11 | % from 0 (omni) to 1 (dipole), where for example 0.5 is a 12 | % cardioid sensor. In the 0 case it is equivalent to an open 13 | % array of omnis. The first order directivity function is 14 | % defined as d(theta) = dirCoeff + (1-dirCoeff)*cos(theta) 15 | % 16 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 17 | % 18 | % SPHMODALCOEFFS.M - 15/7/2013, updated 12/2/2013 19 | % Archontis Politis, archontis.politis@aalto.fi 20 | % 21 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 22 | 23 | b_N = zeros(length(kr), N+1); 24 | 25 | for n=0:N 26 | 27 | if (isequal(arrayType, 'open')) 28 | b_N(:, n+1) = 4*pi*1i^n * sph_besselj(n, kr); 29 | 30 | elseif (isequal(arrayType,'rigid')) 31 | 32 | jn = sph_besselj(n, kr); 33 | jnprime = dsph_besselj(n, kr); 34 | hn = sph_hankel2(n, kr); 35 | hnprime = dsph_hankel2(n, kr); 36 | 37 | temp = 4*pi*1i^n * (jn-(jnprime./hnprime).*hn); 38 | if n==0 39 | temp(kr==0) = 4*pi; 40 | else 41 | temp(kr==0) = 0; 42 | end 43 | b_N(:, n+1) = temp; 44 | 45 | elseif (isequal(arrayType,'directional')) 46 | 47 | jn = sph_besselj(n, kr); 48 | jnprime = dsph_besselj(n, kr); 49 | 50 | temp = 4*pi*1i^n * (dirCoeff*jn - 1i*(1-dirCoeff)*jnprime); 51 | b_N(:, n+1) = temp; 52 | 53 | else 54 | error('Wrong array type') 55 | end 56 | end 57 | 58 | % Avoid NaNs for very high orders, instead of very small values 59 | b_N(isnan(b_N)) = 0; 60 | -------------------------------------------------------------------------------- /sph_besselj.m: -------------------------------------------------------------------------------- 1 | function j = sph_besselj(n, x) 2 | %SPH_BESSELJ Spherical bessel function of the first kind. 3 | % 4 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | % 6 | % SPH_BESSELJ.M - 15/7/2013 7 | % Archontis Politis, archontis.politis@aalto.fi 8 | % 9 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 10 | 11 | % find zeros in the argument 12 | idx_zero = (x==0); 13 | 14 | j = sqrt(pi./(2*x)).*besselj(n+0.5, x); 15 | if n==0 16 | j(idx_zero) = 1; 17 | else 18 | j(idx_zero) = 0; 19 | end 20 | 21 | end 22 | 23 | -------------------------------------------------------------------------------- /sph_bessely.m: -------------------------------------------------------------------------------- 1 | function j = sph_bessely(n, x) 2 | %SPH_BESSELY Spherical bessel function of the second kind. 3 | % 4 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | % 6 | % SPH_BESSELY.M - 15/7/2013 7 | % Archontis Politis, archontis.politis@aalto.fi 8 | % 9 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 10 | 11 | j = sqrt(pi./(2*x)).*bessely(n+0.5, x); 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /sph_function.m: -------------------------------------------------------------------------------- 1 | function f_x = sph_function(n, x, funcName) 2 | %SPH_FUNCTION Spherical Bessel and Hankel function 3 | 4 | switch funcName 5 | case 'besselj' 6 | if n==0 7 | f_x = sinc(x/pi); 8 | else 9 | idx_zero = (x==0); 10 | f_x = sqrt(pi./(2*x)).*besselj(n+0.5, x); 11 | f_x(idx_zero) = 0; 12 | end 13 | 14 | case 'bessely' 15 | f_x = sqrt(pi./(2*x)).*bessely(n+0.5, x); 16 | if any(x==0) 17 | warning('Zero argument for the Bessel function of the second kind results to -Inf') 18 | end 19 | 20 | case 'hankel1' 21 | f_x = sqrt(pi./(2*x)).*besselh(n+0.5, 1, x); 22 | 23 | case 'hankel2' 24 | f_x = sqrt(pi./(2*x)).*besselh(n+0.5, 2, x); 25 | end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /sph_hankel1.m: -------------------------------------------------------------------------------- 1 | function j = sph_hankel1(n, x) 2 | %SPH_HANKEL1 Spherical hankel function of the first kind. 3 | % 4 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | % 6 | % SPH_HANKEL1.M - 15/7/2013 7 | % Archontis Politis, archontis.politis@aalto.fi 8 | % 9 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 10 | 11 | j = sph_besselj(n,x) + 1i*sph_bessely(n,x); 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /sph_hankel2.m: -------------------------------------------------------------------------------- 1 | function j = sph_hankel2(n, x) 2 | %SPH_HANKEL2 Spherical hankel function of the second kind. 3 | % 4 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | % 6 | % SPH_HANKEL2.M - 15/7/2013 7 | % Archontis Politis, archontis.politis@aalto.fi 8 | % 9 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 10 | 11 | j = sph_besselj(n,x) - 1i*sph_bessely(n,x); 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /sphericalScatterer.m: -------------------------------------------------------------------------------- 1 | function [h_mic, H_mic] = sphericalScatterer(mic_dirs_rad, src_dirs_rad, R, N_order, N_filt, fs) 2 | %SPHERICALSCATTERER Compute the pressure due to a spherical scatterer 3 | % SPHERICALSCATTERER computes the impulse responses of the pressure 4 | % measured at some points in the field with a spherical rigid scatterer 5 | % centered at the origin and due to incident plane waves. 6 | % 7 | % mic_dirs_rad = [mic_azi1 mic_elev1 mic_dist1; ...] directions and 8 | % distances of the measurement positions (in rads, 9 | % meters) 10 | % 11 | % src_dirs_rad = [src_azi1 src_elev1; ...] DOAs of plane waves for 12 | % evaluation (in rads) 13 | % 14 | % R = scatterer radius in m 15 | % N_order = maximum order of spherical approximation 16 | % fs = sample rate 17 | % 18 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 19 | % 20 | % SPHERICALSCATTERER.M - 12/5/2014 21 | % Archontis Politis, archontis.politis@aalto.fi 22 | % 23 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 24 | 25 | % Compute the frequency-dependent part of the microphone responses (radial 26 | % dependence) 27 | f = (0:N_filt/2)'*fs/N_filt; 28 | c = 343; 29 | kR = 2*pi*f*R/c; 30 | N_mic = size(mic_dirs_rad,1); 31 | N_pw = size(src_dirs_rad,1); 32 | if any(mic_dirs_rad(:,3)