diff --git a/Signal Processing/Array Processing/MATLAB/Functions/arrMan.m b/Signal Processing/Array Processing/MATLAB/Functions/arrMan.m index afe1a47cf0fa64d1ec34f0e7cbb142024182794c..2fc9ba6d5302d0da04b6042bea690ed59c5f9b72 100644 --- a/Signal Processing/Array Processing/MATLAB/Functions/arrMan.m +++ b/Signal Processing/Array Processing/MATLAB/Functions/arrMan.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 28/05/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -13,12 +13,12 @@ % Input % % mPos [numeric]: The positions of the sensors in Cartesian coordinates. -% This must be an Mx3 matrix where M denotes the number of +% This must be an 3xM matrix where M denotes the number of % sensors and for each sensor the coordinates are in -% [x, y, z] format. +% [x; y; z] format. % % dirs [numeric]: These are the directions for which the array manifold -% will be calculated. This must be an Nx2 matrix where N is +% will be calculated. This must be an 2xN matrix where N is % the number of directions and for each direction the % azimuth and elevation/inclination is given. The values % must be provided in radians. The directions must be @@ -53,18 +53,18 @@ function am = arrMan(mPos, dirs, k) % Validate input arguments % ==================================================== % Validate mandatory arguments - validateattributes(mPos, "numeric", {'2d', 'finite', 'nonnan', 'nonempty', 'real', 'ncols', 3}, mfilename, "Position of sensors", 1); - validateattributes(dirs, "numeric", {'2d', 'finite', 'nonnan', 'nonempty', 'real', 'ncols', 2}, mfilename, "Directions of interest", 2); + validateattributes(mPos, "numeric", {'2d', 'finite', 'nonnan', 'nonempty', 'real', 'nrows', 3}, mfilename, "Position of sensors", 1); + validateattributes(dirs, "numeric", {'2d', 'finite', 'nonnan', 'nonempty', 'real', 'nrows', 2}, mfilename, "Directions of interest", 2); validateattributes(k, "numeric", {'vector', 'finite', 'nonnan', 'nonempty', 'nonnegative', 'real'}, mfilename, "Wavenumbers of interest", 3); % ============================================= % Calculate the array manifold % ============================================= % Get Cartesian coordinates of directions - [x, y, z] = sph2cart(dirs(:, 1), dirs(:, 2), ones(size(dirs(:, 1)))); + [x, y, z] = sph2cart(dirs(1, :), dirs(2, :), ones(size(dirs(1, :)))); % Inner product of directions and sensor position - am = mPos * [x, y, z].'; + am = [x; y; z] * mPos; % Multiply with wavenumbers am = am .* permute(k(:), [3, 2, 1]); diff --git a/Signal Processing/Array Processing/MATLAB/Functions/firstOrderDMA.m b/Signal Processing/Array Processing/MATLAB/Functions/firstOrderDMA.m index 5906331f103a370a91619b6f117f52ef06352491..fdcd0781e759d54e9c7f976eb29338ba224bf55b 100644 --- a/Signal Processing/Array Processing/MATLAB/Functions/firstOrderDMA.m +++ b/Signal Processing/Array Processing/MATLAB/Functions/firstOrderDMA.m @@ -3,73 +3,69 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 05/11/2023 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- -% Functionality: Calculate the output of a first order -% Differential Microphone Array. +% Functionality: Calculate the output of a first order Differential +% Microphone Array. % -------------------------------------------------- % Input % % input [numeric]: The input to the array. 3D array/matrix with dimensions -% [M x measurements x frequency], where M represents the -% number of microphones and must be even. Each pair is -% treated as a first order DMA. "Measurements" are the -% observations (or measurements) made with the array and -% "frequency" is the number of frequencies of interest. +% IxMxF, where I is the number of measurments (or length +% of signals), M represents the number of microphones and +% must be even and F is the number of frequencies of +% interest. Each pair microphone is treated as a first +% order DMA. % -% freq [numeric]: The frequencies of interest. A 1D vector with number of -% elements matching the third dimension of "input" +% freq [numeric]: The frequencies of interest. A vector with number of +% elements matching the third dimension of the "input" % parameter. % -% posVec [numeric]: The position vectors of the DMA elements. This must be -% a 3x2 matrix whose rows will represent the Cartesian -% coordinates and the columns the DMA elements. +% pos [numeric]: The position vectors of the DMA elements. This must be a +% 3xM matrix whose rows will represent the Cartesian +% coordinates and the columns the DMA elements. % -% polarPattern [string/char/numeric] (Optional): The sought out -% beam-pattern. It can be -% either a string (or -% character cell) from one -% of the following (not case -% sensitive): -% - Omni, Omnidirectional, -% Monopole -% - Dipole, Figure-of-Eight -% - Cardioid -% - Hypercardioid -% - Supercardioid -% It can also be a numeric -% value representing the -% angle for which the -% response is specified -% (input parameter -% "betaParam") in degrees. -% [Default: Dipole] +% pPattern [string/char/numeric] (Optional): The sought out beam-pattern. +% It can be either a string (or +% character cell) from one of +% the following (not case +% sensitive): +% - Omni, Omnidirectional, +% Monopole +% - Dipole, Figure-of-Eight +% - Cardioid +% - Hypercardioid +% - Supercardioid +% It can also be a numeric value +% representing the angle for +% which the response is +% specified (input parameter +% "beta") in degrees. +% [Default: Dipole] % -% betaParam [numeric] (Optional): The (normalised to unity) response at -% the angle specified with teh parameter -% "polarPattern". If "polarPattern" is -% given as string/char the appropriate -% value is automatically set, and -% "betaParam" is ignored. [Default: 0] +% beta [numeric] (Optional): The (normalised to unity) response at the +% angle specified with teh parameter "pPattern". +% If "pPattern" is given as string/char the +% appropriate value is automatically set, and +% this argument is ignored. [Default: 0] % % -------------------------------------------------- % Output % % output [numeric]: The output of the array. It has the same dimensions as -% the "input" parameter except for the first dimension -% which is equal to M/2 where M is the number of -% microphone elements provided. +% the "input" parameter except for the second dimension +% which is equal to M/2. % -% h [numeric]: This [2 x F] filter is the filter that results in the +% h [numeric]: This 2xF filter is the filter that results in the % beam-pattern of interest and F is the number of frequencies. % % -------------------------------------------------- % Notes % % -------------------------------------------------- -function [output, h] = firstOrderDMA(input, freq, d, polarPattern, betaParam) +function [output, h] = firstOrderDMA(input, freq, d, pPattern, beta) % ==================================================== % Check for number of arguments % ==================================================== @@ -86,8 +82,8 @@ function [output, h] = firstOrderDMA(input, freq, d, polarPattern, betaParam) % ==================================================== validateattributes(input, {'numeric'}, {'3d', 'nonnan', 'finite', 'nonempty'}, mfilename, 'Input', 1); - if mod(size(input, 1), 2) ~= 0 - error("First dimension of 'input' parameter must have even length.") + if mod(size(input, 2), 2) ~= 0 + error("Second dimension of 'input' parameter must have even length.") end validateattributes(freq, {'numeric'}, {'real', 'nonnan', 'finite', 'nonempty', 'vector', 'numel', size(input, 3)}, mfilename, 'Frequencies', 2); @@ -95,20 +91,20 @@ function [output, h] = firstOrderDMA(input, freq, d, polarPattern, betaParam) % Check beta if nargin > 4 - validateattributes(betaParam, {'numeric'}, {'scalar', 'real', 'finite', 'nonempty', 'nonnan', '<=', 1, '>=', 0}, mfilename, "Beta", 5) + validateattributes(beta, {'numeric'}, {'scalar', 'real', 'finite', 'nonempty', 'nonnan', '<=', 1, '>=', 0}, mfilename, "Beta", 5) else - betaParam = 0; + beta = 0; end % Check alpha (angle of null) if nargin > 3 - if isstring(polarPattern) || ischar(polarPattern) - validateattributes(polarPattern, {'char', 'string'}, {'scalartext', 'nonempty'}, mfilename, 'Polar pattern', 4); - elseif isnumeric(polarPattern) - validateattributes(polarPattern, {'numeric'}, {'scalar', 'real', 'nonnan', 'finite', 'nonempty'}, mfilename, 'Angle of null', 4); + if isstring(pPattern) || ischar(pPattern) + validateattributes(pPattern, {'char', 'string'}, {'scalartext', 'nonempty'}, mfilename, 'Polar pattern', 4); + elseif isnumeric(pPattern) + validateattributes(pPattern, {'numeric'}, {'scalar', 'real', 'nonnan', 'finite', 'nonempty'}, mfilename, 'Angle of null', 4); end else - polarPattern = "dipole"; + pPattern = "dipole"; end @@ -125,35 +121,35 @@ function [output, h] = firstOrderDMA(input, freq, d, polarPattern, betaParam) % Calculate parameters % ==================================================== % Calculate the correct "null" angles based on given polar pattern - if ~isnumeric(polarPattern) - switch convertStringsToChars(lower(polarPattern)) + if ~isnumeric(pPattern) + switch convertStringsToChars(lower(pPattern)) case {'omni', 'omnidirectional', 'monopole'} - polarPattern = pi; - betaParam = 1; + pPattern = pi; + beta = 1; case {'dipole', 'figure-of-eight'} - polarPattern = pi/2; - betaParam = 0; + pPattern = pi/2; + beta = 0; case 'cardioid' - polarPattern = pi; - betaParam = 0; + pPattern = pi; + beta = 0; case 'hypercardioid' - polarPattern = (2 * pi/3); - betaParam = 0; + pPattern = (2 * pi/3); + beta = 0; case 'supercardioid' - polarPattern = (3 * pi/4); - betaParam = 0; + pPattern = (3 * pi/4); + beta = 0; otherwise error("Unsupported polar pattern"); end else - polarPattern = deg2rad(polarPattern); + pPattern = deg2rad(pPattern); end % Calculate filter(s) for freqIdx = length(freq):-1:1 - arrManMat = [arrMan(0, d, freq(freqIdx), 343), arrMan(polarPattern, d, freq(freqIdx), 343)]; - h(:, freqIdx) = (arrManMat')\[1; betaParam]; + arrManMat = [arrMan(0, d, freq(freqIdx), 343), arrMan(pPattern, d, freq(freqIdx), 343)]; + h(:, freqIdx) = (arrManMat')\[1; beta]; end @@ -164,7 +160,7 @@ function [output, h] = firstOrderDMA(input, freq, d, polarPattern, betaParam) for freqIdx = length(freq):-1:1 for pairIdx = size(input, 1)/2:-1:1 % Multiply the array filter with the input - output(pairIdx, :, freqIdx) = h(:, freqIdx)' * input(pairIdx * 2 - 1:pairIdx * 2, :, freqIdx); + output(:, pairIdx, freqIdx) = h(:, freqIdx)' * input(:, pairIdx * 2 - 1:pairIdx * 2, freqIdx); end end end diff --git a/Sound Fields/MATLAB/Functions/circHarm.m b/Sound Fields/MATLAB/Functions/circHarm.m index e7a4d035d602f20485e4efb662ab4c7af0b979e7..55820eb22946758d67a0a155dd7d1bf84f190289 100644 --- a/Sound Fields/MATLAB/Functions/circHarm.m +++ b/Sound Fields/MATLAB/Functions/circHarm.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 28/05/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -14,11 +14,11 @@ % Input % % dir [numeric]: The directions for which the spherical harmonics will be -% calculated. This must be an Mx2 matrix, where M is the +% calculated. This must be an 2xM matrix, where M is the % number of directions and 2 stands for the azimuth and % inclination. The angles must be given in radians. An % example of 2 directions would look like: -% [az1, incl1; az2, incl2]. +% [az1, az2; incl1, incl2]. % % N [numeric]: The highest order of the spherical harmonics to be % calculated. @@ -50,18 +50,18 @@ % Notes % % -------------------------------------------------- -function circHarms = circHarm(dirs, N) +function circHarms = circHarm(dirs, N, kr) % ==================================================== % Check for number of arguments % ==================================================== - narginchk(2, 2); + narginchk(2, 3); nargoutchk(0, 1); % ==================================================== % Validate input arguments % ==================================================== % Validate mandatory arguments - validateattributes(dirs, "numeric", {'2d', 'ncols', 2, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Directions", 1); + validateattributes(dirs, "numeric", {'2d', 'nrows', 2, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Directions", 1); validateattributes(N, "numeric", {'scalar', 'finite', 'nonnegative', 'nonnan', 'nonempty', 'real'}, mfilename, "Highest circular harmonics order", 2); % Validate optional arguments @@ -78,10 +78,10 @@ function circHarms = circHarm(dirs, N) n = -N:N; % Orders % Circular harmonics - circHarms = 1i.^(n) .* exp(-1i * n .* dirs(:, 1)); + circHarms = 1i.^(n) .* exp(-1i * n .* dirs(1, :).'); % "Scale" if ~isempty(kr) - circHarms = circHarms .* besselj(n .* ones(size(dirs, 1), 1), kr * sin(dirs(:, 2)) .* ones(1, length(n))); + circHarms = circHarms .* besselj(n .* ones(size(dirs, 2), 1), kr * sin(dirs(2, :).') .* ones(1, length(n))); end end \ No newline at end of file diff --git a/Sound Fields/MATLAB/Functions/planeWave.m b/Sound Fields/MATLAB/Functions/planeWave.m index 27610087f6e71f368070c88c30530f51acb09f1a..75029cb2eec4b6584b087c8389662869e86a8cd6 100644 --- a/Sound Fields/MATLAB/Functions/planeWave.m +++ b/Sound Fields/MATLAB/Functions/planeWave.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 30/07/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -13,16 +13,16 @@ % Input % % sDir [numeric]: The direction of incidence of the plane waves. This can -% be either an Nx2 matrix with N being the number of angles +% be either an 2xN matrix with N being the number of angles % of incidence in [azimuth, elevation] format, expressed in -% radians, or an Nx3 matrix with the entried corresponding +% radians, or an 3xN matrix with the entries corresponding % to the wavenumbers along each of the Cartesian axes. See % "notes" for more information. % % rPos [numeric]: The position of the receivers in Cartesian coordinates. -% The variable must be a matrix with dimensions Mx3 where -% M is the number of receivers and the columns represent -% the x, y and z coordinates respectively. +% The variable must be a matrix with dimensions 3xM where +% M is the number of receivers and the rows represent the +% x, y and z coordinates respectively. % % f [numeric]: The frequencies for which to calculate the wave field. It % must be a scalar or vector. @@ -65,14 +65,14 @@ function waveField = planeWave(sDir, rPos, f, Q, c) % Validate input arguments % ==================================================== % Validate mandatory arguments - validateattributes(sDir, "numeric", {'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Source positions", 1); + validateattributes(sDir, "numeric", {'2d', 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Source positions", 1); % Check form of sDir (directions or wavenumbers are provided) - if sum(size(sDir, 2) ~= 2) > 0 - error("The size of the directions matrix must be either Nx2 (incidence angles), or Nx3 (wavenumbers along the Cartesian axes)"); + if sum(size(sDir, 1) ~= [2, 3]) > 1 + error("The size of the directions matrix must be either 2xN (incidence angles), or 3xN (wavenumbers along the Cartesian axes)"); end - validateattributes(rPos, "numeric", {'2d', 'ncols', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2); + validateattributes(rPos, "numeric", {'2d', 'nrows', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2); validateattributes(f, "numeric", {'vector', 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Frequencies", 3); % Validate optional arguments @@ -80,7 +80,7 @@ function waveField = planeWave(sDir, rPos, f, Q, c) validateattributes(Q, "numeric", {'2d', 'finite', 'nonempty', 'nonnan'}, mfilename, "Source strength(s)", 5); % Make sure Q has the correct dimensions - if ~isscalar(Q) && sum((size(Q) ~= [size(sDir, 1), length(f)])) > 0 + if ~isscalar(Q) && sum((size(Q) ~= [size(sDir, 2), length(f)])) > 0 error("Source strength(s) must be either a scalar or its length must match the number of sources."); end else @@ -100,14 +100,14 @@ function waveField = planeWave(sDir, rPos, f, Q, c) f = f(:); % Calculate wavenumber from angles of indidence to wavenumber (if needed) - if size(sDir, 2) == 2 - [kx, ky, kz] = sph2cart(sDir(:, 1), sDir(:, 2), ones(size(sDir, 1), 1)); - k = [kx, ky, kz] .* (2 * pi * permute(f, [3, 2, 1])/c); + if size(sDir, 1) == 2 + [kx, ky, kz] = sph2cart(sDir(1, :), sDir(1, :), 1); + k = [kx; ky; kz] .* (2 * pi * permute(f, [3, 2, 1])/c); end % Create a source strength vector (if needed) if isscalar(Q) - Q = ones(size(k, 1), length(f)) * Q; + Q = Q * ones(size(k, 2), length(f)); end % ============================================= @@ -115,6 +115,6 @@ function waveField = planeWave(sDir, rPos, f, Q, c) % ============================================ % Go through the frequencies for fIdx = length(f):-1:1 - waveField(:, :, fIdx) = Q(:, fIdx).' .* exp(-1j * rPos * k(:, :, fIdx)); + waveField(:, :, fIdx) = Q(:, fIdx).' .* exp(-1j * rPos.' * k(:, :, fIdx)); end end \ No newline at end of file diff --git a/Sound Fields/MATLAB/Functions/planeWaveSH.m b/Sound Fields/MATLAB/Functions/planeWaveSH.m index 6970d96e3661dd5826ac09eff44f5324ade6a8fb..3dd6d06ad7d8a1407c6652ed921df4733ae30460 100644 --- a/Sound Fields/MATLAB/Functions/planeWaveSH.m +++ b/Sound Fields/MATLAB/Functions/planeWaveSH.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 02/06/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -13,15 +13,15 @@ % Input % % sDir [numeric]: The directions of the sources in spherical coordinates. -% The variable must be a matrix with dimensions Nx2 where N -% is the number of plane waves and the columns represent -% the azimuth and inclination respectively. The values must -% be in radians. +% The variable must be a matrix with dimensions 2xN where N +% is the number of plane waves and the rows represent the +% azimuth and inclination respectively. The values must be +% in radians. % % rPos [numeric]: The position of the receivers in Cartesian coordinates. -% The variable must be a matrix with dimensions Mx3 where -% M is the number of receivers and the columns represent -% the x, y and z coordinates respectively. +% The variable must be a matrix with dimensions 3xM where +% M is the number of receivers and the rows represent the +% x, y and z coordinates respectively. % % f [numeric]: The frequencies for which to calculate the wave field. It % must be a scalar or vector. @@ -67,9 +67,6 @@ % -------------------------------------------------- % Notes % -% - Dependencies: sphHarm() and idsft(). Both functions come from the -% Sound Field part of the IN-NOVA project codebase. -% % -------------------------------------------------- function [pCoeffs, waveFieldDecomp, waveField] = planeWaveSH(sDir, rPos, f, N, Q, c) % ==================================================== @@ -82,8 +79,8 @@ function [pCoeffs, waveFieldDecomp, waveField] = planeWaveSH(sDir, rPos, f, N, Q % Validate input arguments % ==================================================== % Validate mandatory arguments - validateattributes(sDir, "numeric", {'2d', 'ncols', 2, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Source positions", 1); - validateattributes(rPos, "numeric", {'2d', 'ncols', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2); + validateattributes(sDir, "numeric", {'2d', 'rows', 2, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Source positions", 1); + validateattributes(rPos, "numeric", {'2d', 'rows', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2); validateattributes(f, "numeric", {'vector', 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Frequencies", 3); validateattributes(N, "numeric", {'scalar', 'finite', 'nonempty', 'nonnan', 'real', 'nonnegative'}, mfilename, "Highest order of the generated soundfield", 4); @@ -92,7 +89,7 @@ function [pCoeffs, waveFieldDecomp, waveField] = planeWaveSH(sDir, rPos, f, N, Q validateattributes(Q, "numeric", {'2d', 'finite', 'nonempty', 'nonnan'}, mfilename, "Source strength(s)", 5); % Make sure Q has the correct dimensions - if ~isscalar(Q) && sum((size(Q) ~= [size(sDir, 1), length(f)])) > 0 + if ~isscalar(Q) && sum((size(Q) ~= [size(sDir, 2), length(f)])) > 0 error("Source strength(s) must be either a scalar or its length must match the number of sources."); end else @@ -111,13 +108,13 @@ function [pCoeffs, waveFieldDecomp, waveField] = planeWaveSH(sDir, rPos, f, N, Q % ==================================================== % Make sure the source strength has the correct dimensions if isscalar(Q) - Q = Q * ones(size(sDir, 1), length(f)); + Q = Q * ones(size(sDir, 2), length(f)); end % Convert coordinates to spherical coordinate system - sDir(:, 2) = (pi/2) - sDir(:, 2); + sDir(2, :) = (pi/2) - sDir(2, :); - [rAz, rEl, rR] = cart2sph(rPos(:, 1), rPos(:, 2), rPos(:, 3)); + [rAz, rEl, rR] = cart2sph(rPos(1, :), rPos(2, :), rPos(3, :)); rEl = (pi/2) - rEl; @@ -147,7 +144,7 @@ function [pCoeffs, waveFieldDecomp, waveField] = planeWaveSH(sDir, rPos, f, N, Q % Calculate the sound field if nargout > 1 - sh = sphHarm([rAz, rEl], N); + sh = sphHarm([rAz; rEl], N); for fIdx = numel(k):-1:1 for rIdx = length(rR):-1:1 diff --git a/Sound Fields/MATLAB/Functions/ptSrcField.m b/Sound Fields/MATLAB/Functions/ptSrcField.m index 5fcb60576ac413eb5c24ef4eee11c7c933300805..fd110c0307b3fff9be54d48233f3d77f1211cf25 100644 --- a/Sound Fields/MATLAB/Functions/ptSrcField.m +++ b/Sound Fields/MATLAB/Functions/ptSrcField.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 19/08/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -13,14 +13,14 @@ % Input % % sPos [numeric]: The position of the sources in Cartesian coordinates. The -% variable should be a matrix with dimensions Nx3 where N -% is the number of sources and the columns represent the x, -% y and z coordinates respectively. +% variable should be a matrix with dimensions 3xN where N +% is the number of sources and the rows represent the x, y +% and z coordinates respectively. % % rPos [numeric]: The position of the receivers in Cartesian coordinates. -% The variable should be a matrix with dimensions Mx3 where -% M is the number of receivers and the columns represent -% the x, y and z coordinates respectively. +% The variable should be a matrix with dimensions 3xM where +% M is the number of receivers and the rows represent the +% x, y and z coordinates respectively. % % freqs [numeric]: The frequencies for which to calculate the wave field. % It must be a scalar or vector. @@ -62,8 +62,8 @@ function waveField = ptSrcField(sPos, rPos, f, Q, c, rho) % Validate input arguments % ==================================================== % Validate mandatory arguments - validateattributes(sPos, "numeric", {'2d', 'ncols', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Source positions", 1); - validateattributes(rPos, "numeric", {'2d', 'ncols', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2); + validateattributes(sPos, "numeric", {'2d', 'nrows', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Source positions", 1); + validateattributes(rPos, "numeric", {'2d', 'nrows', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2); validateattributes(f, "numeric", {'vector', 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Frequencies", 3); % Validate optional arguments (and set their values) @@ -83,7 +83,7 @@ function waveField = ptSrcField(sPos, rPos, f, Q, c, rho) validateattributes(Q, "numeric", {'2d', 'finite', 'nonempty', 'nonnan'}, mfilename, "Source strength(s)", 4); % Make sure Q has the correct dimensions - if ~isscalar(Q) && sum((size(Q) ~= [size(sPos, 1), length(f)])) > 0 + if ~isscalar(Q) && sum((size(Q) ~= [size(sPos, 2), length(f)])) > 0 error("Source strength(s) must be either a scalar or its length must match the number of sources."); end else @@ -105,7 +105,7 @@ function waveField = ptSrcField(sPos, rPos, f, Q, c, rho) % Create a vector with appropriate dimensions for the source strength(s) if isscalar(Q) - Q = ones(size(sPos, 1), length(f)) * Q; + Q = ones(size(sPos, 2), length(f)) * Q; end diff --git a/Sound Fields/MATLAB/Functions/ptSrcFieldSH.m b/Sound Fields/MATLAB/Functions/ptSrcFieldSH.m index 49677637f98ab99f69384a6dc875c8bfff06af32..2b0c561a3069065426a9132562d546dd0c148ec7 100644 --- a/Sound Fields/MATLAB/Functions/ptSrcFieldSH.m +++ b/Sound Fields/MATLAB/Functions/ptSrcFieldSH.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 02/06/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -13,14 +13,14 @@ % Input % % sPos [numeric]: The position of the sources in Cartesian coordinates. The -% variable should be a matrix with dimensions Nx3 where N -% is the number of sources and the columns represent the x, -% y and z coordinates respectively. +% variable should be a matrix with dimensions 3xN where N +% is the number of sources and the rows represent the x, y +% and z coordinates respectively. % % rPos [numeric]: The position of the receivers in Cartesian coordinates. -% The variable should be a matrix with dimensions Mx3 where -% M is the number of receivers and the columns represent -% the x, y and z coordinates respectively. +% The variable should be a matrix with dimensions 3xM where +% M is the number of receivers and the rows represent the +% x, y and z coordinates respectively. % % f [numeric]: The frequencies for which to calculate the wave field. It % must be a scalar of vector. @@ -77,8 +77,8 @@ function [pCoeffs, waveFieldDecomp, waveField] = ptSrcFieldSH(sPos, rPos, f, N, % Validate input arguments % ==================================================== % Validate mandatory arguments - validateattributes(sPos, "numeric", {'2d', 'ncols', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Source positions", 1); - validateattributes(rPos, "numeric", {'2d', 'ncols', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2); + validateattributes(sPos, "numeric", {'2d', 'nrows', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Source positions", 1); + validateattributes(rPos, "numeric", {'2d', 'nrows', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2); validateattributes(f, "numeric", {'vector', 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Frequencies", 3); validateattributes(N, "numeric", {'scalar', 'finite', 'nonempty', 'nonnan', 'real', 'nonnegative'}, mfilename, "Highest order of the generated soundfield", 4); @@ -106,14 +106,14 @@ function [pCoeffs, waveFieldDecomp, waveField] = ptSrcFieldSH(sPos, rPos, f, N, % ==================================================== % Make sure the source strength has the correct dimensions if isscalar(Q) - Q = Q * ones(size(sPos, 1), length(f)); + Q = Q * ones(size(sPos, 2), length(f)); end % Convert coordinates to spherical coordinate system - [sAz, sEl, sR] = cart2sph(sPos(:, 1), sPos(:, 2), sPos(:, 3)); + [sAz, sEl, sR] = cart2sph(sPos(1, :), sPos(2, :), sPos(3, :)); sEl = (pi/2) - sEl; - [rAz, rEl, rR] = cart2sph(rPos(:, 1), rPos(:, 2), rPos(:, 3)); + [rAz, rEl, rR] = cart2sph(rPos(1, :), rPos(2, :), rPos(3, :)); rEl = (pi/2) - rEl; @@ -124,7 +124,7 @@ function [pCoeffs, waveFieldDecomp, waveField] = ptSrcFieldSH(sPos, rPos, f, N, k = 2 * pi * f/c; % Spherical harmonics corresponding to the directions of the sources - sh = sphHarm([sAz, sEl], N); + sh = sphHarm([sAz; sEl], N); % ============================================= @@ -153,7 +153,7 @@ function [pCoeffs, waveFieldDecomp, waveField] = ptSrcFieldSH(sPos, rPos, f, N, % Calculate the sound field components if nargout > 1 - sh = sphHarm([rAz, rEl], N); + sh = sphHarm([rAz; rEl], N); for fIdx = numel(k):-1:1 for rIdx = length(rR):-1:1 diff --git a/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m b/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m index 02e78266f83ce7d3bb9b5337886edc8c13b5338c..1dfeadf2020f962c3bba49d64be6fbc427bcb2c5 100644 --- a/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m +++ b/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 23/09/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -13,10 +13,10 @@ % Input % % sPos [numeric]: The Cartesian coordinates of the source positions. This -% must be an Nx3 matrix where N is the number of sources. +% must be an 3xN matrix where N is the number of sources. % % rPos [numeric]: The Cartesian coordinates of the receiver positions. This -% must be an Mx3 matrix where M is the number of receivers. +% must be an 3xM matrix where M is the number of receivers. % % fs [numeric]: The sampling frequency. This is required in order to % calculate the time-of-flight in samples and must be a @@ -94,18 +94,18 @@ function [rSig, rSigMean, rSigMtx, rSigMtxMean, Q] = ptSrcFieldTD(sPos, rPos, fs % Validate input arguments % ==================================================== % Validate mandatory arguments - validateattributes(sPos, "numeric", {'2d', 'real', 'nonnan', 'nonempty', 'finite', 'ncols', 3}, mfilename, "Cartesian coordinates of the source positions", 1); - validateattributes(rPos, "numeric", {'2d', 'real', 'nonnan', 'nonempty', 'finite', 'ncols', 3}, mfilename, "Cartesian coordinates of the receiver positions", 2); + validateattributes(sPos, "numeric", {'2d', 'real', 'nonnan', 'nonempty', 'finite', 'nrows', 3}, mfilename, "Cartesian coordinates of the source positions", 1); + validateattributes(rPos, "numeric", {'2d', 'real', 'nonnan', 'nonempty', 'finite', 'nrows', 3}, mfilename, "Cartesian coordinates of the receiver positions", 2); validateattributes(fs, "numeric", {'scalar', 'real', 'nonnan', 'nonempty', 'finite', 'positive'}, mfilename, "The sampling frequency", 3); % Validate optional arguments if nargin > 3 && ~isempty(Q) if isscalar(Q) validateattributes(Q, "numeric", {'scalar', 'real', 'nonnan', 'nonempty', 'finite', 'positive', 'integer'}, mfilename, "Length of source signals in samples", 4); - elseif isvector(Q) && size(Q, 2) == size(sPos, 1) - validateattributes(Q, "numeric", {'vector', 'real', 'nonnan', 'nonempty', 'finite', 'ncols', size(sPos, 1)}, mfilename, "Source signals", 4); + elseif isvector(Q) + validateattributes(Q, "numeric", {'vector', 'real', 'nonnan', 'nonempty', 'finite'}, mfilename, "Source signals", 4); else - validateattributes(Q, "numeric", {'3d', 'real', 'nonnan', 'nonempty', 'finite', 'ncols', size(sPos, 1)}, mfilename, "Source signals", 4); + validateattributes(Q, "numeric", {'3d', 'real', 'nonnan', 'nonempty', 'finite', 'ncols', size(sPos, 2)}, mfilename, "Source signals", 4); end else Q = 1; @@ -116,7 +116,7 @@ function [rSig, rSigMean, rSigMtx, rSigMtxMean, Q] = ptSrcFieldTD(sPos, rPos, fs else if ~isvector(Q) sigLen = size(Q, 1); - elseif isvector(Q) && size(Q, 2) == size(sPos, 1) + elseif isvector(Q) && size(sPos, 1) == 1 sigLen = length(Q); else sigLen = 128; @@ -144,7 +144,7 @@ function [rSig, rSigMean, rSigMtx, rSigMtxMean, Q] = ptSrcFieldTD(sPos, rPos, fs % ==================================================== % Generate source signals if isvector(Q) && length(Q) ~= sigLen - tmp = rand(sigLen, size(sPos, 1), nTrials); + tmp = rand(sigLen, size(sPos, 2), nTrials); tmp = tmp - mean(tmp); tmp = tmp./max(abs(tmp)); Q = Q(:).' .* tmp; diff --git a/Sound Fields/MATLAB/Functions/sphHarm.m b/Sound Fields/MATLAB/Functions/sphHarm.m index c365d8a12d3a1f42720da944cca6cede2c3a409b..11a315e73b818322008c45258b2da0d1a4cc0bf6 100644 --- a/Sound Fields/MATLAB/Functions/sphHarm.m +++ b/Sound Fields/MATLAB/Functions/sphHarm.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 26/05/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -14,11 +14,11 @@ % Input % % dir [numeric]: The directions for which the spherical harmonics will be -% calculated. This must be an Mx2 matrix, where M is the +% calculated. This must be an 2xM matrix, where M is the % number of directions and 2 stands for the azimuth and -% inclination. The angles must be given in radians. An +% elevation. The angles must be given in radians. An % example of 2 directions would look like: -% [az1, incl1; az2, incl2]. +% [az1, az2; el1, el2]. % % N [numeric]: The highest order of the spherical harmonics to be % calculated. @@ -97,7 +97,7 @@ function [cSphHarm, rSphHarm, cSphHarmMtx, rSphHarmMtx] = sphHarm(dirs, N) % Validate input arguments % ==================================================== % Validate mandatory arguments - validateattributes(dirs, "numeric", {'2d', 'ncols', 2, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Directions", 1); + validateattributes(dirs, "numeric", {'2d', 'nrows', 2, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Directions", 1); validateattributes(N, "numeric", {'scalar', 'finite', 'nonnegative', 'nonnan', 'nonempty', 'real'}, mfilename, "Highest spherical harmonics order", 2); @@ -113,10 +113,10 @@ function [cSphHarm, rSphHarm, cSphHarmMtx, rSphHarmMtx] = sphHarm(dirs, N) normFac = sqrt((2 * n + 1) * factorial(n - m)./(4 * pi * factorial(n + m))); % Legendre function - Lnm = legendre(n, cos(dirs(:, 2))).'; + Lnm = legendre(n, cos(dirs(2, :))).'; % Exponential function - Nnm = exp(1i * m .* dirs(:, 1)); + Nnm = exp(1i * m .* dirs(1, :).'); % Spherical harmonics tmpHarm = normFac .* Lnm .* Nnm; @@ -149,7 +149,7 @@ function [cSphHarm, rSphHarm, cSphHarmMtx, rSphHarmMtx] = sphHarm(dirs, N) % Complex spherical harmonics matrix if nargout > 2 % Pre-allocate - cSphHarmMtx = NaN * (1 + 1i) * zeros(N, length(-N:N), size(dirs, 1)); + cSphHarmMtx = NaN * (1 + 1i) * zeros(N, length(-N:N), size(dirs, 2)); % Go through the orders and degrees for n = N:-1:0 @@ -162,7 +162,7 @@ function [cSphHarm, rSphHarm, cSphHarmMtx, rSphHarmMtx] = sphHarm(dirs, N) % Real spherical harmonics matrix if nargout > 3 % Pre-allocate - rSphHarmMtx = NaN * zeros(N, length(-N:N), size(dirs, 1)); + rSphHarmMtx = NaN * zeros(N, length(-N:N), size(dirs, 2)); % Go through the orders and degrees for n = N:-1:0 diff --git a/Utilities/Generic/MATLAB/Functions/ptsOnSphere.m b/Utilities/Generic/MATLAB/Functions/ptsOnSphere.m index e40b7b7549e1140de40b4bf2a04de57e62dc76d3..525e637493094ff9a1aeda1158b1eed76c27821b 100755 --- a/Utilities/Generic/MATLAB/Functions/ptsOnSphere.m +++ b/Utilities/Generic/MATLAB/Functions/ptsOnSphere.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 23/02/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -38,8 +38,8 @@ % Output arguments % % pts [numeric]: The 3D vectors of the positions. The dimensions of the -% matrix is Nx3 where N is the number of sources and the -% columns represent the coordinates of the vectors [x,y,z]. +% matrix is 3xN where N is the number of sources and the +% rows represent the coordinates of the vectors [x,y,z]. % % -------------------------------------------------- % Acknowledgements @@ -112,10 +112,10 @@ function pts = ptsOnSphere(rad, param, method) % Convert to Cartesian [x, y, z] = sph2cart(azim, elev, repmat(rad, 1, totSrc)); - pts = [x(:), y(:), z(:)]; + pts = [x; y; z]; % Add two sources on the "poles" - pts = [0, 0, rad; pts; 0,0 , -rad]; + pts = cat(2, [0; 0; rad], pts, [0; 0; -rad]); else % Define some parameters gRatio = 1.61803398875; % Golden ratio @@ -132,11 +132,11 @@ function pts = ptsOnSphere(rad, param, method) azim = azim + (360 * azim < -180) - (360 * azim > 180); - pts(cnt, :) = [azim, elev]; + pts(:, cnt) = [azim; elev]; cnt = cnt + 1; end - [ptsX, ptsY, ptsZ] = sph2cart(deg2rad(pts(:, 1)), deg2rad(pts(:, 2)), rad); - pts = [ptsX, ptsY, ptsZ]; + [ptsX, ptsY, ptsZ] = sph2cart(deg2rad(pts(1, :)), deg2rad(pts(2, :)), rad); + pts = [ptsX; ptsY; ptsZ]; end end diff --git a/Utilities/Generic/MATLAB/Functions/twoPtDist.m b/Utilities/Generic/MATLAB/Functions/twoPtDist.m index 5523aaf919b2ac630800fc7550cd7815798e2353..084282b2ee386f81916f77635142e6082b95375b 100644 --- a/Utilities/Generic/MATLAB/Functions/twoPtDist.m +++ b/Utilities/Generic/MATLAB/Functions/twoPtDist.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 19/08/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -12,13 +12,13 @@ % Input arguments % % srcPos [numeric]: The 3D position vectors of the starting points. The -% rows represent points and the columns their Cartesian -% coordinates [x,y,z] resulting in an Nx3 matrix with N +% columns represent points and the rows their Cartesian +% coordinates [x,y,z] resulting in a 3xN matrix with N % being the number of points. % -% rcvPos [numeric]: The 3D position vectors of the end points. The rows -% represent points and the columns their Cartesian -% coordinates [x,y,z] resulting in an Mx3 matrix with M +% rcvPos [numeric]: The 3D position vectors of the end points. The columns +% represent points and the rows their Cartesian +% coordinates [x,y,z] resulting in an 3xM matrix with M % being the number of points. % % -------------------------------------------------- @@ -44,14 +44,14 @@ function dist = twoPtDist(srcPos, rcvPos) % Validate arguments % ==================================================== % Mandatory arguments - validateattributes(srcPos, "numeric", {'ncols', 3, 'nonempty', 'nonnan', 'finite', 'real'}, mfilename, "Start points Cartesian coordinates", 1); - validateattributes(rcvPos, "numeric", {'ncols', 3, 'nonempty', 'nonnan', 'finite', 'real'}, mfilename, "End points Cartesian coordinates", 2); + validateattributes(srcPos, "numeric", {'nrows', 3, 'nonempty', 'nonnan', 'finite', 'real'}, mfilename, "Start points Cartesian coordinates", 1); + validateattributes(rcvPos, "numeric", {'nrows', 3, 'nonempty', 'nonnan', 'finite', 'real'}, mfilename, "End points Cartesian coordinates", 2); % ==================================================== % Start working % ==================================================== % Go through the sources - for idx = size(srcPos, 1):-1:1 - dist(idx, :) = vecnorm(rcvPos - srcPos(idx, :), 2, 2); + for idx = size(srcPos, 2):-1:1 + dist(idx, :) = vecnorm(rcvPos - srcPos(:, idx), 2, 1); end end \ No newline at end of file diff --git a/Utilities/Geometries/MATLAB/Functions/rcvGeo.m b/Utilities/Geometries/MATLAB/Functions/rcvGeo.m index eb9fd5e06b3a6c20055c0add326e8ba900860756..a9f4d80cfc86e9f74318d55995cecb8d0d7739fe 100644 --- a/Utilities/Geometries/MATLAB/Functions/rcvGeo.m +++ b/Utilities/Geometries/MATLAB/Functions/rcvGeo.m @@ -55,12 +55,20 @@ % axis. The rotations are performed clockwise. % [Default: zeros(3, 1)]. % +% rotOrd [char/string] (Optional): The order the rotations will be applied. +% This can be some permutation of the +% string "xyz" and is not case-sensistive. +% The order corresponds to multiplication +% on the left (i.e. R * v, where "R" is +% the rotation matrix and "v" a vector to +% be rotated). [Default: "xyz"]. +% % -------------------------------------------------- % Output % % omniPos [numeric]: This matrix contains the coordinates of the -% omnidirectional receivers and has dimensions Nx3, -% where N is the number of receivers and the other +% omnidirectional receivers and has dimensions 3xN, +% where N is the number of receivers and the first % dimension corresponds to the x, y and z Cartesian % coordinates. % @@ -69,8 +77,8 @@ % can be used to create a figure-of-eight % pattern when combined as a first order % DMA). The dimensions of the matrix are -% Nx3x2 where N is the number of sensors, the -% second dimension corresponds to the x, y +% 3xNx2 where N is the number of sensors, the +% first dimension corresponds to the x, y % and z Cartesian coordinates and the last % dimension corresponds to the % "Figure-of-Eight" elements. For the @@ -85,9 +93,9 @@ % "figure-of-eight" elements, one more is % placed in the middle of ther axis and higher % in order to create an equilateral triangle. -% The matrix has dimensions Nx3x3 where N is +% The matrix has dimensions 3xNx3 where N is % the number of microphone positions, the -% second dimension corresponds to the x, y and +% first dimension corresponds to the x, y and % z Cartesian coordinates and the third % dimension to individual elements. For the % "Single" and "ULA geometries, the axis of @@ -105,9 +113,9 @@ % three "Figure-of-Eight" sensors (two sensors % which can be combined as a first order DMA % to create a figure-of-eight sensor). The -% dimensions of the matrix are Nx3x2x3 where N +% dimensions of the matrix are 3xNx2x3 where N % is the number of sensor positions, the -% second dimension corresponds to the x, y and +% first dimension corresponds to the x, y and % z Cartesian coordinates. The third dimension % corresponds to the elements of each % figure-of-eight sensor with the first @@ -121,101 +129,48 @@ % box2DPos [numeric] (Optional): This matrix contains the coordinates of % the elements of "boxPos" lying on the x-y % plane (the z-axis elements are discarded). -% The dimensions of the matrix are Nx3x2x2. +% The dimensions of the matrix are 3xNx2x2. % % tetPos [numeric]: This matrix contains the positions of a normal % tetrahedron, with four vertices (points) non-parallel % to the Cartesian axes. The dimensions of the matrix are -% Nx3x4 where N is the number of measurement positions, -% the second dimension is the x, y, and z Cartesian +% 3xNx4 where N is the number of measurement positions, +% the first dimension is the x, y, and z Cartesian % coordinates and the last dimension corresponds to the % elements of the configuration. The element indices are: % 1) Positive x, y and z, 2) negative x, positive y, % zero z, 3) negative x and y, zero z and 4) negative x, % y and z. % -% fig8Vec [numeric]: This is an Nx3 matrix containing the positions of +% fig8Vec [numeric]: This is an 3xN matrix containing the positions of % the figure-of-eight elements like: -% Pos_1_Elem_1_x, Pos_1_Elem_2_y, Pos_1_Elem_1_z -% Pos_1_Elem_2_x, Pos_1_Elem_2_y, Pos_1_Elem_2_z -% Pos_2_Elem_1_x, Pos_2_Elem_2_y, Pos_2_Elem_1_z -% Pos_2_Elem_2_x, Pos_2_Elem_2_y, Pos_2_Elem_2_z -% . . . -% . . . -% . . . -% Pos_M_Elem_1_x, Pos_M_Elem_2_y, Pos_M_Elem_1_z -% Pos_M_Elem_2_x, Pos_M_Elem_2_y, Pos_M_Elem_2_z +% Pos_1_Elem_1_x, Pos_1_Elem_2_x, Pos_2_Elem_1_x ... +% Pos_1_Elem_1_y, Pos_1_Elem_2_y, Pos_2_Elem_1_y ... +% Pos_1_Elem_1_z, Pos_1_Elem_2_z, Pos_2_Elem_1_z ... % -% triVec [numeric]: This is an Nx3 matrix containing the positions of +% triVec [numeric]: This is an 3xN matrix containing the positions of % the triangle elements like: -% Pos_1_Elem_1_x, Pos_1_Elem_2_y, Pos_1_Elem_1_z -% Pos_1_Elem_2_x, Pos_1_Elem_2_y, Pos_1_Elem_2_z -% Pos_1_Elem_3_x, Pos_1_Elem_3_y, Pos_1_Elem_3_z -% Pos_1_Elem_1_x, Pos_1_Elem_2_y, Pos_1_Elem_1_z -% Pos_1_Elem_2_x, Pos_1_Elem_2_y, Pos_1_Elem_2_z -% Pos_1_Elem_3_x, Pos_1_Elem_3_y, Pos_1_Elem_3_z -% . . . -% . . . -% . . . -% Pos_M_Elem_1_x, Pos_M_Elem_2_y, Pos_M_Elem_1_z -% Pos_M_Elem_2_x, Pos_M_Elem_2_y, Pos_M_Elem_2_z -% Pos_M_Elem_3_x, Pos_M_Elem_3_y, Pos_M_Elem_3_z +% Pos_1_Elem_1_x, Pos_1_Elem_2_x, Pos_1_Elem_3_x ... +% Pos_1_Elem_1_y, Pos_1_Elem_2_y, Pos_1_Elem_3_y ... +% Pos_1_Elem_1_z, Pos_1_Elem_2_z, Pos_1_Elem_3_z ... % -% boxVec [numeric]: This is an Nx3 matrix containing the positions of +% boxVec [numeric]: This is an 3xN matrix containing the positions of % the box configuration elements like: -% Pos_1_X_Elem_1_x, Pos_1_X_Elem_2_y, Pos_1_X_Elem_1_z -% Pos_1_X_Elem_2_x, Pos_1_X_Elem_2_y, Pos_1_X_Elem_2_z -% Pos_1_Y_Elem_1_x, Pos_1_Y_Elem_2_y, Pos_1_Y_Elem_1_z -% Pos_1_Y_Elem_2_x, Pos_1_Y_Elem_2_y, Pos_1_Y_Elem_2_z -% Pos_1_Z_Elem_1_x, Pos_1_Z_Elem_2_y, Pos_1_Z_Elem_1_z -% Pos_1_Z_Elem_2_x, Pos_1_Z_Elem_2_y, Pos_1_Z_Elem_2_z -% Pos_2_X_Elem_1_x, Pos_2_X_Elem_2_y, Pos_2_X_Elem_1_z -% Pos_2_X_Elem_2_x, Pos_2_X_Elem_2_y, Pos_2_X_Elem_2_z -% Pos_2_Y_Elem_1_x, Pos_2_Y_Elem_2_y, Pos_2_Y_Elem_1_z -% Pos_2_Y_Elem_2_x, Pos_2_Y_Elem_2_y, Pos_2_Y_Elem_2_z -% Pos_2_Z_Elem_1_x, Pos_2_Z_Elem_2_y, Pos_2_Z_Elem_1_z -% Pos_2_Z_Elem_2_x, Pos_2_Z_Elem_2_y, Pos_2_Z_Elem_2_z -% . . . -% . . . -% . . . -% Pos_N_X_Elem_1_x, Pos_N_X_Elem_2_y, Pos_N_X_Elem_1_z -% Pos_N_X_Elem_2_x, Pos_N_X_Elem_2_y, Pos_N_X_Elem_2_z -% Pos_N_Y_Elem_1_x, Pos_N_Y_Elem_2_y, Pos_N_Y_Elem_1_z -% Pos_N_Y_Elem_2_x, Pos_N_Y_Elem_2_y, Pos_N_Y_Elem_2_z -% Pos_N_Z_Elem_1_x, Pos_N_Z_Elem_2_y, Pos_N_Z_Elem_1_z -% Pos_N_Z_Elem_2_x, Pos_N_Z_Elem_2_y, Pos_N_Z_Elem_2_z +% Pos_1_X_Elem_1_x, Pos_1_X_Elem_2_x, Pos_1_Y_Elem_1_x... +% Pos_1_X_Elem_1_y, Pos_1_X_Elem_2_y, Pos_1_Y_Elem_1_y... +% Pos_1_X_Elem_1_z, Pos_1_X_Elem_2_z, Pos_1_Y_Elem_1_z... % -% box2DVec [numeric]: This is an Nx3 matrix containing the positions of +% box2DVec [numeric]: This is an 3xN matrix containing the positions of % the 2D box configuration elements like: -% Pos_1_X_Elem_1_x, Pos_1_X_Elem_2_y, Pos_1_X_Elem_1_z -% Pos_1_X_Elem_2_x, Pos_1_X_Elem_2_y, Pos_1_X_Elem_2_z -% Pos_1_Y_Elem_1_x, Pos_1_Y_Elem_2_y, Pos_1_Y_Elem_1_z -% Pos_1_Y_Elem_2_x, Pos_1_Y_Elem_2_y, Pos_1_Y_Elem_2_z -% Pos_2_X_Elem_1_x, Pos_2_X_Elem_2_y, Pos_2_X_Elem_1_z -% Pos_2_X_Elem_2_x, Pos_2_X_Elem_2_y, Pos_2_X_Elem_2_z -% Pos_2_Y_Elem_1_x, Pos_2_Y_Elem_2_y, Pos_2_Y_Elem_1_z -% Pos_2_Y_Elem_2_x, Pos_2_Y_Elem_2_y, Pos_2_Y_Elem_2_z -% . . . -% . . . -% . . . -% Pos_N_X_Elem_1_x, Pos_N_X_Elem_2_y, Pos_N_X_Elem_1_z -% Pos_N_X_Elem_2_x, Pos_N_X_Elem_2_y, Pos_N_X_Elem_2_z -% Pos_N_Y_Elem_1_x, Pos_N_Y_Elem_2_y, Pos_N_Y_Elem_1_z -% Pos_N_Y_Elem_2_x, Pos_N_Y_Elem_2_y, Pos_N_Y_Elem_2_z +% Pos_1_X_Elem_1_x, Pos_1_X_Elem_2_x, Pos_1_Y_Elem_1_x. +% Pos_1_X_Elem_1_y, Pos_1_X_Elem_2_y, Pos_1_Y_Elem_1_y. +% Pos_1_X_Elem_1_z, Pos_1_X_Elem_2_z, Pos_1_Y_Elem_1_z. % -% tetVec [numeric]: This is an Nx3 matrix containg the positions of the +% tetVec [numeric]: This is an 3xN matrix containg the positions of the % normal tetrahedron configuration like: -% Pos_1_Elem_1_x, Pos_1_Elem_1_y, Pos_1_Elem_1_z -% Pos_1_Elem_2_x, Pos_1_Elem_2_y, Pos_1_Elem_2_z -% Pos_1_Elem_3_x, Pos_1_Elem_3_y, Pos_1_Elem_3_z -% Pos_1_Elem_4_x, Pos_1_Elem_4_y, Pos_1_Elem_4_z -% . . . -% . . . -% . . . -% Pos_N_Elem_1_x, Pos_1_Elem_1_y, Pos_1_Elem_1_z -% Pos_N_Elem_2_x, Pos_1_Elem_2_y, Pos_1_Elem_2_z -% Pos_N_Elem_3_x, Pos_1_Elem_3_y, Pos_1_Elem_3_z -% Pos_N_Elem_4_x, Pos_1_Elem_4_y, Pos_1_Elem_4_z +% Pos_1_Elem_1_x, Pos_1_Elem_2_x, Pos_1_Elem_3_x ... +% Pos_1_Elem_1_y, Pos_1_Elem_2_y, Pos_1_Elem_3_y ... +% Pos_1_Elem_1_z, Pos_1_Elem_2_z, Pos_1_Elem_3_z ... % % -------------------------------------------------- % Notes @@ -223,11 +178,11 @@ % - Dependencies: rotMat3d() to rotate the configurations % % -------------------------------------------------- -function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleVec, boxVec, box2DVec, tetVec] = rcvGeo(gType, nSens, elemDist, dmaElemDist, trans, rot) +function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triVec, boxVec, box2DVec, tetVec] = rcvGeo(gType, nSens, elemDist, dmaElemDist, trans, rot, rotOrd) % ==================================================== % Check for number of arguments % ==================================================== - narginchk(1, 6); + narginchk(1, 7); nargoutchk(0, 11); % ==================================================== @@ -238,6 +193,10 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV validatestring(gType, ["Single", "ULA", "UCA", "Log"], mfilename, "Geometry type", 1); % Validate optional arguments + if nargin < 7 || (nargin > 6 && isempty(rotOrd)) + rotOrd = "xyz"; + end + if nargin > 5 && ~isempty(rot) validateattributes(rot, {'numeric'}, {'vector', 'nonempty', 'nonnan', 'finite', 'real', 'numel', 3}, mfilename, "Geometry rotation around the Cartesian axes", 6); else @@ -275,14 +234,14 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV % Omni sensor coordinates switch lower(gType) case "single" - omniPos = [0, 0, 0]; + omniPos = [0; 0; 0]; case "ula" omniPos = elemDist * ((nSens - 1)/2) * linspace(-1, 1, nSens); - omniPos = [omniPos; zeros(2, nSens)]'; + omniPos = [omniPos; zeros(2, nSens)]; case "uca" angsOfRot = (0:1/nSens:(1 - 1/nSens)) * 2 * pi; [x, y] = pol2cart(angsOfRot, elemDist); - omniPos = [x; y; zeros(size(x))].'; + omniPos = [x; y; zeros(size(x))]; end % ==================================================== @@ -291,14 +250,14 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV % Check whether we have to calculate the directional configuration sensor position if dmaElemDist <= 0 || isempty(dmaElemDist) fig8Pos = []; fig8Vec = []; - triPos = []; triangleVec = []; + triPos = []; triVec = []; boxPos = []; boxVec = []; box2DPos = []; box2DVec = []; tetPos = []; tetVec = []; % Translate if nargout > 0 - omniPos = omniPos + trans(:).'; + omniPos = omniPos + trans(:); end return; end @@ -307,20 +266,20 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV if nargout > 1 switch lower(gType) case {"single", "ula"} - fig8Pos = omniPos + [0, dmaElemDist/2, 0]; - fig8Pos = cat(3, fig8Pos, omniPos - [0, dmaElemDist/2, 0]); + fig8Pos = omniPos + [0; dmaElemDist/2; 0]; + fig8Pos = cat(3, fig8Pos, omniPos - [0; dmaElemDist/2; 0]); case "uca" [x, y] = pol2cart(angsOfRot, elemDist + dmaElemDist/2); - fig8Pos = [x; y; zeros(size(x))].'; + fig8Pos = [x; y; zeros(size(x))]; [x, y] = pol2cart(angsOfRot, elemDist - dmaElemDist/2); - fig8Pos = cat(3, fig8Pos, [x; y; zeros(size(x))].'); + fig8Pos = cat(3, fig8Pos, [x; y; zeros(size(x))]); end end % Triangle positions if nargout > 2 - triPos = cat(3, fig8Pos, omniPos + [0, 0, dmaElemDist * sqrt(3)/2]); % Add an element to create an equilateral triangle - triPos = triPos - [0, 0, dmaElemDist * tand(30)/2]; % Move it down so that the array centre will be at the coordinates of the omni microphone + triPos = cat(3, fig8Pos, omniPos + [0; 0; dmaElemDist * sqrt(3)/2]); % Add an element to create an equilateral triangle + triPos = triPos - [0; 0; dmaElemDist * tand(30)/2]; % Move it down so that the array centre will be at the coordinates of the omni microphone end % Box positions @@ -328,24 +287,24 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV switch lower(gType) case {"single", "ula"} % X-axis figure-of-eight elements - tempBox = omniPos + [dmaElemDist/2, 0, 0]; - tempBox = cat(3, tempBox, omniPos - [dmaElemDist/2, 0, 0]); - boxPos = cat(4, tempBox, fig8Pos); + tmpBox = omniPos + [dmaElemDist/2; 0; 0]; + tmpBox = cat(3, tmpBox, omniPos - [dmaElemDist/2; 0; 0]); + boxPos = cat(4, tmpBox, fig8Pos); case "uca" % X-Y plane perpendicular to the radial direction % figure-of-eight elements (the "X-axis figure-of-eight") [x, y] = pol2cart(angsOfRot + deg2rad(90), dmaElemDist/2); - tempBox = [x; y; zeros(size(x))].'; + tmpBox = [x; y; zeros(size(x))]; [x, y] = pol2cart(angsOfRot - deg2rad(90), dmaElemDist/2); - tempBox = cat(3, tempBox, [x; y; zeros(size(x))].'); - tempBox = omniPos + tempBox; - boxPos = cat(4, tempBox, fig8Pos); + tmpBox = cat(3, tmpBox, [x; y; zeros(size(x))]); + tmpBox = omniPos + tmpBox; + boxPos = cat(4, tmpBox, fig8Pos); end % Z-axis figure-of-eight elements (common to all geometries) - tempBox = omniPos + [0, 0, dmaElemDist/2]; - tempBox = cat(3, tempBox, omniPos - [0, 0, dmaElemDist/2]); - boxPos = cat(4, boxPos, tempBox); + tmpBox = omniPos + [0; 0; dmaElemDist/2]; + tmpBox = cat(3, tmpBox, omniPos - [0; 0; dmaElemDist/2]); + boxPos = cat(4, boxPos, tmpBox); end % 2D box positions @@ -358,21 +317,20 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV if nargout > 5 % Generate normal tetrahedron with lower face parallel to x-y plane % and edge length sqrt(8/3) [from: https://en.wikipedia.org/wiki/Tetrahedron] - tempPos = [sqrt(8/9), 0, -1/3;... - -sqrt(2/9), sqrt(2/3), -1/3;... - -sqrt(2/9), -sqrt(2/3), -1/3;... - 0, 0, 1]; + tmpPos = [sqrt(8/9), -sqrt(2/9), -sqrt(2/9), 0; ... + 0, sqrt(2/3), -sqrt(2/3), 0; ... + -1/3, -1/3, -1/3, 1]; % Set edge length equal to the given DMA inter-element distance - tempPos = dmaElemDist * tempPos/(vecnorm(tempPos(1, :) - tempPos(2, :))); + tmpPos = dmaElemDist * tmpPos/vecnorm(tmpPos(:, 1) - tmpPos(:, 2)); switch lower(gType) case "ula" % Rotate the tetrahedrals on the positive side of the y-axis - rotIdx = sum(omniPos(:, 1) > 0); % Calculate how many tetrahedrals we have to rotate + rotIdx = sum(omniPos(1, :) > 0); % Calculate how many tetrahedrals we have to rotate % Go through the tetrahedrals - for measPosIdx = size(omniPos, 1):-1:1 + for measPosIdx = size(omniPos, 2):-1:1 % Calculate rotation angle if measPosIdx > rotIdx tetRot = 60; @@ -381,22 +339,18 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV end % Rotate and position - tetPos(measPosIdx, :, :) = omniPos(measPosIdx, :) + tempPos * rotMat3d(0, 0, tetRot, [], "Degs"); + tetPos(:, measPosIdx, :) = omniPos(:, measPosIdx) + rotMat3d(0, 0, tetRot, [], "Degs") * tmpPos; end case "uca" % Get the angle of the positions on the circle - az = cart2sph(omniPos(:, 1), omniPos(:, 2), omniPos(:, 3)); + az = cart2sph(omniPos(1, :), omniPos(2, :), omniPos(3, :)); % Rotate and position tetrahedrals for measPosIdx = numel(az):-1:1 - tetPos(measPosIdx, :, :) = omniPos(measPosIdx, :) + tempPos * rotMat3d(0, 0, -az(measPosIdx), [], "Degs"); + tetPos(:, measPosIdx, :) = omniPos(:, measPosIdx) + rotMat3d(0, 0, az(measPosIdx), [], "Degs") * tmpPos; end case "single" - tetPos = omniPos + tempPos * rotMat3d(30, 30, 0, [], "Degs"); - end - - if ~strcmpi(gType, "Single") - tetPos = permute(tetPos, [1, 3, 2]); + tetPos = omniPos + rotMat3d(30, 30, 0, [], "Degs") * tmpPos; end end @@ -404,32 +358,32 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV % Translate and rotate geometries % ==================================================== for mIdx = size(omniPos, 3):-1:1 - omniPos(:, :, mIdx) = omniPos(:, :, mIdx) * rotMat3d(-rot(1), -rot(2), -rot(3), "zyx", "Degs") + trans(:).'; + omniPos(:, :, mIdx) = rotMat3d(rot(1), rot(2), rot(3), rotOrd, "Degs") * omniPos(:, :, mIdx) + trans(:); end if exist("fig8Pos", "var") for mIdx = size(fig8Pos, 3):-1:1 - fig8Pos(:, :, mIdx) = fig8Pos(:, :, mIdx) * rotMat3d(-rot(1), -rot(2), -rot(3), "zyx", "Degs") + trans(:).'; + fig8Pos(:, :, mIdx) = rotMat3d(rot(1), rot(2), rot(3), rotOrd, "Degs") * fig8Pos(:, :, mIdx) + trans(:); end for mIdx = size(triPos, 3):-1:1 - triPos(:, :, mIdx) = triPos(:, :, mIdx) * rotMat3d(-rot(1), -rot(2), -rot(3), "zyx", "Degs") + trans(:).'; + triPos(:, :, mIdx) = rotMat3d(rot(1), rot(2), rot(3), rotOrd, "Degs") * triPos(:, :, mIdx) + trans(:); end for mIdx = size(boxPos, 4):-1:1 for mmIdx = size(boxPos, 3):-1:1 - boxPos(:, :, mmIdx, mIdx) = boxPos(:, :, mmIdx, mIdx) * rotMat3d(-rot(1), -rot(2), -rot(3), "zyx", "Degs") + trans(:).'; + boxPos(:, :, mmIdx, mIdx) = rotMat3d(rot(1), rot(2), rot(3), rotOrd, "Degs") * boxPos(:, :, mmIdx, mIdx) + trans(:); end end for mIdx = size(box2DPos, 4):-1:1 for mmIdx = size(box2DPos, 3):-1:1 - box2DPos(:, :, mmIdx, mIdx) = box2DPos(:, :, mmIdx, mIdx) * rotMat3d(-rot(1), -rot(2), -rot(3), "zyx", "Degs") + trans(:).'; + box2DPos(:, :, mmIdx, mIdx) = rotMat3d(rot(1), rot(2), rot(3), rotOrd, "Degs") * box2DPos(:, :, mmIdx, mIdx) + trans(:); end end for mIdx = size(tetPos, 3):-1:1 - tetPos(:, :, mIdx) = tetPos(:, :, mIdx) * rotMat3d(-rot(1), -rot(2), -rot(3), "zyx", "Degs") + trans(:).'; + tetPos(:, :, mIdx) = rotMat3d(rot(1), rot(2), rot(3), rotOrd, "Degs") * tetPos(:, :, mIdx) + trans(:); end % ==================================================== @@ -437,27 +391,27 @@ function [omniPos, fig8Pos, triPos, boxPos, box2DPos, tetPos, fig8Vec, triangleV % ==================================================== % Provide a Nx3 "Figure-of-Eight" matrix if nargout > 6 - fig8Vec = reshape(permute(fig8Pos, [3, 1, 2]), [], 3); + fig8Vec = reshape(fig8Pos, 3, []); end % Provide a Nx3 "Triangle" matrix if nargout > 7 - triangleVec = reshape(permute(triPos, [3, 1, 2]), [], 3); + triVec = reshape(triPos, 3, []); end % Provide a Nx3 "Box" matrix if nargout > 8 - boxVec = reshape(permute(boxPos, [3, 4, 1, 2]), [], 3); + boxVec = reshape(boxPos, 3, []); end % Provide a Nx3 "2DBox" matrix if nargout > 9 - box2DVec = reshape(permute(box2DPos, [3, 4, 1, 2]), [], 3); + box2DVec = reshape(box2DPos, 3, []); end % Provide a Nx3 Tetrahedron matrix if nargout > 10 - tetVec = reshape(permute(tetPos, [3, 1, 2]), [], 3); + tetVec = reshape(tetPos, 3, []); end end end \ No newline at end of file diff --git a/Utilities/Geometries/MATLAB/Functions/srcGeo.m b/Utilities/Geometries/MATLAB/Functions/srcGeo.m index f327f801ab18fce8405351328fcabf3e3e83f416..b4be6e5643b07c065268bb84454191ac53b5b036 100644 --- a/Utilities/Geometries/MATLAB/Functions/srcGeo.m +++ b/Utilities/Geometries/MATLAB/Functions/srcGeo.m @@ -3,7 +3,7 @@ % Author: Achilles Kappis % e-mail: axilleaz@protonmail.com % -% Date: 28/09/2024 (DD/MM/YYYY) +% Date: 29/09/2024 (DD/MM/YYYY) % % Copyright: MIT % -------------------------------------------------- @@ -70,19 +70,18 @@ % Output % % sPos [numeric]: The matrix with the source coordinates in the Cartesian -% system. The matrix has dimensions Nx3, where N is the -% number of sources and the other dimension correspond to -% the x, y and z coodrinates. +% system. The matrix has dimensions 3xN, where N is the +% number of sources and the first dimension correspons +% to the x, y and z Cartesian coodrinates. % -% Q [numeric] (Optional): The source strengths for the directional source -% configuration. For all other configurations this -% parameter is empty. +% Q [numeric]: The source strengths for the directional source +% configuration. For all other configurations this parameter +% is empty. % -% domIdx [numeric] (Optional): The dominant source indices in the -% directional source configuration. This may -% be used to extract the Cartesian coordinates -% of the dominant sources as well as their -% strenghts. +% domIdx [numeric]: The dominant source indices in the directional source +% configuration. This may be used to extract the +% Cartesian coordinates of the dominant sources as well +% as their strenghts. % % % -------------------------------------------------- @@ -184,24 +183,23 @@ function [sPos, Q, domIdx] = srcGeo(gType, srcLen, originDist, ang, nSrc, azim, % Calculate source positions % ==================================================== if strcmpi(gType, "Single") - sPos = [srcLen, originDist, 0]; + sPos = [srcLen; originDist; 0]; domIdx = []; elseif sum(strcmpi(gType, ["Causal", "Anti-Causal", "AntiCausal", "Side", "Diagonal"])) > 0 if srcLen ~= 0 - sPos = [linspace(-srcLen/2, srcLen/2, nSrc); originDist * ones(1, nSrc); zeros(1, nSrc)].'; + sPos = [linspace(-srcLen/2, srcLen/2, nSrc); originDist * ones(1, nSrc); zeros(1, nSrc)]; else - sPos = [srcLen, originDist, 0]; + sPos = [srcLen; originDist; 0]; end if nSrc ~= 0 switch lower(gType) case {"anti-causal", "anticausal"} - sPos = sPos * rotMat3d(0, 0, 180, [], "Degs"); % Rotate 180 degrees (clockwise) + sPos = rotMat3d(0, 0, 180, [], "Degs") * sPos; % Rotate 180 degrees (anti-clockwise) case "side" - sPos = sPos * rotMat3d(0, 0, 90, [], "Degs"); % Rotate 90 degrees (clockwise) + sPos = rotMat3d(0, 0, 90, [], "Degs") * sPos; % Rotate 90 degrees (anti-clockwise) case "diagonal" - sPos = rotMat3d(0, 0, -90 + ang, [], "Degs") * sPos.'; % Rotate specified degrees (clockwise) - sPos = sPos.'; + sPos = rotMat3d(0, 0, -90 + ang, [], "Degs") * sPos; % Rotate specified degrees (anti-clockwise) end end domIdx = []; @@ -243,16 +241,16 @@ function [sPos, Q, domIdx] = srcGeo(gType, srcLen, originDist, ang, nSrc, azim, domIdx = []; end elseif strcmpi(gType, "circle") - sPos = (0:2 * pi/nSrc:2 * pi - 1/nSrc).'; + sPos = (0:2 * pi/nSrc:2 * pi - 1/nSrc); [sPosX, sPosY] = sph2cart(sPos, 0, originDist); - sPos = [sPosX, sPosY, zeros(size(sPosX))]; + sPos = [sPosX; sPosY; zeros(size(sPosX))]; domIdx = []; end % Calculate source strengths if nargout > 1 % Set the amplitude of the "weak" sources - Q = (10^(-SNR/10))/sum(~ismember(1:size(sPos, 1), domIdx)) * ones(size(sPos, 1), 1); + Q = (10^(-SNR/10))/sum(~ismember(1:size(sPos, 2), domIdx)) * ones(size(sPos, 2), 1); % Set the amplitude of the dominant sources if ~isinf(SNR) diff --git a/Utilities/Geometries/MATLAB/Functions/virtMicGeo.m b/Utilities/Geometries/MATLAB/Functions/virtMicGeo.m index 8c65037ce29062ef4b0940c88d49acdf258be86f..8f9920a96d3b00da33a5cca42ed764b763ee3031 100644 --- a/Utilities/Geometries/MATLAB/Functions/virtMicGeo.m +++ b/Utilities/Geometries/MATLAB/Functions/virtMicGeo.m @@ -53,18 +53,26 @@ % axis. The rotations are performed clockwise. % [Default: zeros(3, 1)]. % +% rotOrd [char/string] (Optional): The order the rotations will be applied. +% This can be some permutation of the +% string "xyz" and is not case-sensistive. +% The order corresponds to multiplication +% on the left (i.e. R * v, where "R" is +% the rotation matrix and "v" a vector to +% be rotated). [Default: "xyz"]. +% % -------------------------------------------------- % Output % % vPos [numeric]: The matrix with the source coordinates in the Cartesian -% system. The matrix has dimensions Nx3, where N is the -% number of sources and the other dimension correspond to +% system. The matrix has dimensions 3xN, where N is the +% number of sources and the first dimension corresponds to % the x, y and z coodrinates. % % vPosMesh [numeric]: The matrix has dimensions nSens(1)xnSens(2)x3. It % holds the x, y and z Cartesian coordinates for each % position of the "Grid" geometry. If the geometry is -% otherwise not "Grid", this is an empty array. +% other than "Grid", this is an empty array. % % -------------------------------------------------- % Notes @@ -80,11 +88,11 @@ % setup is one dimensional. % % -------------------------------------------------- -function [vPos, vPosMesh] = virtMicGeo(gType, geoDim, nSens, trans, rot) +function [vPos, vPosMesh] = virtMicGeo(gType, geoDim, nSens, trans, rot, rotOrd) % ==================================================== % Check for number of arguments % ==================================================== - narginchk(1, 5); + narginchk(1, 6); nargoutchk(0, 2); % ==================================================== @@ -124,7 +132,7 @@ function [vPos, vPosMesh] = virtMicGeo(gType, geoDim, nSens, trans, rot) end end elseif strcmpi(gType, "Dual") - nSens = [2, 0, 0]; + nSens = [2; 0; 0]; else nSens = 10 * ones(3, 1); end @@ -141,6 +149,10 @@ function [vPos, vPosMesh] = virtMicGeo(gType, geoDim, nSens, trans, rot) rot = zeros(3, 1); end + if nargin < 6 || (nargin > 5 && isempty(rotOrd)) + rotOrd = "xyz"; + end + % ==================================================== % Calculate virtual microphone positions @@ -148,15 +160,15 @@ function [vPos, vPosMesh] = virtMicGeo(gType, geoDim, nSens, trans, rot) switch lower(gType) case "single" % Create a single point at offset coordinates and return - vPos = zeros(1, 3); + vPos = zeros(3, 1); case "dual" - vPos = [-geoDim(1)/2, 0, 0; ... - geoDim(1)/2, 0, 0]; + vPos = [-geoDim(1)/2; 0; 0, ... + geoDim(1)/2; 0; 0]; case "array" vPos = linspace(-geoDim(1)/2, geoDim(1)/2, nSens(1)); - vPos = [vPos(:), zeros(numel(vPos), 2)]; + vPos = [vPos; zeros(2, numel(vPos))]; case "cube" - % Make we don't get empty arrays when dimensions are 0 + % Make sure we don't get empty arrays when dimensions are 0 if nSens(1) == 0 x = 0; else @@ -176,13 +188,13 @@ function [vPos, vPosMesh] = virtMicGeo(gType, geoDim, nSens, trans, rot) end [x, y, z] = meshgrid(x, y, z); - vPos = [x(:), y(:), z(:)]; + vPos = [x; y; z]; otherwise error("Oops... something went wrong here... not known geometry...!!!"); end % Rotate and translate - vPos = vPos * rotMat3d(-rot(1), -rot(2), -rot(3), "zyx", "Degs") + trans(:).'; + vPos = rotMat3d(rot(1), rot(2), rot(3), rotOrd, "Degs") * vPos + trans(:); % For "Cube" geometry provide the coordinates in a "mesh format" if nargout > 1 && strcmpi(gType, "Cube")