Something went wrong on our end
Select Git revision
fpga_pinmap.xdc
firstOrderDma.m 6.69 KiB
%% Calculate 1st order Directional Microphone Array output
% --------------------------------------------------
% Author: Achilles Kappis
% e-mail: axilleaz@protonmail.com
%
% Date: 05/11/2024 (DD/MM/YYYY)
%
% Copyright: MIT
% --------------------------------------------------
% Functionality: Calculate the output of a first order Differential
% Microphone Array.
% --------------------------------------------------
% Input
%
% input [numeric]: The input to the array. 3D array/matrix with dimensions
% MxIxF, where M represents the number of microphones and
% must be even, I is the number of measurments (or number
% of sources), and F is the number of frequencies of
% interest. Each microphone pair is treated as a first
% order DMA.
%
% freq [numeric]: The frequencies of interest. A vector with number of
% elements matching the third dimension of the "input"
% parameter.
%
% d [numeric]: The inter-element distance of the microphone pairs. This
% must be a real scalar.
%
% 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]
%
% 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
%
% h [numeric]: This 2xF filter is the filter that results in the
% beam-pattern of interest and F is the number of frequencies.
%
% output [numeric]: The output of the array. It has the same dimensions as
% the "input" parameter except for the second dimension
% which is equal to M/2.
%
% --------------------------------------------------
% Notes
%
% --------------------------------------------------
function [h, output] = firstOrderDma(input, freq, d, pPattern, beta)
% ====================================================
% Check for number of arguments
% ====================================================
narginchk(3, 5);
nargoutchk(0, 2);
% ====================================================
% Validate input arguments
% ====================================================
validateattributes(input, {'numeric'}, {'3d', 'nonnan', 'finite', 'nonempty'}, mfilename, 'Input', 1);
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);
validateattributes(d, {'numeric'}, {'scalar', 'real', 'nonnan', 'finite', 'nonempty'}, mfilename, 'Inter-element distance', 3);
% Check beta
if nargin > 4
validateattributes(beta, {'numeric'}, {'scalar', 'real', 'finite', 'nonempty', 'nonnan', '<=', 1, '>=', 0}, mfilename, "Beta", 5)
else
beta = 0;
end
% Check alpha (angle of null)
if nargin > 3
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
pPattern = "dipole";
end
% ====================================================
% "Condition" arguments
% ====================================================
% Make sure frequencies is a row vector
if ~isrow(freq)
freq = freq.';
end
% ====================================================
% Calculate parameters
% ====================================================
% Calculate the correct "null" angles based on given polar pattern
if ~isnumeric(pPattern)
switch convertStringsToChars(lower(pPattern))
case {'omni', 'omnidirectional', 'monopole'}
pPattern = pi;
beta = 1;
case {'dipole', 'figure-of-eight'}
pPattern = pi/2;
beta = 0;
case 'cardioid'
pPattern = pi;
beta = 0;
case 'hypercardioid'
pPattern = (2 * pi/3);
beta = 0;
case 'supercardioid'
pPattern = (3 * pi/4);
beta = 0;
otherwise
error("Unsupported polar pattern");
end
else
pPattern = deg2rad(pPattern);
end
% Calculate filter(s)
for freqIdx = length(freq):-1:1
arrManLocMtx = [arrManLoc(0, d, freq(freqIdx), 343), arrManLoc(pPattern, d, freq(freqIdx), 343)];
h(:, freqIdx) = (arrManLocMtx')\[1; beta];
end
% ====================================================
% Calculate array output
% ====================================================
if nargout > 1
% Go through the frequencies
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);
end
end
end
end
%% Utility functions
% Calculate the array manifold
function am = arrManLoc(phi, d, freq, c)
k = -2j * pi * freq * cos(phi)/c;
am = [exp(k * (-d/2)); exp(k * (d/2))];
end