Skip to content

Commit a06bf86

Browse files
committed
Fix ecg editor and batch config issues; update pspm_convert_hb2hp and add tests
1 parent c3cb9d8 commit a06bf86

File tree

7 files changed

+112
-28
lines changed

7 files changed

+112
-28
lines changed

doc/Tests_Current_Status.docx

-960 Bytes
Binary file not shown.

src/pspm_cfg/pspm_cfg_convert_cardiac.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,15 @@
144144
limit_upper.strtype = 'r';
145145
limit_upper.num = [1 1];
146146
limit_upper.val = {2};
147-
limit_upper.help = pspm_cfg_help_format('pspm_convert_hb2hp', 'options.upper');
147+
limit_upper.help = pspm_cfg_help_format('pspm_convert_hb2hp', 'options.limit_upper');
148148

149149
limit_lower = cfg_entry;
150150
limit_lower.name = 'Lower limit';
151151
limit_lower.tag = 'lower';
152152
limit_lower.strtype = 'r';
153153
limit_lower.num = [1 1];
154154
limit_lower.val = {.2};
155-
limit_lower.help = pspm_cfg_help_format('pspm_convert_hb2hp', 'options.lower');
155+
limit_lower.help = pspm_cfg_help_format('pspm_convert_hb2hp', 'options.limit_lower');
156156

157157
limit = cfg_branch;
158158
limit.name = 'Limit';

src/pspm_cfg/pspm_cfg_ecg_editor.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
%% Limits
4949
lim = cfg_branch;
5050
lim.name = 'Limit';
51-
lim.tag = 'limit';
51+
lim.tag = 'limits';
5252
lim.val = {ulim, llim};
5353
lim.help = pspm_cfg_help_format('pspm_ecg_editor', 'options.limits');
5454

@@ -58,7 +58,7 @@
5858
factor.tag = 'factor';
5959
factor.strtype = 'r';
6060
factor.num = [1 1];
61-
factor.val = {2};
61+
factor.val = {1};
6262
factor.help = pspm_cfg_help_format('pspm_ecg_editor', 'options.factor');
6363

6464
%% Faulty detection

src/pspm_cfg/pspm_cfg_run_convert_cardiac.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
sr = job.pp_type{i}.hb2hp.sr;
3535
options = struct();
3636
options.channel = chan;
37-
options.limit = job.pp_type{i}.hb2hp.limit;
37+
options.limit_lower = job.pp_type{i}.hb2hp.limit.lower; % same convention as in pspm_options
38+
options.limit_upper = job.pp_type{i}.hb2hp.limit.upper;
3839
options = pspm_update_struct(options, job, 'channel_action');
3940
[sts, outchannel] = pspm_convert_hb2hp(fn, sr, options);
4041
case 'ecg2hp'
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
function pspm_cfg_run_ecg_editor(job)
22
% Updated on 18-12-2023 by Teddy
3+
% Updated on 12-04-2026 by Bernhard von Raußendorf
4+
35
options = struct();
46
fn = job.datafile{1};
57
ecg_chan = pspm_cfg_selector_channel('run', job.ecg_chan);
68
options.channel = pspm_cfg_selector_channel('run', job.hb_chan);
79
if isfield(job.artefact_epochs, 'artefact_file')
810
options.missing = job.artefact_epochs.artefact_file{1};
911
else
10-
options.missing = 0;
12+
options.missing = [];
1113
end
12-
options = pspm_update_struct(options, job.faulty_settings, {'factor',...
13-
'limit.upper',...
14-
'limit.lower'});
14+
15+
% options = pspm_update_struct(options, job.faulty_settings, {'factor',...
16+
% 'limit.upper',...
17+
% 'limit.lower'});
18+
19+
options = pspm_update_struct(options, job.faulty_settings, {'factor'});
20+
options.limits = pspm_update_struct(options, job.faulty_settings.limits, {'upper','lower'});
21+
1522
pspm_ecg_editor(fn, ecg_chan, options);

src/pspm_convert_hb2hp.m

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@
2828
% │ Specifies upper and lower limit for heart
2929
% │ periods. If the limit is exceeded, the values will
3030
% │ be ignored/removed and interpolated.
31-
% ├─────────.upper: [numeric]
31+
% ├───.limit_upper: [numeric]
3232
% │ Specifies the upper limit of the
3333
% │ heart periods in seconds. Default is 2.
34-
% └─────────.lower: [numeric]
34+
% └───.limit_lower: [numeric]
3535
% Specifies the lower limit of the
3636
% heart periods in seconds. Default is 0.2.
3737
% ● Outputs
@@ -70,14 +70,14 @@
7070
% get data
7171
% -------------------------------------------------------------------------
7272
[nsts, data, dinfos] = pspm_load_channel(fn, options.channel, 'hb');
73-
if nsts == -1, return; end
73+
if nsts < 1, return; end
7474

7575

7676
% interpolate
7777
% -------------------------------------------------------------------------
7878
hb = data.data;
7979
ibi = diff(hb);
80-
idx = find(ibi > options.limit.lower & ibi < options.limit.upper);
80+
idx = find(ibi > options.limit_lower & ibi < options.limit_upper);
8181
hp = 1000 * ibi; % in ms
8282
newt = (1/sr):(1/sr):dinfos.duration;
8383
try
@@ -103,7 +103,7 @@
103103
o.msg.prefix = 'Heart beat converted to heart period and';
104104
try
105105
[nsts,winfos] = pspm_write_channel(fn, newdata, options.channel_action, o);
106-
if nsts == -1, return;
106+
if nsts < 1, return;
107107
end
108108
catch
109109
warning('ID:invalid_input', 'call of pspm_write_channel failed');

test/pspm_convert_hb2hp_test.m

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,33 @@
44
% * History
55
% Written in 2019 by Ivan Rojkov (University of Zurich)
66
% Updated in 2024 by Teddy
7+
% Updated in 2026 by Bernhard von Raußendorf
8+
9+
properties (Constant)
10+
input_filename = fullfile(fileparts(mfilename('fullpath')), 'ImportTestData', 'ecg2hb', 'test_ecg_outlier_data_short_hb.mat');
11+
backup_filename = fullfile(fileparts(mfilename('fullpath')),'ImportTestData', 'ecg2hb', 'test_backup.mat');
12+
end
13+
14+
methods (TestClassSetup)
15+
function backup(this)
16+
this.assertTrue(isfile(this.input_filename), ...
17+
sprintf('Input file not found: %s', this.input_filename));
18+
19+
[sts, msg] = copyfile(this.input_filename, this.backup_filename);
20+
this.assertTrue(sts, msg);
21+
end
22+
end
23+
24+
methods (TestMethodSetup)
25+
function reset_input_file(this)
26+
[sts, msg] = copyfile(this.backup_filename, this.input_filename);
27+
this.assertTrue(sts, msg);
28+
end
29+
end
30+
31+
% Tests
732
methods (Test)
833
function invalid_input(this)
9-
files = {...
10-
fullfile('ImportTestData', 'ecg2hb', 'test_ecg_outlier_data_short.mat'),...
11-
fullfile('ImportTestData', 'ecg2hb', 'test_hb2hp_data1.mat'),...
12-
fullfile('ImportTestData', 'ecg2hb', 'test_hb2hp_data2.mat')...
13-
};
1434
% Verify no input
1535
this.verifyWarning(@() pspm_convert_hb2hp(), 'ID:invalid_input');
1636
% Verify not a string filename
@@ -21,18 +41,74 @@ function invalid_input(this)
2141
this.verifyWarning(@() pspm_convert_hb2hp('abc','abc'), 'ID:invalid_input');
2242
% Verify not a numeric channel
2343
this.verifyWarning(@() pspm_convert_hb2hp('abc',2,'abc'), 'ID:invalid_input');
24-
% Verify that call of pspm_load_data fails
25-
this.verifyWarning(@() pspm_convert_hb2hp(files{1},100), 'ID:nonexistent_file');
26-
% Verify that interpolation does not have enough points
27-
% this.verifyWarning(@() pspm_convert_hb2hp(files{2}, 100), 'ID:too_strict_limits');
28-
% Verify that call of pspm_write_channel fails
29-
options.channel_action = 'abc';
30-
this.verifyWarning(@() pspm_convert_hb2hp(files{3},100,[],options),'ID:invalid_input');
31-
%options.channel_action = 'add';
32-
%this.verifyWarningFree(@()pspm_convert_hb2hp(files{1},100,[],options));
44+
45+
%
46+
% % Verify that call of pspm_load_data fails
47+
% this.verifyWarning(@() pspm_convert_hb2hp(files{1},100), 'ID:nonexistent_file');
48+
% % Verify that interpolation does not have enough points
49+
% % this.verifyWarning(@() pspm_convert_hb2hp(files{2}, 100), 'ID:too_strict_limits');
50+
% % Verify that call of pspm_write_channel fails
51+
% options.channel_action = 'abc';
52+
% this.verifyWarning(@() pspm_convert_hb2hp(files{3},100,[],options),'ID:invalid_input');
53+
% %options.channel_action = 'add';
54+
% %this.verifyWarningFree(@()pspm_convert_hb2hp(files{1},100,[],options));
55+
56+
end
57+
58+
function basic_conversion(this)
59+
sr = 1000;
60+
61+
% this.verifyWarningFree(@() pspm_convert_hb2hp(this.input_filename, sr));
62+
63+
[sts, outchannel] = pspm_convert_hb2hp(this.input_filename, sr);
64+
65+
this.verifyEqual(sts, 1);
66+
this.verifyTrue(isnumeric(outchannel));
67+
this.verifyGreaterThan(outchannel, 0);
68+
end
69+
70+
function too_strict_limits(this)
71+
% fn = fullfile('ImportTestData', 'ecg2hb', 'test_hb2hp_data2.mat');
72+
sr = 100;
73+
options = struct();
74+
options = struct('limit_lower', 10, 'limit_upper', 11);
75+
76+
this.verifyWarning(@() pspm_convert_hb2hp(this.input_filename, sr, options), 'ID:too_strict_limits');
77+
78+
[sts, outchannel] = pspm_convert_hb2hp(this.input_filename, sr, options);
79+
this.verifyEqual(sts, 1);
80+
this.verifyTrue(isnumeric(outchannel));
81+
this.verifyGreaterThan(outchannel, 0);
82+
end
83+
84+
function add_channel_action(this)
85+
fn = fullfile('ImportTestData', 'ecg2hb', 'test_ecg_outlier_data_short_hb.mat');
86+
sr = 100;
87+
options = struct();
88+
options.channel_action = 'add';
89+
90+
this.verifyWarningFree(@() pspm_convert_hb2hp(fn, sr, options));
91+
92+
[sts, outchannel] = pspm_convert_hb2hp(fn, sr, options);
93+
94+
this.verifyEqual(sts, 1);
95+
this.verifyTrue(isnumeric(outchannel));
96+
this.verifyGreaterThan(outchannel, 0);
97+
end
98+
99+
100+
33101

34102
end
35103

104+
methods (TestClassTeardown)
105+
function restore(this)
106+
if isfile(this.backup_filename)
107+
[sts, msg] = copyfile(this.backup_filename, this.input_filename);
108+
this.assertTrue(sts, msg);
109+
delete(this.backup_filename);
110+
end
111+
end
36112
end
37113

38114
end

0 commit comments

Comments
 (0)