Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the upcrossing analysis functions in MATLAB #151

Merged
merged 21 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified examples/adcp_example.mlx
MShabara marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.
52 changes: 30 additions & 22 deletions examples/short_term_extremes_example.html
MShabara marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

Binary file modified examples/short_term_extremes_example.mlx
MShabara marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.
176 changes: 176 additions & 0 deletions mhkit/tests/upcrossing_Test.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
classdef upcrossing_Test < matlab.unittest.TestCase
properties
t
signal
zeroCrossApprox
end

methods (TestClassSetup)
% Shared setup for the entire test class
function setupTestClass(testCase)
% Define time vector
testCase.t = linspace(0, 4, 1000);

% Define signal
testCase.signal = testCase.exampleWaveform_(testCase.t);

% Approximate zero crossings
testCase.zeroCrossApprox = [0, 2.1, 3, 3.8];
end

end

methods (TestMethodSetup)
% Setup for each test
end

% methods (Test)
% % Test methods
%
% function unimplementedTest(testCase)
% testCase.verifyFail("Unimplemented test");
% end
% end
MShabara marked this conversation as resolved.
Show resolved Hide resolved

methods
function signal = exampleWaveform_(~, t)
% Generate a simple waveform form to analyse
% This has been created to perform
% a simple independent calcuation that
% the mhkit functions can be tested against.
A = [0.5, 0.6, 0.3];
T = [3, 2, 1];
w = 2 * pi ./ T;

signal = zeros(size(t));
for i = 1:length(A)
signal = signal + A(i) * sin(w(i) * t);
end
end

function [crests, troughs, heights, periods] = exampleAnalysis_(testCase, signal)
% NB: This only works due to the construction
% of our test signal. It is not suitable as
% a general approach.

% Gradient-based turning point analysis
grad = diff(signal);

% +1 to get the index at turning point
turningPoints = find(grad(1:end-1) .* grad(2:end) < 0) + 1;

crestInds = turningPoints(signal(turningPoints) > 0);
troughInds = turningPoints(signal(turningPoints) < 0);

crests = signal(crestInds);
troughs = signal(troughInds);
heights = crests - troughs;

% Numerical zero-crossing solution
zeroCross = zeros(size(testCase.zeroCrossApprox));
for i = 1:length(testCase.zeroCrossApprox)
zeroCross(i) = fzero(@(x) testCase.exampleWaveform_(x), ...
testCase.zeroCrossApprox(i));
end

periods = diff(zeroCross);
end
end

methods (Test)
%% Test functions without indices (inds)
function test_peaks(testCase)
[want, ~, ~, ~] = testCase.exampleAnalysis_(testCase.signal);
got = uc_peaks(testCase.t, testCase.signal);

testCase.verifyEqual(got, want, 'AbsTol', 1e-3);
end

function test_troughs(testCase)
[~, want, ~, ~] = testCase.exampleAnalysis_(testCase.signal);
got = uc_troughs(testCase.t, testCase.signal);

testCase.verifyEqual(got, want, 'AbsTol', 1e-3);
end

function test_heights(testCase)
[~, ~, want, ~] = testCase.exampleAnalysis_(testCase.signal);

got = uc_heights(testCase.t, testCase.signal);

testCase.verifyEqual(got, want, 'AbsTol', 1e-3);
end

function test_periods(testCase)
[~, ~, ~, want] = testCase.exampleAnalysis_(testCase.signal);

got = uc_periods(testCase.t, testCase.signal);

testCase.verifyEqual(got, want, 'AbsTol', 2e-3);
end

function test_custom(testCase)
[want, ~, ~, ~] = testCase.exampleAnalysis_(testCase.signal);

% create a similar function to finding the peaks
f = @(ind1, ind2) max(testCase.signal(ind1:ind2));

got = uc_custom(testCase.t, testCase.signal, f);

testCase.verifyEqual(got, want, 'AbsTol', 1e-3);
end

%% Test functions with indcies
function test_peaks_with_inds(testCase)
[want, ~, ~, ~] = testCase.exampleAnalysis_(testCase.signal);

inds = upcrossing(testCase.t, testCase.signal);

got = uc_peaks(testCase.t, testCase.signal, inds);

testCase.verifyEqual(got, want, 'AbsTol', 1e-3);
end

function test_trough_with_inds(testCase)
[~, want, ~, ~] = testCase.exampleAnalysis_(testCase.signal);

inds = upcrossing(testCase.t, testCase.signal);

got = uc_troughs(testCase.t, testCase.signal, inds);

testCase.verifyEqual(got, want, 'AbsTol', 1e-3);
end

function test_heights_with_inds(testCase)
[~, ~, want, ~] = testCase.exampleAnalysis_(testCase.signal);

inds = upcrossing(testCase.t, testCase.signal);

got = uc_heights(testCase.t, testCase.signal, inds);

testCase.verifyEqual(got, want, 'AbsTol', 1e-3);
end

function test_periods_with_inds(testCase)
[~, ~, ~, want] = testCase.exampleAnalysis_(testCase.signal);
inds = upcrossing(testCase.t, testCase.signal);

got = uc_periods(testCase.t, testCase.signal,inds);

testCase.verifyEqual(got, want, 'AbsTol', 2e-3);
end

function test_custom_with_inds(testCase)
[want, ~, ~, ~] = testCase.exampleAnalysis_(testCase.signal);
inds = upcrossing(testCase.t, testCase.signal);

% create a similar function to finding the peaks
f = @(ind1, ind2) max(testCase.signal(ind1:ind2));

got = uc_custom(testCase.t, testCase.signal, f, inds);

testCase.verifyEqual(got, want, 'AbsTol', 1e-3);
end

end
end
Loading
Loading