Skip to content
Snippets Groups Projects
Commit d313ed12 authored by leprob001's avatar leprob001
Browse files

Added plugin system to be able to write custom tracking methods.

parent 99cc23c0
No related branches found
No related tags found
No related merge requests found
175
\ No newline at end of file
176
\ No newline at end of file
......@@ -25,6 +25,8 @@ artoa.controller.track.trajectoryOutput.close();
%% Load mat file
load(fullfile(pathname, filename), '-mat');
%% Initialize plugins
artoa.plugins.initialize();
%% Initialize tracking parameter
artoa.controller.track.parameter.updateGui();
%% Initialize offsets
......
function [trackingMethods] = collectTrackingMethods()
%INITIALIZE Summary of this function goes here
% Detailed explanation goes here
global artoaWorkspace;
%% Get default tracking methods
trackingMethods = artoaWorkspace.defaults.trackingMethods;
%% Get plugin methods
if (artoa.data.hasMember(artoaWorkspace, {'plugins', 'tracking', 'functionHandles'}))
trackingMethods = [trackingMethods fieldnames(artoaWorkspace.plugins.tracking.functionHandles)];
end
end
......@@ -62,14 +62,12 @@ callbacks.buttonEmpiricalToNan = @artoa.controller.edit.offsets.buttonEmpiricalT
artoaWorkspace.defaults.mainCallbacks = callbacks;
%% Start artoa4
artoa.gui.main( ...
callbacks, ...
artoaWorkspace.defaults.trackingMethods, ...
artoa.controller.main.collectTrackingMethods(), ...
artoaWorkspace.defaults.interpolationMethods, ...
artoaWorkspace.defaults.soundspeedMethods ...
);
end
function [] = initializeArtoa4()
%INITIALIZEARTOA4 Initializes variables required for artoa4 and creates the main window if required.
%
%
global artoaDataInput artoaWorkspace artoaGui;
......@@ -36,6 +36,9 @@ artoaWorkspace.defaults.soundspeedMethods = { ...
};
artoaWorkspace.defaults.pickPointMarkerSize = 80;
artoaWorkspace.plugins = struct();
artoaWorkspace.plugins.tracking = struct();
artoaGui = struct();
artoaGui.figures = struct();
artoaGui.main = struct();
......@@ -58,6 +61,9 @@ catch ex
error([mfilename ': Reading soundsource file failed with the following message: ' ex.message]);
end
%% Initialize all plugins
artoa.plugins.initialize();
%% Open main gui
artoa.controller.main.open();
......@@ -70,4 +76,3 @@ if artoa.data.getMember(artoaDataInput.ini, {'view', 'hidedeleteddatapoints'}, f
end
end
function [positions, clockError] = callTrackingMethod(pTrackingMethod, pData, pSoundsourcePositions, pCombinationDetails)
%CALLTRACKINGMETHOD Summary of this function goes here
% Detailed explanation goes here
global artoaWorkspace;
positions = [];
clockError = [];
if ~artoa.data.hasMember(artoaWorkspace, {'plugins', 'tracking', 'functionHandles', pTrackingMethod})
return;
end
[positions, clockError] = artoaWorkspace.plugins.tracking.functionHandles.(pTrackingMethod)( ...
pData, ...
pSoundsourcePositions, ...
pCombinationDetails ...
);
end
function [trackingMethods] = findTrackingMethods()
%INITIALIZE Summary of this function goes here
% Detailed explanation goes here
%% Find all tracking methods
[filePath, ~, ~] = fileparts(mfilename('fullpath'));
filePath = fullfile(filePath, '..', '..', '..');
trackingMethods = dir(fullfile(filePath, 'plugins', 'tracking', '*.m'));
plugins = {};
for oTrackingMethods = 1:size(trackingMethods)
if (trackingMethods(oTrackingMethods).isdir)
continue;
end
plugins{end + 1} = fullfile( ...
trackingMethods(oTrackingMethods).folder, ...
trackingMethods(oTrackingMethods).name ...
);
end
trackingHandles = function_handle(plugins);
%% Convert them to struct
trackingMethods = struct();
if ~iscell(trackingHandles)
trackingMethods.(func2str(trackingHandles)) = trackingHandles;
return;
end
for i = 1:size(trackingHandles)
trackingMethods.(func2str(trackingHandles{i})) = trackingHandles{i};
end
end
function [] = initialize()
%INITIALIZE Summary of this function goes here
% Detailed explanation goes here
global artoaWorkspace;
%% Check if workspace supports plugins
if ~artoa.data.hasMember(artoaWorkspace, {'plugins', 'tracking'})
warning('Loaded artoaWorkspace does not support tracking plugins! Aborting ...');
return;
end
%% Get tracking methods
artoaWorkspace.plugins.tracking.functionHandles = ...
artoa.plugins.findTrackingMethods();
end
......@@ -233,11 +233,11 @@ for oCombination = 1:size(soundsourceCombinations, 1)
currentCombination.referencePosition{1} = num2str(trajectory(end, :));
end
[segmentPositions, segmentDates, segmentClockError, segmentResiduals] = artoa.trajectory.calculateCombinationSegment( ...
preparedData, ...
currentCombination, ...%trajectory(end, :), ...
floatReferenceTime, ...
pSoundVelocity(oCombination, :), ...
pTrackingParameter.trackingMethodString ...
preparedData, ...
currentCombination, ...%trajectory(end, :), ...
floatReferenceTime, ...
pSoundVelocity(oCombination, :), ...
pTrackingParameter.trackingMethodString ...
);
% remove NaNs from segment
......
......@@ -62,9 +62,12 @@ referencePosition = cellfun(@str2double, strsplit(pCombinationDetails.referenceP
%% Filter toa data
intersectedToaDates = pCorrectedData.(soundsourceNames{1}).toaDate;
soundsourcePositions = [pCorrectedData.(soundsourceNames{1}).position];
pluginSoundsourcePositions = struct();
pluginSoundsourcePositions.(soundsourceNames{1}) = pCorrectedData.(soundsourceNames{1}).position;
for i = 2:length(soundsourceNames)
[intersectedToaDates] = intersect(intersectedToaDates, pCorrectedData.(soundsourceNames{i}).toaDate);
soundsourcePositions = [soundsourcePositions; pCorrectedData.(soundsourceNames{i}).position];
pluginSoundsourcePositions.(soundsourceNames{i}) = pCorrectedData.(soundsourceNames{i}).position;
end
%% Remove all dates that are out of bounds
......@@ -73,12 +76,22 @@ intersectedToaDates = intersectedToaDates( ...
& intersectedToaDates <= segmentEnd ...
);
%% Create a table for every soundsource (required by tracking plugin system)
pluginTables = struct();
for i = 1:length(soundsourceNames)
pluginTables.(soundsourceNames{i}) = table();
end
%% Prepare data
distanceToSoundsources = {};
%segmentPositions = [pStartposition];
segmentPositions = referencePosition;
segmentToas = {};
warning('off'); % disable warnings because creating the tables are not consistent
for oDates = 1:length(intersectedToaDates)
currentDateValue = intersectedToaDates(oDates);
distanceToSoundsources{oDates} = [];
segmentToas{oDates} = [];
for i = 1:length(soundsourceNames)
currentName = soundsourceNames{i};
selection = pCorrectedData.(currentName).toaDate == currentDateValue;
......@@ -105,12 +118,18 @@ for oDates = 1:length(intersectedToaDates)
+ windowStartTime ...
+ leapseconds ...
+ floatWindowStart;
segmentToas{oDates} = [segmentToas{oDates}; relativeToa];
% calculate distance to the source
distanceToSoundsources{oDates} = [ ...
distanceToSoundsources{oDates}; ...
relativeToa * (pSoundVelocity(i)/1000)
];
% store in pluginTables
pluginTables.(currentName).date{oDates} = currentDateValue;
pluginTables.(currentName).toa{oDates} = relativeToa;
pluginTables.(currentName).distance{oDates} = distanceToSoundsources{oDates}(i);
% distanceToSoundsources{oDates} = [ ...
% distanceToSoundsources{oDates}; ...
% (...
......@@ -123,6 +142,7 @@ for oDates = 1:length(intersectedToaDates)
end
end
warning('on');
if strcmp(trackingMethod, 'hyperbolic') && (length(soundsourceNames) < 3)
trackingMethod = 'circular';
......@@ -171,6 +191,16 @@ switch trackingMethod
end
segmentClockError = [segmentClockError; currentClockError];
end
otherwise
% prepare soundsource infos
[segmentPositions, segmentClockError] = artoa.plugins.tracking.callTrackingMethod( ...
pTrackingMethod, ...
pluginTables, ...
pluginSoundsourcePositions, ...
pCombinationDetails ...
);
segmentPositions = [referencePosition; segmentPositions];
% if length(unique(sosonr)) < 3; % check if 3 sources have been specified in track panel
% fprintf(1,'Hyperbolic tracking not possible: only 2 sound sources specified, using circular tracking\r');
......
function h = function_handle(fcn)
% FUNCTION_HANDLE Construct function handle from string or
% cell array of strings
%
% FUNCTION_HANDLE constructs a <a href="matlab:help([matlabroot '/toolbox/matlab/datatypes/function_handle'])">function_handle</a> for built-in or
% user-defined functions.
%
% When constructing a <a href="matlab:help([matlabroot
% '/toolbox/matlab/datatypes/function_handle'])">function_handle</a> directly with <a href="matlab:help('@')">@</a> or <a href="matlab:help('str2func')">str2func</a>,
% it is only possible to succesfully evaluate the resulting handle when
% the function the handle refers to was on the MATLAB search at the time
% the handle was created. To create valid handles to arbitrarty
% functions possibly not on the search path, use the FUNCTION_HANDLE
% constructor.
%
% F = FUNCTION_HANDLE(fcn) for string or cell string [fcn] creates a
% function_handle or cell array of function handles for each of the
% handles or strings in [fcn]. Any string in [fcn] may include the
% path to the function of interest. An error is issued in case the
% path information is incorrect, of the specified file is not an
% executable MATLAB function.
%
% EXAMPLES:
%
% >> F = function_handle('./path/to/function/myFcn.m')
% F =
% @myFcn
%
% >> A = function_handle(...
% {@cos, '../dir/not/on/MATLAB/path/myFunction.m'})
% A =
% @cos @myFunction
%
% >> A{1}(pi)
% ans =
% -1
%
% >> functions(A{1})
% ans =
% function: 'min'
% type: 'simple'
% file: ''
%
% >> functions(A{2})
% ans =
% function: 'myFunction'
% type: 'simple'
% file: '/fullpath/dir/not/on/MATLAB/path/myFunction.m'
%
% See also <a href="matlab:help([matlabroot '/toolbox/matlab/datatypes/function_handle'])">function_handle (built-in)</a>, str2func, functions.
% Please report bugs and inquiries to:
%
% Name : Rody P.S. Oldenhuis
% E-mail : oldenhuis@gmail.com
% Licence : 2-clause BSD (See License.txt)
% Changelog
%{
2014/March/19 (Rody Oldenhuis)
- NEW: first version
%}
% If you find this work useful, please consider a donation:
% https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6G3S5UYM7HJ3N
%% Initialize
% Quick exit
if isa(fcn, 'function_handle')
h = fcn; return; end
if ~iscell(fcn)
fcn = {fcn}; end
% Extract everything that already is a function handle
h = cell(size(fcn));
F = cellfun('isclass', fcn, 'function_handle');
h(F) = fcn(F);
if all(F)
return; end
% Continue with the ones specified as strings
fcn = fcn(~F);
if ~iscellstr(fcn)
throwAsCaller(MException(...
'function_handle:invalid_objects',...
'Invalid types detected in input. Expected types are ''function_handle'' or ''char''.'));
end
hF = cell(size(fcn));
%% Get to work
% Make sure we always end up where we started
prevDir = pwd;
clean__ = onCleanup(@(~)cd(prevDir));
for ii = 1:numel(fcn)
% Valid inputs
if any(exist(fcn{ii})==[2 3 5 6 8]) %#ok<EXIST>
[pth,name] = fileparts(fcn{ii});
% non-builtin
if ~isempty(pth)
if exist(pth,'dir')==7
cd(pth);
hF{ii} = str2func(['@' name]);
cd(prevDir);
else
throwAsCaller(MException(...
'function_handle:dir_not_found',...
'Directory ''%s'' not found.', pth));
end
% builtin
elseif exist(fcn{ii},'builtin')==5
hF{ii} = str2func(fcn{ii});
% unrecognized
else
throwAsCaller(MException(...
'function_handle:fcn_not_found',...
['Function ''%s'' is not on the MATLAB search path, and does not seem ',...
'to be a builtin.'], name));
end
% Invalid input
else
throwAsCaller(MException(...
'function_handle:fcn_invalid',...
'Function or file ''%s'' not found.', fcn{ii}));
end
end
% Final assignment
h(~F) = hF;
% Make output more intuitive
if numel(h)==1
h = h{1}; end
end
function [positions, clockError] = sampleTrackingMethod(pData, pSoundsourcePositions, pCombinationDetails)
%SAMPLETRACKINGMETHOD A sample function to write your own tracking algorithms
%
% Parameters:
% pData (struct):
% Data structured by soundsource. The toa is already corrected
% (e.g. doppler correction etc.)
% pSoundsourcePositions (struct):
% The soundsource positions [deg].
% pCombinationDetails (table):
% Contains all information of the current soundsource
% combination.
%
%
% Returns:
% positions (double):
% The calculated list of segment positions as a X x 2 column vector.
% clockError (double):
% The estimated clock error.
%% Get reference position and all soundsource names that are involved
referencePosition = cellfun(@str2double, strsplit(pCombinationDetails.referencePosition{1}));
soundsourceNames = strsplit(pCombinationDetails.soundsources{1});
%% Show all data that has been passed to the plugin method
pData
pCombinationDetails
pSoundsourcePositions
%% Return NaN because this is a sample
positions = NaN(size(pData.(soundsourceNames{1}).date, 1), 2);
clockError = [];
end
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment