From ffe296b975d2c22fff7969bd34f18d97d6707dc6 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 12:01:36 +0100
Subject: [PATCH 01/13] Fix structure of RmeMtx and RmmMtx in obsFiltTD.m

---
 .../MATLAB/Functions/obsFiltTD.m              | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m
index 77ecd87..b0be8a2 100644
--- a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m	
+++ b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m	
@@ -211,27 +211,24 @@ function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, condNum, mMtx, Omean, RmeMean, RmmM
         for mIdx = size(m, 2):-1:1
             % Calculate the cross-correlations between virtual and monitoring microphones
             for eIdx = size(e, 2):-1:1
-                [corr, lags] = xcorr(m(:, mIdx, jIdx), e(:, eIdx, jIdx));
-                lIdx = find(lags == 0);
+                corr = xcorr(m(:, mIdx, jIdx), e(:, eIdx, jIdx), filtLen, "unbiased");
 
-                Rme(:, mIdx, eIdx, jIdx) = corr(lIdx:-1:lIdx-filtLen + 1);
+                Rme(:, mIdx, eIdx, jIdx) = corr(filtLen + 1:-1:2);
             end
 
             % Go through the monitoring microphones to calculate the monitoring microphone correlation matrices
             for mmIdx = mIdx:-1:1
                 % Auto-correlation matrices are Toeplitz symmetric
                 if mIdx == mmIdx
-                    [corr, lags] = xcorr(m(:, mmIdx, jIdx), m(:, mmIdx, jIdx));
-                    lIdx = find(lags == 0);
+                    corr = xcorr(m(:, mmIdx, jIdx), m(:, mmIdx, jIdx), filtLen, "unbiased");
 
-                    Rmm(:, :, mIdx, mmIdx, jIdx) = toeplitz(corr(lIdx:-1:lIdx - filtLen + 1));
+                    Rmm(:, :, mIdx, mmIdx, jIdx) = toeplitz(corr(filtLen + 1:-1:2));
                 else
-                    [corr, lags] = xcorr(m(:, mIdx, jIdx), m(:, mmIdx, jIdx));
-                    lIdx = find(lags == 0);
+                    corr = xcorr(m(:, mIdx, jIdx), m(:, mmIdx, jIdx), filtLen, "unbiased");
 
                     % Cross-correlation matrices
                     for iIdx = filtLen-1:-1:0
-                        Rmm(:, iIdx + 1, mIdx, mmIdx, jIdx) = corr(iIdx + (lIdx:-1:lIdx - filtLen + 1));
+                        Rmm(:, iIdx + 1, mIdx, mmIdx, jIdx) = corr(iIdx + (filtLen + 1:-1:2));
                     end
                     Rmm(:, :, mmIdx, mIdx, jIdx) = squeeze(Rmm(:, :, mIdx, mmIdx, jIdx)).';
                 end
@@ -243,8 +240,8 @@ function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, condNum, mMtx, Omean, RmeMean, RmmM
     % Post-process cross- and auto-correlation matrices
     % ====================================================
     % "Reshape" the data
-    RmeMtx = reshape(Rme, prod(size(Rme, [1, 2])), size(Rme, 3), size(Rme, 4));
-    RmmMtx = reshape(permute(Rmm, [1, 3, 2, 4, 5]), prod(size(Rmm, [1, 3])), prod(size(Rmm, [2, 4])), size(Rmm, 5));
+    RmeMtx = reshape(permute(Rme, [2, 1, 3, 4]), prod(size(Rme, [1, 2])), size(Rme, 3), size(Rme, 4));
+    RmmMtx = reshape(permute(Rmm, [3, 1, 4, 2, 5]), prod(size(Rmm, [1, 3])), prod(size(Rmm, [2, 4])), size(Rmm, 5));
 
     % ====================================================
     % Calculate observation filters
-- 
GitLab


From af05ac10af7b6530977351b4345ad0cb9fd4840e Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 12:13:05 +0100
Subject: [PATCH 02/13] Update date in obsFiltTD.m

---
 .../Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m
index b0be8a2..eaef498 100644
--- a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m	
+++ b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m	
@@ -3,7 +3,7 @@
 % Author: Achilles Kappis
 % e-mail: axilleaz@protonmail.com
 %
-% Date: 15/09/2024 (DD/MM/YYYY)
+% Date: 17/09/2024 (DD/MM/YYYY)
 %
 % Copyright: MIT
 % --------------------------------------------------
-- 
GitLab


From 8e4c0cef5d90954753e26450a331c94d57346556 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 16:12:47 +0100
Subject: [PATCH 03/13] Fix separation of observation filters from the
 vectorised filter in obsFiltTD.m

---
 .../MATLAB/Functions/obsFiltTD.m                       | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m
index eaef498..b09f705 100644
--- a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m	
+++ b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m	
@@ -251,7 +251,11 @@ function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, condNum, mMtx, Omean, RmeMean, RmmM
     end
 
     % "Split" observation filter vector to observation filters per monitoring and virtual microphone 
-    O = permute(reshape(permute(Ovec, [2, 1, 3]), filtLen, size(m, 2), size(e, 2), size(m, 3)), [3, 1, 2, 4]);
+    for jIdx = size(Ovec, 3):-1:1
+        for mIdx = size(m, 2):-1:1
+            O(:, :, mIdx, jIdx) = Ovec(:, mIdx:size(m, 2):end, jIdx);
+        end
+    end
 
     % ====================================================
     % Provide additional output arguments
@@ -278,7 +282,9 @@ function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, condNum, mMtx, Omean, RmeMean, RmmM
         Oopt = RmeMtxMean.'/(RmmMtxMean + beta * eye(size(RmmMtxMean)));
 
         % Reshape
-        Omean = permute(reshape(Oopt.', filtLen, size(m, 2), size(e, 2)), [3, 1, 2]);
+        for mIdx = size(m, 2):-1:1
+            Omean(:, :, mIdx) = Oopt(:, mIdx:size(m, 2):end);
+        end
     end
     
     % Mean cross-correlations between monitoring and virtual microphones over trials/sound field realisations
-- 
GitLab


From e0aba2698959be90366fef36f2f5706fff001ff2 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 17:48:50 +0100
Subject: [PATCH 04/13] Fix bug in obsFiltEstTD.m that would screw dimension
 when there is only one source present

---
 .../Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m
index d8827b8..804c205 100644
--- a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m	
+++ b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m	
@@ -111,7 +111,7 @@ function [estPerMic, est, err, estMean, errMean] = obsFiltEstTD(m, O, e)
 
     % Sum the estimates of each monitoring microphone to get the estimated virtual microphone signals
     if nargout > 1
-        est = squeeze(sum(estPerMic, 3));
+        est = reshape(sum(estPerMic, 3), size(estPerMic, 1), size(estPerMic, 2), size(estPerMic, 4));
     end
 
     % Calculate the error signals
-- 
GitLab


From 143d9f094c3a9e29050a13e61b4794a36e9f70ca Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 17:50:01 +0100
Subject: [PATCH 05/13] Fix bug in ptSrcFieldTD.m that would screw dimension
 when there is only one source present

---
 Sound Fields/MATLAB/Functions/ptSrcFieldTD.m | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m b/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m
index 422cb6e..0c17c25 100644
--- a/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m	
+++ b/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m	
@@ -170,7 +170,7 @@ function [rSig, rSigMean, rSigMtx, rSigMtxMean, Q] = ptSrcFieldTD(sPos, rPos, fs
     end
 
     % Sum source signals at each receiver position
-    rSig = squeeze(sum(rSigMtx, 3));
+    rSig = reshape(sum(rSigMtx, 3), size(rSigMtx, 1), size(rSigMtx, 2), size(rSigMtx, 4));
 
     % ====================================================
     % Calculate output arguments
-- 
GitLab


From bf1d1ecd7a38be714d96183500eebb840affb6c8 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 17:50:36 +0100
Subject: [PATCH 06/13] Update date in ptSrcFieldTD.m

---
 Sound Fields/MATLAB/Functions/ptSrcFieldTD.m | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m b/Sound Fields/MATLAB/Functions/ptSrcFieldTD.m
index 0c17c25..ddfa6a6 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: 15/09/2024 (DD/MM/YYYY)
+% Date: 17/09/2024 (DD/MM/YYYY)
 %
 % Copyright: MIT
 % --------------------------------------------------
-- 
GitLab


From 897456450b0e30d4423c759bdefc506a72f73c39 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 17:53:06 +0100
Subject: [PATCH 07/13] Remove scaling from cross-correlations and reshape
 observation filters with single liners instead of loops in obsFiltTD.m

---
 .../MATLAB/Functions/obsFiltTD.m               | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m
index b09f705..43cf7fb 100644
--- a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m	
+++ b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m	
@@ -211,7 +211,7 @@ function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, condNum, mMtx, Omean, RmeMean, RmmM
         for mIdx = size(m, 2):-1:1
             % Calculate the cross-correlations between virtual and monitoring microphones
             for eIdx = size(e, 2):-1:1
-                corr = xcorr(m(:, mIdx, jIdx), e(:, eIdx, jIdx), filtLen, "unbiased");
+                corr = xcorr(m(:, mIdx, jIdx), e(:, eIdx, jIdx), filtLen);
 
                 Rme(:, mIdx, eIdx, jIdx) = corr(filtLen + 1:-1:2);
             end
@@ -220,11 +220,11 @@ function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, condNum, mMtx, Omean, RmeMean, RmmM
             for mmIdx = mIdx:-1:1
                 % Auto-correlation matrices are Toeplitz symmetric
                 if mIdx == mmIdx
-                    corr = xcorr(m(:, mmIdx, jIdx), m(:, mmIdx, jIdx), filtLen, "unbiased");
+                    corr = xcorr(m(:, mmIdx, jIdx), m(:, mmIdx, jIdx), filtLen);
 
                     Rmm(:, :, mIdx, mmIdx, jIdx) = toeplitz(corr(filtLen + 1:-1:2));
                 else
-                    corr = xcorr(m(:, mIdx, jIdx), m(:, mmIdx, jIdx), filtLen, "unbiased");
+                    corr = xcorr(m(:, mIdx, jIdx), m(:, mmIdx, jIdx), filtLen);
 
                     % Cross-correlation matrices
                     for iIdx = filtLen-1:-1:0
@@ -250,12 +250,8 @@ function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, condNum, mMtx, Omean, RmeMean, RmmM
         Ovec(:, :, jIdx) = RmeMtx(:, :, jIdx).'/(RmmMtx(:, :, jIdx) + beta * eye(size(RmmMtx, 1)));
     end
 
-    % "Split" observation filter vector to observation filters per monitoring and virtual microphone 
-    for jIdx = size(Ovec, 3):-1:1
-        for mIdx = size(m, 2):-1:1
-            O(:, :, mIdx, jIdx) = Ovec(:, mIdx:size(m, 2):end, jIdx);
-        end
-    end
+    % "Split" observation filter vector to observation filters per monitoring and virtual microphone
+    O = permute(reshape(Ovec, size(Ovec, 1), size(m, 2), filtLen, size(Ovec, 3)), [1, 3, 2, 4]);
 
     % ====================================================
     % Provide additional output arguments
@@ -282,9 +278,7 @@ function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, condNum, mMtx, Omean, RmeMean, RmmM
         Oopt = RmeMtxMean.'/(RmmMtxMean + beta * eye(size(RmmMtxMean)));
 
         % Reshape
-        for mIdx = size(m, 2):-1:1
-            Omean(:, :, mIdx) = Oopt(:, mIdx:size(m, 2):end);
-        end
+        Omean = permute(reshape(Oopt, size(Oopt, 1), size(m, 2), filtLen), [1, 3, 2]);
     end
     
     % Mean cross-correlations between monitoring and virtual microphones over trials/sound field realisations
-- 
GitLab


From 5a92515611519d0a6a63f904d0be2834825b5810 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 17:53:31 +0100
Subject: [PATCH 08/13] Update date in obsFiltEstTD.m

---
 .../Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m
index 804c205..4ac8d10 100644
--- a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m	
+++ b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltEstTD.m	
@@ -3,7 +3,7 @@
 % Author: Achilles Kappis
 % e-mail: axilleaz@protonmail.com
 %
-% Date: 14/09/2024 (DD/MM/YYYY)
+% Date: 17/09/2024 (DD/MM/YYYY)
 %
 % Copyright: MIT
 % --------------------------------------------------
-- 
GitLab


From 3982da70818409a48b9e7d90be7e7589681b2691 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 18:03:29 +0100
Subject: [PATCH 09/13] Update README.md of Virtual Sensing folder

---
 Virtual Sensing/README.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Virtual Sensing/README.md b/Virtual Sensing/README.md
index fc21835..953cc48 100644
--- a/Virtual Sensing/README.md	
+++ b/Virtual Sensing/README.md	
@@ -11,6 +11,7 @@ The current structure of the section is shown below
 - Remote Microphone Technique
   - Observation filter calculation
     - Frequency domain (tonal)
+    - Time domain (broadband and causal)
 
 
 ## Dependencies
@@ -51,4 +52,5 @@ None.
 
 ## References
 
-[1] A. Roure, A. Albarrazin, *"The remote microphone technique for active noise control"*,  Inter-Noise and Noise-Con Congress and Conference Proceedings, Active99, Fort Lauderdale FL, pp. 1233-1244(12).
+[1] A. Roure, A. Albarrazin, *"The remote microphone technique for active noise control"*, Inter-Noise and Noise-Con Congress and Conference Proceedings, Active99, Fort Lauderdale FL, pp. 1233-1244(12).
+[2] S. J. Elliott, J. Cheer, *"Modeling local active sound control with remote sensors in spatially random pressure fields", Journal of the Acoustical Society of America, 137, pp. 1936-1946, (2015).
-- 
GitLab


From 056906c87a5347382862c4a8750a8af7dea3ad62 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 18:04:13 +0100
Subject: [PATCH 10/13] Update version in project's README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index ed4f39a..8a3cb46 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,7 @@ The project is licensed under the ***MIT License***, which is a rather unrestric
 
 
 ## Versioning ##
-The project uses [semantic versioning](https://semver.org/) and the current version is **v0.2.3**.
+The project uses [semantic versioning](https://semver.org/) and the current version is **v0.2.4**.
 
 
 #### **Important**
-- 
GitLab


From 31978c0881be2e99a30e83a3ac651596189192c2 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 17 Sep 2024 18:04:54 +0100
Subject: [PATCH 11/13] Update CHANGELOG.md with changes of v0.2.4

---
 CHANGELOG.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8883239..f45ce55 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+### v0.2.4 ###
+**Virtual Sensing**\
+\* Corrected time-domain observation filter calculations.\
+\* Removed scaling from the cross-correlation calculations in the time-domain optimal observation filters.\
+\* Fixed bug in time-domaiin observation filter that screwed up dimensions of the filters when only one source was present.
+
+**Sound Fields**\
+\* Fixed bug in time-domain point source signal calculations that screwed up dimensions of the resulting arrays when only one source was present. 
+
+
 ### v0.2.3 ###
 **Virtual Sensing**\
 \+ Added function to estimate the observation filters in the time-domain.\
-- 
GitLab


From 09f272de68295900f31382654b48d160a817509a Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Wed, 18 Sep 2024 21:29:51 +0100
Subject: [PATCH 12/13] Update interface and corresponding internal
 calculations of the virtMicGeo.m function

---
 .../Geometries/MATLAB/Functions/virtMicGeo.m  | 248 ++++++++----------
 1 file changed, 116 insertions(+), 132 deletions(-)

diff --git a/Utilities/Geometries/MATLAB/Functions/virtMicGeo.m b/Utilities/Geometries/MATLAB/Functions/virtMicGeo.m
index 564dfa3..b6947be 100644
--- a/Utilities/Geometries/MATLAB/Functions/virtMicGeo.m
+++ b/Utilities/Geometries/MATLAB/Functions/virtMicGeo.m
@@ -3,7 +3,7 @@
 % Author: Achilles Kappis
 % e-mail: axilleaz@protonmail.com
 %
-% Date: 16/07/2024 (DD/MM/YYYY)
+% Date: 18/09/2024 (DD/MM/YYYY)
 %
 % Copyright: MIT
 % --------------------------------------------------
@@ -13,17 +13,35 @@
 % Input
 % 
 % gType [char/string]: The type of the source geometry. At the moment the
-%                      available options are "Single", "Array", "Grid",
-%                      "Cube" and "Dual". The last is an arrangement with 2
-%                      sensors "xLen" apart that can be translated and
-%                      rotated.
+%                      available options are "Single", "Array", "Cube" and
+%                      "Dual". The last is an arrangement a distance apart
+%                      that can be translated and 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
-%                            of the side of the "Grid" and "Cube geometries
-%                            and the distance between positions in "Dual"
-%                            geometry.
-%                            [Default: Array/Grid/Cube - 1, Dual - 0.2].
+% nSens [numeric]  (Optional): This can be either a real integer value
+%                              denoting the number of sensors in the array
+%                              or a vector with three elements denoting
+%                              the number of sensor along each Cartesian
+%                              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].
 % 
@@ -31,28 +49,10 @@
 % 
 % zOff [numeric] (Optional): Z-axis offset. [Default: 0].
 % 
-% nSens [numeric]  (Optional): The number of sensors in the array.
-%                              For the "Grid" geometry the square root of
-%                              "nSens" must be an integer value. For the
-%                              "Cube" geometry the cubic root of "nSens"
-%                              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].
+% orient [numeric] (Optional): This is a real vector holding the rotation
+%                              for the geometry in degrees, along each
+%                              Cartesian axis. The rotations are performed
+%                              clockwise. [Default: zeros(3, 1)].
 % 
 % --------------------------------------------------
 % Output
@@ -62,24 +62,26 @@
 %                 number of sources and the other dimension correspond to
 %                 the x, y and z coodrinates.
 % 
-% vPosMesh [numeric]: The matrix has dimensions sqrt(N)xsqrt(N)x3 where N
-%                     is the number of sources. It contains the x, y and z
-%                     Cartesian coordinates for each position in a
-%                     rectangular grid with dimensions sqrt(N)xsqrt(N).
-%                     This is used only with the "Grid" geometry, otherwise
-%                     it is empty.
+% 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.
 % 
 % --------------------------------------------------
 % 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
     % ====================================================
@@ -91,81 +93,67 @@ function [vPos, vPosMesh] = virtMicGeo(gType, xLen, xOff, yOff, zOff, nSens, ori
     % ====================================================
     % Validate mandatory arguments
     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
-    if nargin > 1 && ~isempty(xLen) && ~strcmpi(gType, "Single")
-        validateattributes(xLen, "numeric", {'scalar', 'real', 'nonnan', 'finite', 'positive'}, mfilename, "Width of the geometry, or distance between positions in Dual geometry", 2);
+    if nargin > 1 && ~isempty(geoDim) && ~strcmpi(gType, "Single")
+        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
-        if strcmpi(gType, "Dual")
-            xLen = 0.2;
-        else
-            xLen = 1;
+        geoDim = ones(3, 1);
+    end
+
+    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
+    elseif strcmpi(gType, "Dual")
+        nSens = [2, 0, 0];
+    else
+        nSens = 10 * ones(3, 1);
     end
 
-    if nargin > 2 && ~isempty(xOff)
-        validateattributes(xOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "X-offset", 3);
+    if nargin > 3 && ~isempty(xOff)
+        validateattributes(xOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "X-offset", 4);
     else
         xOff = 0;
     end
 
-    if nargin > 3 && ~isempty(yOff)
-        validateattributes(yOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "Y-offset", 4);
+    if nargin > 4 && ~isempty(yOff)
+        validateattributes(yOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "Y-offset", 5);
     else
         yOff = 0;
     end
 
-    if nargin > 4 && ~isempty(zOff)
-        validateattributes(zOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "Z-offset", 5);
+    if nargin > 5 && ~isempty(zOff)
+        validateattributes(zOff, "numeric", {'scalar', 'real', 'nonnan', 'finite'}, mfilename, "Z-offset", 6);
     else
         zOff = 0;
     end
 
-    if nargin > 5 && ~isempty(nSens)
-        if ~strcmpi(gType, "Single")
-            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
+    if nargin > 6 && ~isempty(orient) && ~strcmpi(gType, "Single")
+        validateattributes(orient, {'numeric'}, {'vector', 'nonempty', 'nonnan', 'finite', 'real', 'numel', 3}, mfilename, "Geometry rotation around the Cartesian axes", 7);
     else
-        if strcmpi(gType, "Dual")
-            orient = 0;
-        else
-            orient = "XY";
-        end
+        orient = zeros(3, 1);
     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
@@ -173,53 +161,49 @@ function [vPos, vPosMesh] = virtMicGeo(gType, xLen, xOff, yOff, zOff, nSens, ori
     switch lower(gType)
         case "single"
             % Create a single point at offset coordinates and return
-            vPos = [xOff, yOff, zOff];
-            vPosMesh = [];
-            return;
+            vPos = zeros(1, 3);
         case "dual"
-            vPos = [-xLen/2, 0, 0; ...
-                     xLen/2, 0, 0];
+            vPos = [-geoDim(1)/2, 0, 0; ...
+                     geoDim(1)/2, 0, 0];
         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)];
-        case "grid"
-            vPos = linspace(-xLen/2, xLen/2, sqrt(nSens));
-            [x, y] = meshgrid(vPos, vPos);
-            vPos = [x(:), y(:), zeros(numel(x), 1)];
         case "cube"
-            vPos = linspace(-xLen/2, xLen/2, round(nSens^(1/3)));
-            [x, y, z] = meshgrid(vPos, vPos, vPos);
+            % Make we don't get empty arrays when dimensions are 0
+            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(:)];
         otherwise
             error("Oops... something went wrong here... not known geometry...!!!");
     end
 
     % Rotate
-    if ~strcmpi(gType, "Cube")
-        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
+    vPos = vPos * rotMat3d(-orient(1), -orient(2), -orient(3), "Degs");
 
     % Translate
-    if ~strcmpi(gType, "Cube")
-        vPos = vPos + [xOff, yOff, zOff];
-    end
+    vPos = vPos + [xOff, yOff, zOff];
 
-    % For "Grid" and "Cube" geometry provide the coordinates in a "mesh format"
-    if strcmpi(gType, "Grid")
-        vPosMesh = reshape(vPos, sqrt(nSens), [], 3);
-    elseif strcmpi(gType, "Cube")
-        vPosMesh = reshape(vPos, round(nSens^(1/3)), round(nSens^(1/3)), [], 3);
-    else
+    % For "Cube" geometry provide the coordinates in a "mesh format"
+    if nargout > 1 && strcmpi(gType, "Cube")
+        vPosMesh = cat(3, x, y, z);
+    elseif nargout > 1
         vPosMesh = [];
     end
 end
\ No newline at end of file
-- 
GitLab


From 7b6002ca5d68df7e80a2a75bd5a83eefd2399125 Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Wed, 18 Sep 2024 21:30:28 +0100
Subject: [PATCH 13/13] Update CHANGELOG.md with virtMicGeo.m info

---
 CHANGELOG.md | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f45ce55..8c36569 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,10 +2,13 @@
 **Virtual Sensing**\
 \* Corrected time-domain observation filter calculations.\
 \* Removed scaling from the cross-correlation calculations in the time-domain optimal observation filters.\
-\* Fixed bug in time-domaiin observation filter that screwed up dimensions of the filters when only one source was present.
+\* Fixed bug in time-domain observation filter that screwed up dimensions of the filters when only one source was present.
 
 **Sound Fields**\
-\* Fixed bug in time-domain point source signal calculations that screwed up dimensions of the resulting arrays when only one source was present. 
+\* Fixed bug in time-domain point source signal calculations that screwed up dimensions of the resulting arrays when only one source was present.
+
+**Utilities - Geometries**\
+\* The virtual microphone geometry generation function has been heavily modified and its interface has changed in a **backward incompatible** way. It is now more generic and allows for "arbitrary" size of each Cartesian dimension of the geometries as well as different number of sensors along each dimension.
 
 
 ### v0.2.3 ###
-- 
GitLab