Model for Simulating ECG and PPG Signals with Arrhythmia Episodes 1.3.1
(9,691 bytes)
function [rr, targets_SR_AF, targets_APB, pafBoundaries, pafEpisodeLength] = simPAF_gen_rr_intervals(rrLength, fibFreqz, APBrate, realRRon, stayInAF, AFburden)
%
% [] = simPAF_gen_rr_intervals() returns RR intervals during PAF.
% The dominant length of PAF episodes, and the percentage of the total time AF is
% present (AF burden) is determined by a first-order two-state Markov chain.
%
% An atrioventricular node model in which the ventricles are assumed to be
% activated by the arriving atrial impulses according to a Poisson process
% is used to generate realistic RR series during an episode of AF (Corino et al 2011)
%
% Ventricular rhythm during SR is simulated using RR interval generator
% proposed by McSharry et al(2003) in which both the impact of
% parasympathetic stimulation (respiratory sinus arrhythmia) and baroreflex
% regulation (Mayer waves) is modeled by a bimodal power spectrum
%
% In case of real data the entire MIT-BIH Normal Sinus Rhythm database,
% consisting of 18 long-term ECG recordings, was taken as a basis for the set
% of 18 RR interval series of sinus rhythm.
%
% The Long Term Atrial Fibrillation database was used to compose a set
% of AF rhythm. In total, 69 RR interval series were extracted out of
% 84 long-term ECG recordings. The remaining records were excluded since
% they did not meet the criteria of <5000 RR intervals of AF.
%
% Copyright (C) 2017 Andrius Petrenas
% Biomedical Engineering Institute, Kaunas University of Technology
%
% Available under the GNU General Public License version 3
% - please see the accompanying file named "LICENSE"
%
% Decide which rhythm type to generate
if AFburden == 0
rhythmType = 0; % SR
elseif AFburden == 1
rhythmType = 1; % AF
elseif AFburden > 0 && AFburden < 1
rhythmType = 2; % PAF
else
error('AF burden must be a value between 0 and 1')
end
switch rhythmType % 0 - sinus rhythm, 1 - AF, 2 - PAF
case 0 % The entire rhythm is SR
if realRRon == 1 % Use real RR series
rr = simPAF_construct_real_RR(0, rrLength);
else % Use simulated RR series
rr = simPAF_gen_SR_RR_intervals(rrLength);
rr = rr(1:rrLength);
end
targets_SR_AF = zeros(1,rrLength);
pafBoundaries(1,1) = 0;
pafBoundaries(1,2) = 0;
pafEpisodeLength = 0;
case 1 % The entire rhythm is AF
if realRRon == 1 % Use real RR series
rr = simPAF_construct_real_RR(1, rrLength);
else
rr = simPAF_gen_AF_RR_intervals(fibFreqz,rrLength);
rr = rr(1:rrLength);
end
targets_SR_AF = ones(1,rrLength);
pafBoundaries(1,1) = 1;
pafBoundaries(1,2) = rrLength;
pafEpisodeLength = rrLength;
case 2 % PAF
% Generate alternating SR and AF episodes
goToNS = 1-stayInAF;
goToAF = (goToNS*AFburden)/(1-AFburden);
stayInNS = 1 - goToAF;
trans = [stayInNS,1-stayInNS;1-stayInAF, stayInAF];
[~,targets_SR_AF] = hmmgenerate(rrLength,trans,[0; 1]);
% Find the number of AF episodes and the length of each episode
k = 1;
pafEpisodeLength = [];
for p = 1:length(targets_SR_AF)-1
if targets_SR_AF(p) == 2
if targets_SR_AF(p+1) == 2
k = k + 1;
if p == (length(targets_SR_AF)-1)
pafEpisodeLength = [pafEpisodeLength k];
end
else
pafEpisodeLength = [pafEpisodeLength k];
k = 1;
end
end
end
% Find boundaries of each PAF episode
if rrLength == length(find(targets_SR_AF == 1))% The entire signal is SR
pafBoundaries(1,1) = 0;
pafBoundaries(1,2) = 0;
elseif rrLength == length(find(targets_SR_AF == 2))% The entire signal is AF
pafBoundaries(1,1) = 1;
pafBoundaries(1,2) = rrLength;
elseif rrLength ~= length(find(targets_SR_AF == 2))% The signal with PAF
diffTar = diff(targets_SR_AF);
j = 1;
k = 1;
flag = 1;
for i = 1:length(diffTar)
if diffTar(i) == 1
pafBoundaries(j,1) = i + 1;
j = j + 1;
flag = 1;
end
if diffTar(i) == -1
pafBoundaries(k,2) = i;
k = k + 1;
flag = 2;
end
if i == length(diffTar)
if flag == 1
pafBoundaries(k,2) = i+1;
end
end
end
end
% Generate RR intervals during AF
rrLengthAF = length(find(targets_SR_AF == 2));
if realRRon == 1 % Use real RR series
rrAF = simPAF_construct_real_RR(1, rrLengthAF);
else
rrAF = simPAF_gen_AF_RR_intervals(fibFreqz,rrLengthAF);
rrAF = rrAF(1:rrLengthAF);
end
% Generate RR intervals during SR
rrLengthSR = length(find(targets_SR_AF == 1));
if realRRon == 1 % Use real RR series
rrSR = simPAF_construct_real_RR(0, rrLengthSR);
else % Use simulated RR series
rrSR = simPAF_gen_SR_RR_intervals(rrLengthSR);
rrSR = rrSR(1:rrLengthSR);
end
% Make sure that the average RR value during SR is larger than that in AF
if mean(rrSR) < mean(rrAF)
rrSR = rrSR + (mean(rrAF) - mean(rrSR));
end
% Construct PAF RR series
j = 1;
k = 1;
for i = 1:rrLength
if targets_SR_AF(i) == 1
rr(i) = rrSR(j);
j = j+1;
else
rr(i) = rrAF(k);
k = k+1;
end
end
targets_SR_AF = targets_SR_AF - 1;
end
%% Insert atrial premature beats
if APBrate > 0.5
error('APB rate must be less or equal to 0.5')
elseif APBrate > 0
targets_APB = zeros(1,rrLength);
transAPB = [1-APBrate,APBrate;0.99999,0.00001];
[~,statesAPB] = hmmgenerate(rrLength,transAPB, [0; 1]);
% Remove APBs during AF
statesAPB = statesAPB.*(abs(targets_SR_AF-1));
rrAPB=rr;
apbType = randi([0 3],1,1);
switch apbType
case 0 % APBs with sinus reset
for i=1:length(statesAPB)-1
if statesAPB (i) == 2
rrAPB(i)=0.8*rrAPB(i);
targets_APB(i) = 1;
end
end
case 1 % Interpolated PACs
for i=1:length(statesAPB)-1
if statesAPB (i) == 2
rrAPBtemp = rrAPB(i);
rrAPB(i)=0.6*rrAPB(i);
rrAPB(i+1)=rrAPBtemp - rrAPB(i);
targets_APB(i) = 1;
end
end
case 2 % PACs with delayed sinus reset
for i=1:length(statesAPB)-1
if statesAPB (i) == 2
rrAPBtemp = rrAPB(i);
rrAPB(i)= 0.8*rrAPB(i);
rrAPB(i+1)= 1.2*rrAPB(i+1);
targets_APB(i) = 1;
end
end
case 3 %PACs with full compensatory pause
for i=1:length(statesAPB)-1
if statesAPB (i) == 2
rrAPBtemp = rrAPB(i);
rrAPB(i)= 0.8*rrAPB(i);
rrAPB(i+1)= 2*rrAPBtemp-rrAPB(i);
targets_APB(i) = 1;
end
end
end
rr = rrAPB;
else
targets_APB = zeros(1,rrLength);
end
% %% Switching between AF an SR
% goToNS = 1-stayInAF;
% goToAF = (goToNS*AFburden)/(1-AFburden);
% stayInNS = 1 - goToAF;
%
% trans = [stayInNS,1-stayInNS;1-stayInAF, stayInAF];
% [~,states_SR_AF] = hmmgenerate(ecgLength,trans,[0; 1]);
%
% %% Calculate the number of AF episodes generate by Markov chain
% k = 1;
% episodeLength = [];
% for p = 1:length(states_SR_AF)-1
% if states_SR_AF(p) == 2
% if states_SR_AF(p+1) == 2
% k = k + 1;
% if p == (length(states_SR_AF)-1)
% episodeLength = [episodeLength k];
% end
% else
% episodeLength = [episodeLength k];
% k = 1;
% end
% end
% end
%
% %% Find boundaries of each PAF episode
% if ecgLength == length(find(states_SR_AF == 1))
% % The entire signal is SR
% beginOfAF(1,1) = 0;
% beginOfAF(1,2) = 0;
% elseif ecgLength == length(find(states_SR_AF == 2))
% % The entire signal is AF
% beginOfAF(1,1) = 1;
% beginOfAF(1,2) = ecgLength;
% elseif ecgLength ~= length(find(states_SR_AF == 2))
% diffTar = diff(states_SR_AF);
% j = 1;
% k = 1;
% flag = 1;
% for i = 1:length(diffTar)
% if diffTar(i) == 1
% beginOfAF(j,1) = i + 1;
% j = j + 1;
% flag = 1;
% end
% if diffTar(i) == -1
% beginOfAF(k,2) = i;
% k = k + 1;
% flag = 2;
% end
%
% if i == length(diffTar)
% if flag == 1
% beginOfAF(k,2) = i+1;
% end
% end
% end
% end
%
% %%
%
%
end