diff --git a/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m new file mode 100644 index 0000000000000000000000000000000000000000..09a53a42a368f7482eadcaa05b4ac3afa0bad836 --- /dev/null +++ b/Virtual Sensing/Remote Microphone Technique/MATLAB/Functions/obsFiltTD.m @@ -0,0 +1,247 @@ +%% Calculate optimal observation filters in the time domain +% -------------------------------------------------- +% Author: Achilles Kappis +% e-mail: axilleaz@protonmail.com +% +% Date: 12/09/2024 (DD/MM/YYYY) +% +% Copyright: MIT +% -------------------------------------------------- +% Functionality: Calculate the optimal, in the least squares sense, +% observation filters for the Remote Microphone Technique in +% the time domain. +% -------------------------------------------------- +% Input +% +% e [numeric]: The measurement(s) at the virtual microphone position(s). +% This must be an IxNxJ matrix with I the length of the +% measurements in samples, N the number of virtual microphones +% and J the number of trials/sound field realisations. +% +% m [numeric]: The measurement(s) at the monitoring microphone position(s). +% This must be an KxMxJ matrix with K representing the length +% of the measurements in samples, M the number of monitoring +% microphones and J the number of trials/sound field +% realisations. The number of trials must be the same as in +% the argument "e". +% +% beta [numeric] (Optional): The regularisation factor. This must be a real +% non-negative scalar. [Default: 0]. +% +% filtLen [numeric] (Optional): The length of the observation filters in +% samples. This must be a positive scalar, at +% less than or equal to K, the number of +% samples in the monitoring microphone +% signals. [Default: size(m, 2)]. +% +% delay [numeric] (Optional): The delay to be added to the virtual +% microphone signals, effectively implementing +% the delayed Remote Microphone Technique. This +% must be a real non-negative scalar less than +% or equal to I, the length of the virtual +% microphone signals. [Default: 0]. +% +% -------------------------------------------------- +% Output +% +% O [numeric]: The observation filters. This is an firLenxNxMxJ array. +% +% Rme [numeric]: The cross-correlation matrices between the monitoring and +% virtual microphone signals. This is an filtLenxMxNxJ +% array. +% +% Rmm [numeric]: The cross-correlation matrices of the monitoring +% microphone signals. This is an filtLenxfiltLenxMxMxJ +% array. +% +% Ovec [numeric]: This is a matrix with the observation filters +% stacked/vectorised, so that they can be applied to a +% stacked/vectorised monitoring microphone measurements +% vector. The dimensions of this matrix are NxTotFiltLenxJ, +% where TotFiltLen is equal to M times filtLen. This way, +% stacking the monitoring microphone measured signals one +% can perform Ovec(:, :, jIdx) * dm (dm is the vectorised +% monitoring microphone signals vector) to calculate the +% estimated measurements at the N virtual microphone +% positions. +% +% RmeMtx [numeric]: This is the matrix with the cross-correlation vectors +% between the monitoring and virtual microphones +% stacked/vectorised. The dimensions of the matrix are +% NxTotFiltLenxJ. +% +% RmmMtx [numeric]: This is the matrix with the cross-correlation matrices +% of the monitoring microphone signals stacked together. +% The dimensions are TotFiltLenxTotFiltLenxJ. +% +% mMtx [numeric]: The monitoring microphone signals vectorised so that they +% can be used directly with either "Ovec" or "Oopt" +% arguments to perform the filtering/estimation. +% +% Omean [numeric]: The observation filters averaged over the trials/sound +% field realisations. This is an NxMxfirLen array. +% +% Rmemean [numeric]: The cross-correlation matrices between the monitoring +% and virtual microphone signals averaged over the +% trials/sound field realisations. This is an +% NxMxfirLen array. +% +% RmmMean [numeric]: The cross-correlation matrices of the monitoring +% microphone signals averaged over the trials/sound +% field realisations. This is an MxMxfirLenxfirLen +% array. +% +% Oopt [numeric]: This is a matrix with the observation filters +% stacked/vectorised, so that they can be applied to a +% stacked/vectorised monitoring microphone measurements +% vector, averaged over the trials/sound field +% realisations. The dimensions of this matrix are +% NxTotFiltLen. These filters are denoted "optimal" since +% they are averaged over many realisations (if J > 1). +% +% RmeMtxMean [numeric]: This is the matrix with the cross-correlation +% vectors between the monitoring and virtual +% microphones stacked/vectorised, averaged over the +% trials/sound field realisations. The dimensions of +% the matrix are NxTotFiltLen. +% +% RmmMtxMean [numeric]: This is the matrix with the cross-correlation +% matrices of the monitoring microphone signals +% stacked together and averaged over the trials/sound +% field realisations. The dimensions are +% TotFiltLenxTotFiltLen. +% +% -------------------------------------------------- +% Notes +% +% - The calculations are based on the paper: "Modeling local active sound +% control with remote sensors in spatially random pressure fields" by +% Stephen J. Elliott and Jordan Cheer. +% +% -------------------------------------------------- +function [O, Rme, Rmm, Ovec, RmeMtx, RmmMtx, mMtx, Omean, RmeMean, RmmMean, Oopt, RmeMtxMean, RmmMtxMean] = obsFiltTD(e, m, beta, filtLen, delay) + % ==================================================== + % Check for number of arguments + % ==================================================== + narginchk(2, 5); + nargoutchk(0, 13); + + % ==================================================== + % Validate input arguments + % ==================================================== + % Validate mandatory arguments + validateattributes(e, "numeric", {'3d', 'real', 'nonnan', 'nonempty', 'finite'}, mfilename, "Virtual microphone measurements", 1); + validateattributes(m, "numeric", {'3d', 'real', 'nonnan', 'nonempty', 'finite', 'size', [NaN, NaN, size(e, 3)]}, mfilename, "Monitoring microphone measurements", 2); + + % Validate optional arguments + if nargin > 2 && ~isempty(beta) + validateattributes(beta, "numeric", {'scalar', 'nonnegative', 'nonnan', 'nonempty', 'finite'}, mfilename, "Regularisation factor", 3); + else + beta = 0; + end + + if nargin > 3 && ~isempty(filtLen) + validateattributes(filtLen, "numeric", {'scalar', 'positive', 'nonnan', 'nonempty', 'finite', '<=', size(m, 1)}, mfilename, "Length of observation filters", 4); + else + filtLen = size(m, 2); + end + + if nargin > 4 && ~isempty(delay) + validateattributes(delay, "numeric", {'scalar', 'nonnegative', 'nonnan', 'finite', 'nonempty', '<=', size(e, 1)}, mfilename, "Delay to be added to virtual microphone signals to implement the delayed Remote Microphone Technique", 5); + else + delay = 0; + end + + + % ==================================================== + % Calculate cross- and auto-correlation matrices + % ==================================================== + % Go through the trials/sound field realisations + for jIdx = size(m, 3):-1:1 + % Go through the monitoring microphones + 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); + + Rme(:, mIdx, eIdx, jIdx) = corr(delay + lIdx:-1:lIdx-filtLen + 1); + 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); + + Rmm(:, :, mIdx, mmIdx, jIdx) = toeplitz(corr(lIdx:-1:lIdx - filtLen + 1)); + else + [corr, lags] = xcorr(m(:, mIdx, jIdx), m(:, mmIdx, jIdx)); + lIdx = find(lags == 0); + + % Cross-correlation matrices + for iIdx = filtLen-1:-1:0 + Rmm(:, iIdx + 1, mIdx, mmIdx, jIdx) = corr(iIdx + (lIdx:-1:lIdx - filtLen + 1)); + end + Rmm(:, :, mmIdx, mIdx, jIdx) = squeeze(Rmm(:, :, mIdx, mmIdx, jIdx)).'; + end + end + end + end + + % ==================================================== + % 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)); + + % ==================================================== + % Calculate observation filters + % ==================================================== + for jIdx = size(RmmMtx, 3):-1:1 + Ovec(:, :, jIdx) = RmeMtx(:, :, jIdx).'/(RmmMtx(:, :, jIdx) + beta * eye(size(RmmMtx, 1))); + 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]); + + % ==================================================== + % Provide additional output arguments + % ==================================================== + % The monitoring microphone signals vectorised per trial/sound field realisation + if nargout > 6 + mMtx = reshape(m, prod(size(m, [1, 2])), size(m, 3)); + end + + % Mean observation over trials/sound field realisations + if nargout > 7 + Omean = mean(O, 4); + end + + % Mean cross-correlations between monitoring and virtual microphones over trials/sound field realisations + if nargout > 8 + RmeMean = mean(Rme, 4); + end + + % Mean cross-correlations of monitoring microphones over trials/sound field realisations + if nargout > 9 + RmmMean = mean(Rmm, 5); + end + + % Average of optimal observation filters over trials/sound field realisations + if nargout > 10 + Oopt = mean(Ovec, 3); + end + + % Mean cross-correlation matrix between monitoring and virtual microphones over trials/sound field realisations + if nargout > 11 + RmeMtxMean = mean(RmeMtx, 3); + end + + % Mean cross-correlation matrix of monitoring microphones over trials/sound field realisations + if nargout > 12 + RmmMtxMean = mean(RmmMtx, 3); + end +end \ No newline at end of file