Skip to content
Snippets Groups Projects
Commit 09f272de authored by Achilles Kappis's avatar Achilles Kappis
Browse files

Update interface and corresponding internal calculations of the virtMicGeo.m function

parent 31978c08
No related branches found
No related tags found
1 merge request!4Fix time-domain calculations of the optimal observation filter
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
% Author: Achilles Kappis % Author: Achilles Kappis
% e-mail: axilleaz@protonmail.com % e-mail: axilleaz@protonmail.com
% %
% Date: 16/07/2024 (DD/MM/YYYY) % Date: 18/09/2024 (DD/MM/YYYY)
% %
% Copyright: MIT % Copyright: MIT
% -------------------------------------------------- % --------------------------------------------------
...@@ -13,17 +13,35 @@ ...@@ -13,17 +13,35 @@
% Input % Input
% %
% gType [char/string]: The type of the source geometry. At the moment the % gType [char/string]: The type of the source geometry. At the moment the
% available options are "Single", "Array", "Grid", % available options are "Single", "Array", "Cube" and
% "Cube" and "Dual". The last is an arrangement with 2 % "Dual". The last is an arrangement a distance apart
% sensors "xLen" apart that can be translated and % that can be translated and rotated.
% rotated.
% %
% geoDim [numeric] (Optional): The dimensions of the geometry. This is a
% vector with three elements holding the
% dimensions of the setup in each Cartesian
% direction. Based on the "gType" some or all
% of them are used. For "gType" set either as
% "Array" or "Dual", the first value is used
% as the length of the "Array" or distance
% between the two position in "Dual". When
% "gType" is "Single" this argument is
% ignored. [Default: ones(3, 1)].
% %
% xLen [numeric] (Optional): The width of the "Array" geometry, the length % nSens [numeric] (Optional): This can be either a real integer value
% of the side of the "Grid" and "Cube geometries % denoting the number of sensors in the array
% and the distance between positions in "Dual" % or a vector with three elements denoting
% geometry. % the number of sensor along each Cartesian
% [Default: Array/Grid/Cube - 1, Dual - 0.2]. % dimension. If a dimension is equal to zero
% the corresponding value is ignored. If this
% is a scalar value the used must ensure that
% it is correctly divisible over the non-zero
% dimensions of the geometry uniformly (see
% Notes for info). For the "Array" geometry,
% only the first value (if "nSens" is a
% vector) is used and for the "Single"
% geometry the argument is ignored.
% [Default: 10 * ones(3, 1)].
% %
% xOff [numeric] (Optional): X-axis offset. [Default: 0]. % xOff [numeric] (Optional): X-axis offset. [Default: 0].
% %
...@@ -31,28 +49,10 @@ ...@@ -31,28 +49,10 @@
% %
% zOff [numeric] (Optional): Z-axis offset. [Default: 0]. % zOff [numeric] (Optional): Z-axis offset. [Default: 0].
% %
% nSens [numeric] (Optional): The number of sensors in the array. % orient [numeric] (Optional): This is a real vector holding the rotation
% For the "Grid" geometry the square root of % for the geometry in degrees, along each
% "nSens" must be an integer value. For the % Cartesian axis. The rotations are performed
% "Cube" geometry the cubic root of "nSens" % clockwise. [Default: zeros(3, 1)].
% must be an integer value. This parameter is
% ignored for the "Single" geometry.
% [Default: Array/Grid - 100, Cube - 1000].
%
% orient [char/string/numeric] (Optional): The orientation of the
% geometry. For the "Single" and
% "Cube" geometries this value is
% ignored. For the "Dual"
% configuration only numerical
% values are used to declare the
% rotation of the configuration
% with respect to the local z-axis
% in degrees. For the rest of the
% configurations, the string
% values allowed are "XY", "XZ"
% and "YZ" and declare the plane
% on which the configuration will
% be placed. [Default: "XY" | 0].
% %
% -------------------------------------------------- % --------------------------------------------------
% Output % Output
...@@ -62,24 +62,26 @@ ...@@ -62,24 +62,26 @@
% number of sources and the other dimension correspond to % number of sources and the other dimension correspond to
% the x, y and z coodrinates. % the x, y and z coodrinates.
% %
% vPosMesh [numeric]: The matrix has dimensions sqrt(N)xsqrt(N)x3 where N % vPosMesh [numeric]: The matrix has dimensions nSens(1)xnSens(2)x3. It
% is the number of sources. It contains the x, y and z % holds the x, y and z Cartesian coordinates for each
% Cartesian coordinates for each position in a % position of the "Grid" geometry. If the geometry is
% rectangular grid with dimensions sqrt(N)xsqrt(N). % otherwise not "Grid", this is an empty array.
% This is used only with the "Grid" geometry, otherwise
% it is empty.
% %
% -------------------------------------------------- % --------------------------------------------------
% Notes % Notes
% %
% - Dependencies: rotMat3d(): To rotate sources. % - Dependencies: rotMat3d(): To rotate the geometry.
%
% - If the number of sources are provided as a single integer, the user
% must make sure that they can be uniformly distributed in the non-zero
% dimensions of the geometry. For example, for the "Cube" geometry with
% two non-zero dimensions, the square root of the number of sensors must
% be an integer and for three non-zero dimensions its third root must be
% an integer. For the "Array" configuration, there is not an issue as the
% setup is one dimensional.
% %
% - The position of the geometry is calculated like this: First the
% geometry is placed on the x-y plane, then rotated to match the
% orientation defined by the "orient" argument and finally, the offsets
% on each axis are applied.
% -------------------------------------------------- % --------------------------------------------------
function [vPos, vPosMesh] = virtMicGeo(gType, xLen, xOff, yOff, zOff, nSens, orient) function [vPos, vPosMesh] = virtMicGeo(gType, geoDim, nSens, xOff, yOff, zOff, orient)
% ==================================================== % ====================================================
% Check for number of arguments % Check for number of arguments
% ==================================================== % ====================================================
...@@ -91,81 +93,67 @@ function [vPos, vPosMesh] = virtMicGeo(gType, xLen, xOff, yOff, zOff, nSens, ori ...@@ -91,81 +93,67 @@ function [vPos, vPosMesh] = virtMicGeo(gType, xLen, xOff, yOff, zOff, nSens, ori
% ==================================================== % ====================================================
% Validate mandatory arguments % Validate mandatory arguments
validateattributes(gType, {'char', 'string'}, {'scalartext', 'nonempty'}, mfilename, "Geometry type", 1); validateattributes(gType, {'char', 'string'}, {'scalartext', 'nonempty'}, mfilename, "Geometry type", 1);
validatestring(gType, ["Single", "Dual", "Array", "Grid", "Cube"], mfilename, "Geometry type", 1); validatestring(gType, ["Single", "Dual", "Array", "Cube"], mfilename, "Geometry type", 1);
% Validate optional arguments % Validate optional arguments
if nargin > 1 && ~isempty(xLen) && ~strcmpi(gType, "Single") if nargin > 1 && ~isempty(geoDim) && ~strcmpi(gType, "Single")
validateattributes(xLen, "numeric", {'scalar', 'real', 'nonnan', 'finite', 'positive'}, mfilename, "Width of the geometry, or distance between positions in Dual geometry", 2); validateattributes(geoDim, "numeric", {'vector', 'real', 'nonnan', 'finite', 'nonempty', 'positive'}, mfilename, "Geometry dimensions", 2);
if numel(geoDim) > 3 || numel(geoDim) == 2
error("The dimensions argument must be either a scalar or a vector with three elements");
end
if isscalar(geoDim)
geoDim = geoDim * ones(3, 1);
end
else else
if strcmpi(gType, "Dual") geoDim = ones(3, 1);
xLen = 0.2; end
else
xLen = 1; if nargin > 2 && ~isempty(nSens) && sum(strcmpi(gType, ["Single", "Dual"])) == 0
validateattributes(nSens, "numeric", {'vector', 'real', 'nonempty', 'nonnan', 'nonnegative', 'integer', 'finite'}, mfilename, "Number of sensors in the geometry", 3);
if numel(nSens) > 3 || numel(nSens) == 2
error("The number of sensors must be either a scalar or a vector with three elements");
end
if isscalar(nSens) && strcmpi(gType, "Cube")
if mod(nthroot(nSens, sum(geoDim ~= 0)), 1) ~= 0
error("The number of sources is cannot be divided uniformly over the non-zero dimensions");
else
nSens = nthroot(nSens, sum(geoDim ~= 0)) * double(geoDim ~= 0);
end
end end
elseif strcmpi(gType, "Dual")
nSens = [2, 0, 0];
else
nSens = 10 * ones(3, 1);
end end
if nargin > 2 && ~isempty(xOff) if nargin > 3 && ~isempty(xOff)
validateattributes(xOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "X-offset", 3); validateattributes(xOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "X-offset", 4);
else else
xOff = 0; xOff = 0;
end end
if nargin > 3 && ~isempty(yOff) if nargin > 4 && ~isempty(yOff)
validateattributes(yOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "Y-offset", 4); validateattributes(yOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "Y-offset", 5);
else else
yOff = 0; yOff = 0;
end end
if nargin > 4 && ~isempty(zOff) if nargin > 5 && ~isempty(zOff)
validateattributes(zOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "Z-offset", 5); validateattributes(zOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "Z-offset", 6);
else else
zOff = 0; zOff = 0;
end end
if nargin > 5 && ~isempty(nSens) if nargin > 6 && ~isempty(orient) && ~strcmpi(gType, "Single")
if ~strcmpi(gType, "Single") validateattributes(orient, {'numeric'}, {'vector', 'nonempty', 'nonnan', 'finite', 'real', 'numel', 3}, mfilename, "Geometry rotation around the Cartesian axes", 7);
validateattributes(nSens, "numeric", {'scalar', 'real', 'nonnegative', 'finite', 'nonnan'}, mfilename, "Number of sensors", 6);
end
else
if sum(strcmpi(gType, ["Array", "Grid"])) > 0
nSens = 1e2;
elseif strcmpi(gType, "Cube")
nSens = 1e3;
else
nSens = 0;
end
end
if nargin > 6 && ~isempty(orient)
if ischar(orient) || isstring(orient)
validateattributes(orient, {'char', 'string'}, {'scalartext', 'nonempty'}, mfilename, "Geometry orientation", 7);
validatestring(orient, ["XY", "XZ", "YZ"], mfilename, "Geometry orientation", 7);
else
if ~strcmpi(gType, "Dual")
error("Planes can be provided for the orientation of only the Array and Grid geometries.");
end
validateattributes(orient, {'numeric'}, {'scalar', 'nonempty', 'nonnan', 'finite', 'real'}, mfilename, "Geometry orientation", 7);
end
else else
if strcmpi(gType, "Dual") orient = zeros(3, 1);
orient = 0;
else
orient = "XY";
end
end end
% ====================================================
% Check we have all needed parameters and they have
% acceptable values
% ====================================================
% For "Grid" geometry the square root of the number of sensors must be an integral value
if strcmpi(gType, "Grid") && mod(sqrt(nSens), 1) ~= 0
error("For the Grid geometry, the square root of the number of sensors must be an integer.");
elseif strcmpi(gType, "Cube") && mod(nthroot(nSens, 3), 1) ~= 0
error("For the Cube geometry, the cubic root of the number of sensors must be an integer (a tolerance of 1e-6 has been used for the calculation).");
end
% ==================================================== % ====================================================
% Calculate virtual microphone positions % Calculate virtual microphone positions
...@@ -173,53 +161,49 @@ function [vPos, vPosMesh] = virtMicGeo(gType, xLen, xOff, yOff, zOff, nSens, ori ...@@ -173,53 +161,49 @@ function [vPos, vPosMesh] = virtMicGeo(gType, xLen, xOff, yOff, zOff, nSens, ori
switch lower(gType) switch lower(gType)
case "single" case "single"
% Create a single point at offset coordinates and return % Create a single point at offset coordinates and return
vPos = [xOff, yOff, zOff]; vPos = zeros(1, 3);
vPosMesh = [];
return;
case "dual" case "dual"
vPos = [-xLen/2, 0, 0; ... vPos = [-geoDim(1)/2, 0, 0; ...
xLen/2, 0, 0]; geoDim(1)/2, 0, 0];
case "array" case "array"
vPos = linspace(-xLen/2, xLen/2, nSens); vPos = linspace(-geoDim(1)/2, geoDim(1)/2, nSens(1));
vPos = [vPos(:), zeros(numel(vPos), 2)]; vPos = [vPos(:), zeros(numel(vPos), 2)];
case "grid"
vPos = linspace(-xLen/2, xLen/2, sqrt(nSens));
[x, y] = meshgrid(vPos, vPos);
vPos = [x(:), y(:), zeros(numel(x), 1)];
case "cube" case "cube"
vPos = linspace(-xLen/2, xLen/2, round(nSens^(1/3))); % Make we don't get empty arrays when dimensions are 0
[x, y, z] = meshgrid(vPos, vPos, vPos); if nSens(1) == 0
x = 0;
else
x = linspace(-geoDim(1)/2, geoDim(1)/2, nSens(1));
end
if nSens(2) == 0
y = 0;
else
y = linspace(-geoDim(2)/2, geoDim(2)/2, nSens(2));
end
if nSens(3) == 0
z = 0;
else
z = linspace(-geoDim(3)/2, geoDim(3)/2, nSens(3));
end
[x, y, z] = meshgrid(x, y, z);
vPos = [x(:), y(:), z(:)]; vPos = [x(:), y(:), z(:)];
otherwise otherwise
error("Oops... something went wrong here... not known geometry...!!!"); error("Oops... something went wrong here... not known geometry...!!!");
end end
% Rotate % Rotate
if ~strcmpi(gType, "Cube") vPos = vPos * rotMat3d(-orient(1), -orient(2), -orient(3), "Degs");
if isnumeric(orient)
vPos = vPos * rotMat3d(0, 0, -orient, "Degs");
elseif strcmpi(orient, "XZ")
if strcmpi(gType, "array")
vPos = vPos * rotMat3d(0, 0, 90, "Degs");
else
vPos = vPos * rotMat3d(90, 0, 0, "Degs");
end
elseif strcmpi(orient, "YZ")
vPos = vPos * rotMat3d(0, 90, 0, "Degs");
end
end
% Translate % Translate
if ~strcmpi(gType, "Cube") vPos = vPos + [xOff, yOff, zOff];
vPos = vPos + [xOff, yOff, zOff];
end
% For "Grid" and "Cube" geometry provide the coordinates in a "mesh format" % For "Cube" geometry provide the coordinates in a "mesh format"
if strcmpi(gType, "Grid") if nargout > 1 && strcmpi(gType, "Cube")
vPosMesh = reshape(vPos, sqrt(nSens), [], 3); vPosMesh = cat(3, x, y, z);
elseif strcmpi(gType, "Cube") elseif nargout > 1
vPosMesh = reshape(vPos, round(nSens^(1/3)), round(nSens^(1/3)), [], 3);
else
vPosMesh = []; vPosMesh = [];
end end
end end
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment