Respiratory and Pulse Oximetry Waveforms from Healthy Adults During Simulated Apnoea Events 1.0.0

File: <base>/Code/Plotting_Code.m (12,821 bytes)
%Plotting code for pulse data and inline sensor data
%Jordan Hil
%11/06/2024

clc
clear 
close all

%% Load Data ------------------------------------------------------------
subject_num = 1;                       % subject number 
trial_number = 4;                       % trial number (1-7 with inline sensor) 
Fs = 250;                               % sampling frequency for pulse oximter

%% Load pulse oximeter data
for j = subject_num 
    for i = trial_number          
        subject{j}.test{i}.data_path = sprintf("Neck_Pulse_Oximeter_Data_timefixed/Subject_%d/*.csv", subject_num);      %path for all data in subject folder
        subject{j}.test{i}.data_files = dir(subject{j}.test{i}.data_path);
        subject{j}.test{i}.num_files = length(subject{j}.test{i}.data_files);

        for filenum = 1:subject{j}.test{i}.num_files 
            subject{j}.test{i}.filenames{filenum} = subject{j}.test{i}.data_files(filenum).name;
        end
        subject{j}.test{i}.sorted_datanames = natsort(string(subject{j}.test{i}.filenames)); % Sort the files chronologically

        for filenum = 1:subject{j}.test{i}.num_files
            temp_data_path = sprintf("Neck_Pulse_Oximeter_Data_timefixed/Subject_%d/%s", subject_num, subject{j}.test{i}.sorted_datanames(trial_number)); % Get the data path 
            subject{j}.test{i}.loaded_data = readtable(temp_data_path, 'HeaderLines', 1); % load the data and skip first line
        end 
    end
end



%% Load Inline Sensor data
for j = subject_num                           
    for i = trial_number         
        subject{j}.test{i}.data_path_breath = sprintf("Inline_PQ_Data/Subject%d/*.csv", subject_num);      %path for all data in subject folder
        subject{j}.test{i}.data_files_breath = dir(subject{j}.test{i}.data_path_breath);
        subject{j}.test{i}.num_files_breath = length(subject{j}.test{i}.data_files_breath);

        for filenum = 1:subject{j}.test{i}.num_files_breath 
            subject{j}.test{i}.filenames_breath{filenum} = subject{j}.test{i}.data_files_breath(filenum).name;
        end
        subject{j}.test{i}.sorted_datanames_breath = natsort(string(subject{j}.test{i}.filenames_breath)); % Sort the files chronologically

        for filenum = 1:subject{j}.test{i}.num_files_breath
            temp_data_path = sprintf("Inline_PQ_Data/Subject%d/%s", subject_num, subject{j}.test{i}.sorted_datanames_breath(trial_number)); % Get the data path 
            subject{j}.test{i}.loaded_data_breath = readtable(temp_data_path, 'HeaderLines', 1); % load the data and skip first line
            % order - Time [s]	,Gauge Pressure [cmH2O],	Inspiratory differenrtial pressure [cmH2O],	Start Time
        end 
    end
end



%% Filters Pulse Data --------------------------------------------------------------
% All frequency values are in Hz

% Filter for pulse data
N  = 6;         % Order
Fc = 8;         % Cutoff Frequency
f_da  = fdesign.lowpass('N,F3dB', N, Fc, Fs);
Fda = design(f_da, 'butter');        

%Filter the data
for j = subject_num
    for i = trial_number
        for k = 2:9      %as k(1) is time 
            temp_data = -subject{j}.test{i}.loaded_data{:,k};       %negative due to photodiode 

            %filter the data with filtfilt for zerophase shift 
            subject{j}.test{i}.unfiltered{k-1} = temp_data;
            subject{j}.test{i}.filtered{k-1} = filtfilt(Fda.sosMatrix,Fda.ScaleValues,temp_data);
        end
    end
end

%% Remove first and last 2 seconds of filtered data -----
% this accounts for the filter rise and end period when data has not been
% filtered bang on

for j = subject_num
    for i = trial_number
        for k = 2:9
            % Define the number of samples to remove
            samples_to_remove = 500;
            
            % Process filtered signals
            fields = {'filtered'};
            for field = fields
                temp_data = subject{j}.test{i}.(field{1}){k-1};
                
                % Ensure the data length is sufficient
                if length(temp_data) > 2 * samples_to_remove
                    % Remove first and last 500 samples
                    processed_data = temp_data(samples_to_remove:end - samples_to_remove);
                    subject{j}.test{i}.(field{1}){k-1} = processed_data;
                   
                end
            end
        end
    end
end


%% Time --------------------------------------------------------------
time_test = cell(trial_number, 1);
time_unchanged_test = cell(trial_number, 1);

for i = trial_number          % Test
    %Adjust from date timing from data to seconds
    time_unchanged = subject{j}.test{i}.loaded_data{:,1};
    time = time_unchanged(1:length(time_unchanged)-999); %as remove first and last 500 samples above

    time_test{i} = time; 
    time_unchanged_test{i} = time_unchanged;
end



%% Start time for the breathing data

% Extract data using Var1–Var3
breath_data = subject{j}.test{i}.loaded_data_breath;
time_breath = breath_data.Var1(1:19999);
gauge_pressure = breath_data.Var2(1:19999);
diff_pressure = breath_data.Var3(1:19999);

% Extract start datenum from the first row of column 4
start_datenum = subject{j}.test{i}.loaded_data_breath{1,4};
% Convert datenum to datetime
start_datetime = datetime(start_datenum, 'ConvertFrom', 'datenum', 'Format', 'dd-MMM-yyyy HH:mm:ss.SSSSSS');



%% Plotting ------------------------------------------------------------
for j = subject_num
    for i = trial_number     

        % Unfiltered pulse data
        figure(10+i)
        sgtitle('Unfiltered Pulse Data')
        titles = {'PD1 660nm', 'PD2 660nm', 'PD1 940nm', 'PD2 940nm', 'PD3 660nm', 'PD4 660nm', 'PD3 940nm', 'PD4 940nm'};
        index_adjustments = [0, 0, 2, 2, -2, -2, 0, 0];
        
        for ii = 1:8
            subplot(4,2,ii)
            plot(time_unchanged_test{i}, subject{j}.test{i}.unfiltered{ii + index_adjustments(ii)}, LineWidth=1.5)
        
            if ~isempty(titles{ii})
                title(titles{ii})
            end
            grid on
            ylabel('Voltage (V)')
            xlabel('Time (s)')
        end


        %plot filtered Pulse Data
        figure(20+i)
        sgtitle('Filtered Pulse Data')
        titles = {'PD1 660nm', 'PD2 660nm', 'PD1 940nm', 'PD2 940nm', 'PD3 660nm', 'PD4 660nm', 'PD3 940nm', 'PD4 940nm'};
        index_adjustments = [0, 0, 2, 2, -2, -2, 0, 0];
        
        for ii = 1:8
            subplot(4,2,ii)
            plot(time_test{i}, subject{j}.test{i}.filtered{ii + index_adjustments(ii)}, LineWidth=1.5)
        
            if ~isempty(titles{ii})
                title(titles{ii})
            end
            grid on
            ylabel('Voltage (V)')
            xlabel('Time (s)')
        end

        % Gauge and differential pressure data
        figure(30+i)
        sgtitle('Inline Sensor Data')
        subplot(2,1,1)
        plot(time_breath, gauge_pressure, LineWidth=1.5)
        grid on
        ylabel('Gauge Pressure (cmH2O)')
        xlabel('Time (s)')

        subplot(2,1,2)
        plot(time_breath, diff_pressure, LineWidth=1.5)
        grid on
        ylabel('Differential Pressure (cmH2O)')
        xlabel('Time (s)')
        xlim([0, 50])
       
    end
end

%% plot pressure & pulse on same plot --------------------------------------------------

figure(50)
%sgtitle('Pulse and Pressure Data')

titles = {'PD1 660nm', 'PD2 660nm', 'PD1 940nm', 'PD2 940nm', ...
          'PD3 660nm', 'PD4 660nm', 'PD3 940nm', 'PD4 940nm', ...
          'Gauge Pressure', 'Differential Pressure'};

index_adjustments = [0, 0, 2, 2, -2, -2, 0, 0];

% Plot the 8 filtered signals
for ii = 1:8
    subplot(5, 2, ii)
    plot(time_test{i}, subject{j}.test{i}.filtered{ii + index_adjustments(ii)}, 'LineWidth', 1.5)
    title(titles{ii})
    grid on
    ylabel('Voltage (V)')
    xlabel('Time (s)')
end


% Plot gauge pressure
subplot(5, 2, 9)
plot(time_breath, gauge_pressure, 'b', 'LineWidth', 1.5)
title(titles{9})
grid on
ylabel('Pressure (cmH2O)')
xlabel('Time (s)')
xlim([0, 50])

% Plot differential pressure
subplot(5, 2, 10)
plot(time_breath, diff_pressure, 'r', 'LineWidth', 1.5)
title(titles{10})
grid on
ylabel('Pressure (cmH2O)')
xlabel('Time (s)')
xlim([0, 50])


% differential pressure and pules
% Plot both on the same graph with different axes using yyaxis
figure(60);
t = tiledlayout(4, 1); % Create a 4x1 tiled layout

titles = {'PD1', 'PD2', 'PD3', 'PD4'};


for ii = 1:4
    nexttile; % Move to the next subplot

    % Plot using yyaxis for dual y-axis
    yyaxis left;
    plot(time_test{i}, subject{j}.test{i}.filtered{ii}, 'Color', [0 0.4470 0.7410], 'LineWidth', 2); % Blue
    ax = gca;
    ax.YColor = [0 0.4470 0.7410]; % Match y-axis color with the line

    yyaxis right;
    plot(time_breath, diff_pressure, 'Color', [0.6350 0.0780 0.1840], 'LineWidth', 2); % Red for diff pressure
    ax.YColor = [0.6350 0.0780 0.1840];

    % Title and grid settings
    if ~isempty(titles{ii})
        title(titles{ii}, 'FontName', 'Times New Roman', 'FontSize', 16);
    end
    grid on;
    grid minor;

    % Set font properties
    ax.FontName = 'Times New Roman';
    ax.FontSize = 16;

end

% Shared x-axis label
xlabel(t, 'Time (s)', 'FontName', 'Times New Roman', 'FontSize', 20, 'FontWeight', 'bold');
title(t, 'Filtered Pulse Signals and Differential Pressure', ...
    'FontName', 'Times New Roman', 'FontSize', 22, 'FontWeight', 'bold');


% Gauge presure and Pulse
% Plot both on the same graph with different axes using yyaxis
figure(70);
t = tiledlayout(4, 1); % Create a 4x1 tiled layout

titles = {'PD1', 'PD2', 'PD3', 'PD4'};

for ii = 1:4
    nexttile; % Move to the next subplot

    % Plot using yyaxis for dual y-axis
    yyaxis left;
    plot(time_test{i}, subject{j}.test{i}.filtered{ii}, 'Color', [0 0.4470 0.7410], 'LineWidth', 2); % Blue
    ax = gca;
    ax.YColor = [0 0.4470 0.7410]; % Match y-axis color with the line

    yyaxis right;
    plot(time_breath, gauge_pressure, 'Color', [0.6350 0.0780 0.1840], 'LineWidth', 2); % Red for diff pressure
    ax.YColor = [0.6350 0.0780 0.1840];

    % Title and grid settings
    if ~isempty(titles{ii})
        title(titles{ii}, 'FontName', 'Times New Roman', 'FontSize', 16);
    end
    grid on;
    grid minor;

    % Set font properties
    ax.FontName = 'Times New Roman';
    ax.FontSize = 16;

    xlim([0, 50])
end

% Shared x-axis label
xlabel(t, 'Time (s)', 'FontName', 'Times New Roman', 'FontSize', 20, 'FontWeight', 'bold');
title(t, 'Filtered Pulse Signals and Gauge Pressure', ...
    'FontName', 'Times New Roman', 'FontSize', 22, 'FontWeight', 'bold');




%% new plot
figure(860 + i); clf
t = tiledlayout(6,1); % 6 rows, 1 column
t.TileSpacing = 'compact';
t.Padding = 'compact';

titles = {'PD1', 'PD2', 'PD3', 'PD4', 'Gauge Pressure', 'Differential Pressure'};

% --- First 4 tiles: PD pairs (660nm vs 940nm) ---
for ii = 1:4
    nexttile;
    
    yyaxis left;
    plot(time_test{i}, subject{j}.test{i}.filtered{ii}, ...
        'Color', [0.6350 0.0780 0.1840], 'LineWidth', 2); % Dark red for 660nm
    ax = gca;
    ax.YColor = [0.6350 0.0780 0.1840];
    if ii == 1
        ylabel('660nm (V)', 'FontName','Times New Roman', 'FontSize',14)
    else
        ylabel('')
    end

    yyaxis right;
    plot(time_test{i}, subject{j}.test{i}.filtered{ii+4}, ...
        'Color', [0 0.4470 0.7410], 'LineWidth', 2); % Blue for 940nm
    ax.YColor = [0 0.4470 0.7410];
    if ii == 1
        ylabel('940nm (V)', 'FontName','Times New Roman', 'FontSize',14)
    else
        ylabel('')
    end

    title(titles{ii}, 'FontName','Times New Roman', 'FontSize',16)
    grid on; grid minor;
    xlim([0 50])
    
    if ii < 7
        set(gca,'XTickLabel',[]) % hide x labels except bottom
    end
end

% --- 5th tile: Gauge pressure ---
nexttile
plot(time_breath, gauge_pressure, 'b', 'LineWidth', 2)
title(titles{5}, 'FontName','Times New Roman', 'FontSize',16)
ylabel('cmH_2O', 'FontName','Times New Roman', 'FontSize',14)
grid on; grid minor;
xlim([0 50])
set(gca,'XTickLabel',[])

% --- 6th tile: Differential pressure ---
nexttile
plot(time_breath, diff_pressure, 'r', 'LineWidth', 2)
title(titles{6}, 'FontName','Times New Roman', 'FontSize',16)
ylabel('cmH_2O', 'FontName','Times New Roman', 'FontSize',14)
grid on; grid minor;
xlim([0 50])

% Shared x-axis label at bottom
xlabel(t, 'Time (s)', 'FontName','Times New Roman', ...
    'FontSize',20, 'FontWeight','bold')