Model for Simulating ECG and PPG Signals with Arrhythmia Episodes 1.3.0

File: <base>/ECG_PPG_model/simPAF_gen_rr_intervals.m (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