From 5c0313ed6d439d9c3d33f90f4abed5cb241cbffc Mon Sep 17 00:00:00 2001
From: ZaellixA <axilleaz@protonmail.com>
Date: Tue, 30 Jul 2024 15:42:03 +0100
Subject: [PATCH] Add planeWave function to calculate plane wave fields in the
 frequency domain at specified locations and frequencies

---
 Sound Fields/MATLAB/Functions/planeWave.m | 120 ++++++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100644 Sound Fields/MATLAB/Functions/planeWave.m

diff --git a/Sound Fields/MATLAB/Functions/planeWave.m b/Sound Fields/MATLAB/Functions/planeWave.m
new file mode 100644
index 0000000..2761008
--- /dev/null
+++ b/Sound Fields/MATLAB/Functions/planeWave.m	
@@ -0,0 +1,120 @@
+%% Calculate wavefield generated by plane waves in the frequency domain
+% --------------------------------------------------
+% Author: Achilles Kappis
+% e-mail: axilleaz@protonmail.com
+%
+% Date: 30/07/2024 (DD/MM/YYYY)
+%
+% Copyright: MIT
+% --------------------------------------------------
+% Functionality: Calculate the wavefield generated by plane waves at
+%                specified directions in the frequency domain.
+% --------------------------------------------------
+% 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
+%                 of incidence in [azimuth, elevation] format, expressed in
+%                 radians, or an Nx3 matrix with the entried 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.
+% 
+% f [numeric]: The frequencies for which to calculate the wave field. It
+%              must be a scalar or vector.
+% 
+% Q [numeric] (Optional): The source strength of the plane waves. This
+%                         variable must be either a scalar corresponding to
+%                         all sources having the same strength, or a matrix
+%                         with dimensions NxK, where K is the number of
+%                         frequencies of interest. The strengths can be
+%                         complex numbers. [Default: 1].
+% 
+% c [numeric] (Optional): The speed of sound in m/s. This variable is not
+%                         used if the wavenumbers are provided in the sDir
+%                         argument. [Default: 343].
+% 
+% --------------------------------------------------
+% Output
+% 
+% waveField [numeric]: The complex pressures at the position of the
+%                      receivers. The variable is a matrix of dimensions
+%                      MxNxF where M is the number of receivers, N the
+%                      number of sources and F the number of frequencies.
+% 
+% --------------------------------------------------
+% Notes
+% 
+% If the variable "sDir" provides the wavenumbers along the Cartesian axes,
+% it is the responsibility of the user to make sure that the condition
+% k^2 = kx^2 + ky^2 + kz^2 holds true.
+% 
+% --------------------------------------------------
+function waveField = planeWave(sDir, rPos, f, Q, c)
+    % ====================================================
+    % Check for number of arguments
+    % ====================================================
+    narginchk(3, 5);
+    nargoutchk(0, 1);
+
+    % ====================================================
+    % Validate input arguments
+    % ====================================================
+    % Validate mandatory arguments
+    validateattributes(sDir, "numeric", {'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)");
+    end
+
+    validateattributes(rPos, "numeric", {'2d', 'ncols', 3, 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Receiver positions", 2);
+    validateattributes(f, "numeric", {'vector', 'finite', 'nonnan', 'nonempty', 'real'}, mfilename, "Frequencies", 3);
+
+    % Validate optional arguments
+    if nargin > 3 && ~isempty(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
+            error("Source strength(s) must be either a scalar or its length must match the number of sources.");
+        end
+    else
+        Q = 1;
+    end
+
+    if nargin > 4 && ~isempty(c)
+        validateattributes(c, "numeric", {'scalar', 'finite', 'positive', 'nonnan', 'nonempty', 'real'}, mfilename, "Speed of propagation", 6);
+    else
+        c = 343;
+    end
+
+    % ====================================================
+    % Pre-process
+    % ====================================================
+    % Make sure frequencies is a column vector
+    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);
+    end
+
+    % Create a source strength vector (if needed)
+    if isscalar(Q)
+        Q = ones(size(k, 1), length(f)) * Q;
+    end
+
+    % =============================================
+    % Calculate wavefield
+    % ============================================
+    % Go through the frequencies
+    for fIdx = length(f):-1:1
+        waveField(:, :, fIdx) = Q(:, fIdx).' .* exp(-1j * rPos * k(:, :, fIdx));
+    end
+end
\ No newline at end of file
-- 
GitLab