PhysioNet Cardiovascular Signal Toolbox 1.0.0

File: <base>/Tools/HRV_Metrics_Tools/EvalFrequencyDomainHRVstats.m (9,648 bytes)
function out = EvalFrequencyDomainHRVstats(NN, tNN, sqi, HRVparams, windows_all)
%
% out = EvalFrequencyDomainHRVstats (NN, tNN, , sqi, settings)
%   
%   OVERVIEW:   This function returns frequency domain HRV metrics 
%               calculated on input NN intervals.
%
%   INPUT:      MANDATORY:
%               NN          : a single row of NN (normal normal) interval
%                             data in seconds
%               tNN         : a single row of time indices of the rr interval 
%                             data (seconds)
%               sqi         : (Optional) Signal Quality Index; Requires a 
%                             matrix with at least two columns. Column 1 
%                             should be timestamps of each sqi measure, and 
%                             Column 2 should be SQI on a scale from 0 to 1.
%                             Additional columns can be included with
%                             additional sqi at the same timestamps
%               HRVparams   : struct of settings for hrv_toolbox analysis
%               windows_all : vector containing the starting time of each
%                             windows (in seconds) 
%
%   OUTPUT:     out.ulf        : (ms^2) Power in the ultra low frequency range (default < 0.003 Hz)
%               out.vlf        : (ms^2) Power in very low frequency range (default 0.003 <= vlf < 0.04 Hz)
%               out.lf         : (ms^2) Power in low frequency range (default 0.04Hz  <= lf < 0.15 Hz)
%               out.hf         : (ms^2) Power in high frequency range (default 0.15 <= hf < 0.4 Hz)
%               out.lfhf       : Ratio LF [ms^2]/HF [ms^2]
%               out.ttlpwr     : (ms^2) Total spectral power (approximately <0.4 Hz)
%               out.fdflag     : 1 - Lomb Periodogram or other method failed
%                                2 - Not enough high SQI data
%                                3 - Not enough data in the window to analyze
%                                4 - Window is missing too much data
%                                5 - Success
%
%	REPO:       
%       https://github.com/cliffordlab/PhysioNet-Cardiovascular-Signal-Toolbox
%   ORIGINAL SOURCE AND AUTHORS:     
%       Gari Clifford HRV Tools
%                   G. Clifford 2001 gari@mit.edu, calc_lfhf.m 
%                   http://www.robots.ox.ac.uk/~gari/CODE/HRV/
%       ChengYu Lui
%       Adriana Vest
%       Dependent scripts written by various authors 
%       (see functions for details)       
%	COPYRIGHT (C) 2016 
%   LICENSE:    
%       This software is offered freely and without warranty under 
%       the GNU (v3 or later) public license. See license file for
%       more information  
%
%   10-28-2017 Modified by Giulia Da Poian: 
%   Initlialiezed output as NaN
%   Removed fft, Welch and Burg method without resampling. 
%   Added function for resampling when required by the method 
%   Acceppting only one method at time to make the function output more
%   clear
%   Removed option to write results on file directly from this function
%   Converted output to ms^2



% Verify input arguments

if nargin < 5
    error('Eval_FrequencydomainHRVstats: wrong number of input arguments!')
end

if isempty(sqi) 
     sqi(:,1) = tNN;
     sqi(:,2) = ones(length(tNN),1);
end

% Set Defaults
windowlength = HRVparams.windowlength;
fd_threshold1 = HRVparams.sqi.LowQualityThreshold;
fd_threshold2 = HRVparams.RejectionThreshold;
limits = HRVparams.freq.limits;
method = HRVparams.freq.method;
plot_on = HRVparams.freq.plot_on;
debug_sine = HRVparams.freq.debug_sine;   % if use the sine wave to debug
f_sine = HRVparams.freq.debug_freq;       % the frequency of the added sine wave
weight = HRVparams.freq.debug_weight;
sf = HRVparams.freq.resampling_freq;


% Preallocate arrays before entering the loop

out.ulf = nan(1,length(windows_all));
out.vlf = nan(1,length(windows_all));
out.lf = nan(1,length(windows_all));
out.hf = nan(1,length(windows_all));
out.lfhf = nan(1,length(windows_all));
out.ttlpwr = nan(1,length(windows_all));
out.fdflag = nan(1,length(windows_all));
% Window by Window Analysis

% Loop through each window of RR data
for iWin = 1:length(windows_all)
    % Check window for sufficient data
    if ~isnan(windows_all(iWin))    
        % Isolate data in this window
        idx_NN_in_win = find(tNN >= windows_all(iWin) & tNN < windows_all(iWin) + windowlength);
        idx_sqi_win = find(sqi(:,1) >= windows_all(iWin) & sqi(:,1) < windows_all(iWin) + windowlength);

        sqi_win = sqi(idx_sqi_win,:);
        t_win = tNN(idx_NN_in_win);
        nn_win = NN(idx_NN_in_win);

        % Analysis of SQI for the window
        lowqual_idx = find(sqi_win(:,2) < fd_threshold1);

        % If enough data has an adequate SQI, perform the calculations
        if numel(lowqual_idx)/length(sqi_win(:,2)) < fd_threshold2

            % Initialize variables
            % maxF=fs/2; % This calculation works for regularly sampled data
            N     =  length(nn_win);       % RR interval series length
            % m_fs  = 1/mean(nn_win);     % mean frequency of heart rate, i.e., the mean sample rate (fs) of RR sereis  
            % max_f = .5/(min(nn_win));   % max frequency of RR interval series
            % nfft  = 2^nextpow2(N);      % Next power of 2 from N
            nfft = 1024;
            %F = [1/nfft:1/nfft:m_fs];  % setting up frequency vector
            F = [1/nfft:1/nfft:.5];  % setting up frequency vector

            % add sine wave to RR signal
            if debug_sine
                s_sin  = weight*sin(2*pi*f_sine*t);
                nn_win = nn_win + s_sin;
            end

            % subtract mean of segment
            if HRVparams.freq.zero_mean
                rr_0 = nn_win - mean(nn_win); %rudimentary detrending
            else
                rr_0 = nn_win;
            end
            
            switch method
                % 1. Lomb-Scargle Periodogram (no resampling)
                case 'lomb'
                    try
                        [PSDlomb,Flomb] = CalcLomb(t_win,rr_0,F,nfft,HRVparams.freq.normalize_lomb);
                        % plomb equivalent to CalcLomb when normalized 
                        % lomb-scargle periodogram
                        %[PSDmatlablomb,fplombout] = plomb(vv,tt,F,'normalized');
                        [out.ulf(iWin), out.vlf(iWin), out.lf(iWin), out.hf(iWin), out.lfhf(iWin),...
                            out.ttlpwr(iWin)] = CalcLfHfParams(PSDlomb, Flomb, limits, plot_on);
                        
                        out.fdflag(iWin) = 5; %'sucess';

                    catch
                        out.fdflag(iWin) = 1; %'lomb_failed'
                    end

                % 2. Pwelch (after resampling)
                case 'welch'                    
                    % Resampling
                    rr_int = ApplyResamplingToRR(t_win,rr_0,HRVparams);
                    try
                        [PSDwelch,Fwelch] = pwelch(rr_int,[],[],2^nextpow2(length(rr_int)),sf);
                        [out.ulf(iWin), out.vlf(iWin), out.lf(iWin), out.hf(iWin), out.lfhf(iWin),...
                                out.ttlpwr(iWin)] = CalcLfHfParams(PSDwelch, Fwelch, limits, plot_on) ;  
                        out.fdflag(iWin) = 5; %'sucess';
                    catch
                        out.fdflag(iWin) = 1; %'Pwelch faild';
                    end
                    
                % 3. FFT (after resampling)
                case 'fft'
                    % Resampling
                    rr_int = ApplyResamplingToRR(t_win,rr_0,HRVparams);
                    try
                        Y_FFT2 = fft(rr_int);  % fft using the original sample length
                        PSDfft = Y_FFT2.*conj(Y_FFT2)/length(rr_int);
                        Ffft = sf*(0:floor(length(rr_int)/2)+1)/length(rr_int);
                        [out.ulf(iWin), out.vlf(iWin), out.lf(iWin), out.hf(iWin), out.lfhf(iWin),...
                                out.ttlpwr(iWin)] = CalcLfHfParams(PSDfft(1:length(Ffft)), Ffft, limits, plot_on);
                        out.fdflag(iWin) = 5; %'sucess';
                    catch
                        out.fdflag(iWin) = 1; %'fft faild';
                    end

                % 4. Burg (after resampling)
                case 'burg'
                    % Resampling
                    rr_int = ApplyResamplingToRR(t_win,rr_0,HRVparams);
                    try
                    % ChengYu's Burg Method with resampled data
                    pb = HRVparams.freq.resampled_burg_poles; % pole setting
                    [PSD_Burg1,f_Burg1] = pburg(rr_int,pb,2^nextpow2(length(rr_int)),sf);
                    [out.ulf(iWin), out.vlf(iWin), out.lf(iWin), out.hf(iWin), out.lfhf(iWin),...
                            out.ttlpwr(iWin)] = CalcLfHfParams(PSD_Burg1,f_Burg1, limits, plot_on);
                        out.fdflag(iWin) = 5; %'sucess';
                    catch
                        out.fdflag(iWin) = 1; %'Burg faild';
                    end
            end
        else
            out.fdflag(iWin) = 2; %'nt_enuf_hi_sqi_data'
        end % end conditional statements that run only if SQI is adequate

    else
        out.fdflag(iWin) = 3; %'nt_enuf_data_in_win';   
    end % end check for sufficient data
end % end of loop through windows


%   References:
%               5 minutes is the shortest period that HRV spectral metrics
%               should be calculated according to 184(Clifford Thesis). With a 5 min
%               window, the lowest frequency that can be theoretically resolved is
%               1/300 (.003 Hz).