Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/pspm_cfg/pspm_cfg_selector_channel_action.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
channel_action.values = {'add', 'replace'};
channel_action.labels = {'Add', 'Replace'};
channel_action.val = {'add'};
channel_action.help = {'Choose whether to add a new channel, or to replace the last existing channel of the same type and with the same units (if any).'};
channel_action.help = {'Choose whether to add a new channel, or to replace the processed channel.'};
23 changes: 15 additions & 8 deletions src/pspm_convert_au2unit.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
% input data if the recording method is area. This is performed to always
% return linear units.
% Using the given variables, the following calculations are performed:
% 0. Take square root of data if recording is 'area'.
% 0. Take 2*sqrt(data/pi) of data if recording is 'area'.
% 1. Let from unit to reference_unit converted recording distance be Dconv.
% 2. x ← A*(Dconv/Dref)*x
% 3. Convert x from ref_unit to unit.
Expand Down Expand Up @@ -163,25 +163,32 @@
[sts, channeldata, infos, pos_of_channel(i)] = pspm_load_channel(alldata, channel{i}, 'pupil');
if sts < 1, return; end
% recursive call to avoid the formula being stated twice in the same function
[sts, convert_data.data{i}] = pspm_convert_au2unit(channeldata.data, unit, distance, record_method, ...
[sts, convert_data{i}.data] = pspm_convert_au2unit(channeldata.data, unit, distance, record_method, ...
multiplicator, reference_distance, reference_unit, options);
if sts < 1, return; end
convert_data{i}.header = channeldata.header;
convert_data{i}.header.units = unit;
end
[f_sts, f_info] = pspm_write_channel(fn, convert_data, options.channel_action, struct('channel', pos_of_channel));
if f_sts < 1, return; end
[sts, f_info] = pspm_write_channel(fn, convert_data, options.channel_action, struct('channel', pos_of_channel));
if sts < 1, return; end
outchannel = f_info.channel;
% convert data
case 'data'
convert_data = data;
if strcmpi(record_method, 'area')
convert_data = sqrt(convert_data);
[f_sts, convert_data] = pspm_convert_area2diameter(convert_data);
if f_sts < 1, return; end
end
[~, distance] = pspm_convert_unit(distance, unit, reference_unit);

[f_sts, distance] = pspm_convert_unit(distance, unit, reference_unit);
if f_sts < 1, return; end
convert_data = multiplicator * (distance / reference_distance) * convert_data;

%% convert data from reference_unit to unit
[~, convert_data] = pspm_convert_unit(convert_data, reference_unit, unit);
[f_sts, convert_data] = pspm_convert_unit(convert_data, reference_unit, unit);
if f_sts < 1, return; end
outchannel = convert_data;
sts = 1;
end
sts = 1;

end
5 changes: 2 additions & 3 deletions src/pspm_write_channel.m
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
for iChannel = 1:numel(newdata)
warning off
[sts, ~, pos_of_channels] = pspm_select_channels(data, ...
newdata{iChannel}.header.chantype, newdata{iChannel}.header.units);
newdata{iChannel}.header.chantype);
warning on
if sts < 1
channeli(iChannel) = 0;
Expand All @@ -132,8 +132,7 @@
warning off
sts = pspm_select_channels( ...
data(options.channel(iChannel)), ...
newdata{iChannel}.header.chantype, ...
newdata{iChannel}.header.units);
newdata{iChannel}.header.chantype);
warning on
if sts < 1
channeli(iChannel) = 0;
Expand Down
232 changes: 228 additions & 4 deletions test/pspm_convert_au2unit_test.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,230 @@
classdef pspm_convert_au2unit_test < pspm_testcase
% ● Description
% unittest class for the pspm_convert_au2unit function
% ● Authorship
% (C) 2019 Eshref Yozdemir (University of Zurich)
% ● Description
% unittest class for the pspm_convert_au2unit function
% ● Authorship
% (C) 2019 Eshref Yozdemir (University of Zurich)
% Updated in 2026 by Bernhard von Raußendorf

methods (Test)

%% data mode
% [sts, converted_data] = pspm_convert_au2unit(data, unit, distance, record_method,
% multiplicator, reference_distance, reference_unit, options)
function testDiameterSameUnits(testCase)
[sts, out] = pspm_convert_au2unit(20, 'mm', 60, 'diameter', 0.1, 50, 'mm');
testCase.verifyEqual(sts, 1);
testCase.verifyEqual(out, 2.4);
end
function testDiameterUnitConversion(testCase)
[sts, out] = pspm_convert_au2unit(30, 'cm', 6, 'diameter', 0.2, 50, 'mm');
testCase.verifyEqual(sts, 1);
testCase.verifyEqual(out, 0.72);
end
function testAreaSameUnits(testCase)
data = 100;
[sts, out] = pspm_convert_au2unit(data, 'mm', 60, 'area', 0.1, 50, 'mm');
testCase.verifyEqual(sts, 1);

% expected with formula diameter = 2.*sqrt(area./pi);
expected_from_formula = 0.1 * (60 / 50) * 2*sqrt(data./pi);
testCase.verifyEqual(out, expected_from_formula);

% expected with [sts, diameter] = pspm_convert_area2diameter(area)
[sts, diam] = pspm_convert_area2diameter(data);
expected_from_pspm = 0.1 * (60 / 50) * diam;
testCase.verifyEqual(out, expected_from_pspm);

end
function testAreaUnitConversion(testCase)
data = 400;
[sts, out] = pspm_convert_au2unit(data, 'mm', 100, 'area', 0.1, 100, 'm'); % reference_unit in m
testCase.verifyEqual(sts, 1);


% expected with formula diameter = 2.*sqrt(area./pi);
% ref. units in m: Dconv 100mm -> 0.1 m and A(Dconv/Dref) m -> mm (*1000)
expected_from_formula = 0.1 * (0.1 / 100) * 2 * sqrt(data ./ pi) * 1000;
testCase.verifyEqual(out, expected_from_formula);

% expected with [sts, diameter] = pspm_convert_area2diameter(area)
[sts, diam] = pspm_convert_area2diameter(data);
expected_from_pspm = 0.1 * (0.1 / 100) * diam * 1000;
testCase.verifyEqual(out, expected_from_pspm);



end
function testVectorAreaSameUnits(testCase)
data = [25 36 49]
[sts, out] = pspm_convert_au2unit(data, 'mm', 80, 'area', 0.05, 40, 'mm');
testCase.verifyEqual(sts, 1);


% expected with formula diameter = 2.*sqrt(area./pi);
expected_from_formula = 0.05 * (80 / 40) * 2 * sqrt(data ./ pi) ;
testCase.verifyEqual(out, expected_from_formula);

% expected with [sts, diameter] = pspm_convert_area2diameter(area)
[sts, diam] = pspm_convert_area2diameter(data);
expected_from_pspm = 0.05 * (80 / 40) * diam ;
testCase.verifyEqual(out, expected_from_pspm);

end
function testVectorAreaUnitConversion(testCase)
data = [100 400 900];
[sts,out]=pspm_convert_au2unit(data,'mm',100,'area',0.1,50,'m');
testCase.verifyEqual(sts,1);

% expected with formula diameter = 2.*sqrt(area./pi);
expected_from_formula = 0.1 * (0.1 / 50) * 2 * sqrt(data ./ pi) * 1000 ;
testCase.verifyEqual(out, expected_from_formula);

% expected with [sts, diameter] = pspm_convert_area2diameter(area)
[sts, diam] = pspm_convert_area2diameter(data);
expected_from_pspm = 0.1 * (0.1 / 50) * diam * 1000;
testCase.verifyEqual(out, expected_from_pspm);
end
function testVectorAreaUnitConversionTEST(testCase)
data = [100 400 900];
[sts,out]=pspm_convert_au2unit(data,'mm',100,'area',0.1,50,'mm');
testCase.verifyEqual(sts,1);
%
% % expected with formula diameter = 2.*sqrt(area./pi);
% expected_from_formula = 0.1 * (0.1 / 50) * 2 * sqrt(data ./ pi) * 1000 ;
% testCase.verifyEqual(out, expected_from_formula);
%
% % expected with [sts, diameter] = pspm_convert_area2diameter(area)
% [sts, diam] = pspm_convert_area2diameter(data);
% expected_from_pspm = 0.1 * (0.1 / 50) * diam * 1000;
% testCase.verifyEqual(out, expected_from_pspm);

data2 = unit2au(out,'mm',100,'area',0.1,50,'mm');
testCase.verifyEqual(data , data2, 'AbsTol', 1e-12);



end

%% Error handeling
function testErrorHandling(testCase)
% invalid inputs
[sts,out] = pspm_convert_au2unit();
testCase.verifyEqual(sts,-1);
testCase.verifyEmpty(out);

[sts,out] = pspm_convert_au2unit(20);
testCase.verifyEqual(sts,-1);
testCase.verifyEmpty(out);

[sts,out] = pspm_convert_au2unit(20,'mm');
testCase.verifyEqual(sts,-1);
testCase.verifyEmpty(out);

[sts,out] = pspm_convert_au2unit(20,'mm',60);
testCase.verifyEqual(sts,-1);
testCase.verifyEmpty(out);
% invalid unit inputs
[sts,out] = pspm_convert_au2unit(20,'mm',60,'wrong',0.1,50,'mm');
testCase.verifyEqual(sts,-1);
testCase.verifyEmpty(out);

[sts,out] = pspm_convert_au2unit(20,'mm','far','diameter',0.1,50,'mm');
testCase.verifyEqual(sts,-1);
testCase.verifyEmpty(out);

[sts,out] = pspm_convert_au2unit(20,'mm',60,'diameter','bad',50,'mm');
testCase.verifyEqual(sts,-1);
testCase.verifyEmpty(out);

[sts,out] = pspm_convert_au2unit(20,'mm',60,'diameter',0.1,'bad','mm');
testCase.verifyEqual(sts,-1);
testCase.verifyEmpty(out);
end

%% Pspm files
function testFileRoundTripConvertAu2Unit(testCase)
fn = '/home/bernd/git/PsPM/ImportTestData/eyelink/pspm_u_sc4b31.mat';
fn_roundtrip = '/home/bernd/git/PsPM/ImportTestData/eyelink/pspm_u_sc4b31_au.mat';

% copy file
S = load(fn);
save(fn_roundtrip, '-struct', 'S');

unit = 'mm';
distance = 600;
record_method = 'diameter';
multiplicator = 0.04;
reference_distance = 500;
reference_unit = 'mm';

options = struct();
options.channel = 'pupil';
options.channel_action = 'replace';

%% 1) load original file
[sts, infos, data] = pspm_load_data(fn);
testCase.verifyEqual(sts, 1);

%% 2) load pupil channel
[sts, original_channel, ~, pos] = pspm_load_channel(fn, options.channel, 'pupil');
testCase.verifyEqual(sts, 1);

%% 3) change units to au
au_data = unit2au(original_channel.data, unit, distance, record_method, ...
multiplicator, reference_distance, reference_unit);

newdata = original_channel;
newdata.data = au_data;
newdata.header.units = 'au';

[sts, info_write] = pspm_write_channel(fn_roundtrip, newdata, 'replace', struct('channel', pos));
testCase.verifyEqual(sts, 1);

%% 4) convert back to units
[sts, outchannel] = pspm_convert_au2unit( ...
fn_roundtrip, unit, distance, record_method, ...
multiplicator, reference_distance, reference_unit, ...
struct('channel', pos, 'channel_action', 'replace'));
testCase.verifyEqual(sts, 1);

%% 5) load converted channel
[sts, reconverted_channel] = pspm_load_channel(fn_roundtrip, outchannel, 'pupil');
testCase.verifyEqual(sts, 1);

%% 8) Test
testCase.verifyEqual(reconverted_channel.data, original_channel.data, 'AbsTol', 1e-12);
testCase.verifyEqual(reconverted_channel.header.units, unit);

%% cleanup
if exist(fn_roundtrip, 'file')
delete(fn_roundtrip);
end
end

end
end

function data = unit2au(outchannel, unit, distance, record_method, ...
multiplicator, reference_distance, reference_unit)

% [~, outchannel_ref] = pspm_convert_unit(outchannel, unit, reference_unit);
% [~, distance_conv] = pspm_convert_unit(distance, unit, reference_unit);

switch lower(record_method)
case 'diameter'
data = outchannel ./ ...
(multiplicator * (distance / reference_distance ));
case 'area'

data = (outchannel ./ ...
(multiplicator * ( distance/ reference_distance)));
data = ((data./2).^2 ).*pi;

otherwise
error('Invalid record_method');
end
end




2 changes: 1 addition & 1 deletion test/pspm_convert_gaze_test.m
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ function conversion(this, target, from, channel_action)
this.verifyTrue(~isempty(out_channel));
if strcmpi(target, 'sps')
extra = 1;
elseif strcmpi(channel_action, 'add') || ~strcmpi(target, from)
elseif strcmpi(channel_action, 'add') %|| ~strcmpi(target, from)
extra = 2;
else
extra = 0;
Expand Down
8 changes: 4 additions & 4 deletions test/pspm_write_channel_test.m
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,11 @@ function test_replace_units(this)
gen_data.data{1}.header.units = 'degree';
[~, ~] = this.verifyWarningFree(@() pspm_write_channel(this.testdatafile, gen_data.data{1}, 'replace'));
[~, post_unit_change.infos, post_unit_change.data] = pspm_load_data(this.testdatafile);
% should be one more channel as degrees did not exist
this.verifyEqual(length(post_unit_change.data), length(new.data) + 1);
% should be the same nr. of channels
this.verifyEqual(length(post_unit_change.data), length(new.data));
% assert one mm gaze channel and one degree gaze channel
this.verifyEqual(length(find(cellfun(@(c) strcmp(c.header.units, 'mm') && ...
strcmp(c.header.chantype, 'gaze_x_l'), post_unit_change.data))), 1);
% this.verifyEqual(length(find(cellfun(@(c) strcmp(c.header.units, 'mm') && ...
% strcmp(c.header.chantype, 'gaze_x_l'), post_unit_change.data))), 1);
this.verifyEqual(length(find(cellfun(@(c) strcmp(c.header.units, 'degree') && ...
strcmp(c.header.chantype, 'gaze_x_l'), post_unit_change.data))), 1);
end
Expand Down
Loading