diff --git a/CHANGELOG.md b/CHANGELOG.md index f7048c826c6c77ba92d36a0355a42bdc852197c3..0264ef19fbd76a14a8368002c5b8b3fb3f7dc41e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### v0.2.6 ### +**Signal Processing - Generic**\ +\+ Add function to perform fractional octave smoothing of spectra. + + ### v0.2.5 ### **Utilities - Geometries**\ \* Combine translation offsets to a single vector input argument for the `rcvGeo()` MATLAB function.\ diff --git a/README.md b/README.md index f50c20acb4b575f586b0a7d0524126538d508d20..5d2e43b76482f316511ce76ba89ae210f9851d60 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.5**. +The project uses [semantic versioning](https://semver.org/) and the current version is **v0.2.6**. #### **Important** diff --git a/Signal Processing/Generic/MATLAB/Functions/fracOctSmooth.m b/Signal Processing/Generic/MATLAB/Functions/fracOctSmooth.m new file mode 100644 index 0000000000000000000000000000000000000000..a8de63daf9612f9dfc0070621616a4077da14386 --- /dev/null +++ b/Signal Processing/Generic/MATLAB/Functions/fracOctSmooth.m @@ -0,0 +1,120 @@ +%% Fractional octave band smoothing of spectrum +% -------------------------------------------------- +% Author: Achilles Kappis +% e-mail: axilleaz@protonmail.com +% +% Date: 20/09/2024 (DD/MM/YYYY) +% +% Copyright: MIT +% -------------------------------------------------- +% Functionality: Perform fractional octave smoothing of spectrum. +% -------------------------------------------------- +% Input +% +% spec [numeric]: The half spectrum. This can be either a vector or a +% matrix where each column is considered to be a spectrum +% to be smoothed. The data can be either complex or real. +% +% fVec [numeric]: This is a real vector holding the frequencies +% corresponding to the samples of "spec". +% +% fracOct [numeric] (Optional): This is the fraction of an octave +% corresponding to the smoothing parameter. +% [Default: 1]. +% +% edgeCase [string] (Optional): What to do when the smoothing window +% extends outside the given spectrum. The +% options are "Zero-Pad", where the spectrum +% is extended with zeros and they are used +% for the smoothing and "Clamp" where the +% right most window edge of the window is +% constrained to be at the upper edge of the +% spectrum. In the first case the smoothed +% spectrum will exhibit a downward slope at +% the highest edge while with the "Clamp" +% options the smoothing is inconsistent since +% the scale of the window is getting shorter +% at the highest edge of the spectrum. The +% options are not case-sensitive. +% [Default: "Zero-Pad"]. +% +% -------------------------------------------------- +% Output +% +% - smoothSpec [numeric]: The smoothed spectrum. +% +% -------------------------------------------------- +% Notes +% +% - For a very large "fracOct" value, at low frequencies, a value of less +% than 1 can result for the width of the smoothing window. In this case +% the sample is not averaged/smoothed and is returned as is. This may +% result in incorrect smoothing at low frequencies for small fractions of +% an octave. +% +% -------------------------------------------------- +function smoothSpec = fracOctSmooth(spec, fVec, fracOct, edgeCase) + % ==================================================== + % Check for number of arguments + % ==================================================== + narginchk(2, 4); + nargoutchk(0, 1); + + + % ==================================================== + % Validate input arguments + % ==================================================== + % Validate mandatory arguments + validateattributes(spec, "numeric", {'2d', 'nonnan', 'nonempty', 'finite'}, mfilename, "The spectrum to smooth", 1); + + if isvector(spec) + validateattributes(fVec, "numeric", {'vector', 'nonempty', 'nonnan', 'nonnegative', 'finite', 'real', 'increasing', 'numel', length(spec)}, mfilename, "The frequencies corresponding to the samples of the spectrum", 2); + spec = spec(:); + else + validateattributes(fVec, "numeric", {'vector', 'nonempty', 'nonnan', 'nonnegative', 'finite', 'real', 'increasing', 'numel', size(spec, 1)}, mfilename, "The frequencies corresponding to the samples of the spectrum", 2); + end + + % Validate optional arguments + if nargin > 2 && ~isempty(fracOct) + validateattributes(fracOct, "numeric", {'scalar', 'nonempty', 'nonnan', 'real', 'positive', 'finite', 'integer'}, mfilename, "Octave band fraction", 3); + else + fracOct = 1; + end + + if nargin > 3 && ~isempty(edgeCase) + validateattributes(edgeCase, {'string', 'char'}, {'scalartext', 'nonempty'}, mfilename, "Behaviour at the (high) edge of the spectrum", 4); + validatestring(edgeCase, ["Zero-Pad", "Clamp"], mfilename, "Behaviour at the (high) edge of the spectrum", 4); + else + edgeCase = "Zero-Pad"; + end + + + % ==================================================== + % Calculate parameters/variables + % ==================================================== + idx = fVec(:) .* 2.^(1./(2 * [-fracOct, fracOct])); % Edge frequencies for the window of each sample/bin + idx = round(idx/fVec(2)) + 1; % Convert to bin indices and round + + + % ==================================================== + % Pre-process data + % ==================================================== + % Zero-pad the upper end of the spectrum + if strcmpi(edgeCase, "Zero-Pad") + spec = cat(1, spec, zeros(max(idx, [], "all") - size(spec, 1), size(spec, 2))); + else + idx(idx > length(fVec)) = length(fVec); + end + + + % ==================================================== + % Smooth the spectrum + % ==================================================== + % Go through the frequencies + for fIdx = length(idx):-1:2 + smoothSpec(fIdx, :) = mean(spec(idx(fIdx, 1):idx(fIdx, 2), :), 1); + end + + % Add the DC component + smoothSpec(1, :) = spec(1, :); +end \ No newline at end of file diff --git a/Signal Processing/README.md b/Signal Processing/README.md index 92d50ca0d3d36b74007f6a71e1946f5fc64562d2..450a10bb067ba8f8c73c09d3a123250e1a81ddc9 100644 --- a/Signal Processing/README.md +++ b/Signal Processing/README.md @@ -11,5 +11,7 @@ The current structure of the section is summarised below. - Generic - Windowed Sinc Fractional Delay FIR filter + - Octave band frequencies calculation + - Fractional octave band smoothing of spectra - Array Processing - - First order Differential Microphone Array \ No newline at end of file + - First order Differential Microphone Array