├── requirements.txt ├── source ├── beat_detection_algorithms │ └── ecg_beat_detection │ │ ├── temp.hea │ │ ├── temp.dat │ │ ├── temp.qrs │ │ ├── rdeco │ │ ├── env_secant.m │ │ └── calc_pos_opt.m │ │ └── run_qrsdet_by_seg.m ├── low_pass_differentiator_64Hz.mat ├── dev │ ├── msptdpc5_beat_detector.m │ ├── msptdpc9_beat_detector.m │ ├── msptdpc10_beat_detector.m │ ├── msptdpc11_beat_detector.m │ ├── msptdpc14_beat_detector.m │ ├── msptdpc3_beat_detector.m │ ├── msptdpc1_beat_detector.m │ ├── msptdpc2_beat_detector.m │ ├── msptdpc15_beat_detector.m │ ├── msptdpc16_beat_detector.m │ ├── msptdpc6_beat_detector.m │ ├── msptdpc7_beat_detector.m │ ├── msptdpc8_beat_detector.m │ ├── msptdpc4_beat_detector.m │ ├── msptdpc12_beat_detector.m │ └── msptdpc13_beat_detector.m ├── tidy_beats.m ├── pulse_onsets_from_peaks.m ├── pulse_peaks_from_onsets.m ├── ampd_beat_detector.m ├── coppg_beat_detector.m ├── figures │ ├── create_ppg_beat_detection_fig.m │ └── make_plot_of_ppg_beat_detection_challenges_msptdfastv2.m ├── wepd_beat_detector.m ├── preprocess_ppg_signal.m └── pda_beat_detector.m ├── docs ├── assets │ └── images │ │ ├── ppg_and_beats.png │ │ ├── ecg_1_min_beat_detection.png │ │ ├── ppg_1_min_beat_detection.png │ │ ├── ppg_1_min_beat_detection_af.png │ │ ├── ecg_1_min_beat_detection_noisy.png │ │ ├── ppg_1_min_beat_detection_noisy.png │ │ ├── ppg_1_min_beat_detection_normal.png │ │ └── ppg_1_min_beat_detection_neonatal.png ├── toolbox │ ├── contributing.md │ ├── acknowledgments.md │ ├── performance_assessment.md │ ├── ecg_beat_detectors.md │ └── getting_started.md ├── tutorials │ ├── time_alignment.md │ ├── summary.md │ ├── creating_docs.md │ ├── ecg_beat_detection.md │ ├── performance_assessment.md │ ├── ppg_beat_detection.md │ └── designing_beat_detector.md ├── functions │ ├── pwd_beat_detector.md │ ├── tidy_beats.md │ ├── pulse_onsets_from_peaks.md │ ├── pulse_peaks_from_onsets.md │ ├── qppgfast_beat_detector.md │ ├── detect_ppg_beats.md │ ├── detect_ppg_beats_old.md │ ├── atmax_beat_detector.md │ ├── atmin_beat_detector.md │ ├── collate_mimic_perform_af_dataset.md │ ├── ppgpulses_beat_detector.md │ ├── swt_beat_detector.md │ ├── assess_multiple_datasets.md │ ├── collate_mimic_perform_ethnicity_dataset.md │ ├── export_sample_mimic_perform_data.md │ ├── spar_beat_detector.md │ ├── collate_mimic_perform_train_test_datasets.md │ ├── ims_beat_detector.md │ ├── abd_beat_detector.md │ ├── qppg_beat_detector.md │ ├── wepd_beat_detector.md │ ├── msptdfastv1_beat_detector.md │ ├── msptdfastv2_beat_detector.md │ ├── msptd_beat_detector.md │ ├── ampd_beat_detector.md │ ├── coppg_beat_detector.md │ ├── mmpdv2_beat_detector.md │ ├── preprocess_ppg_signal.md │ ├── erma_beat_detector.md │ ├── wfd_beat_detector.md │ ├── pda_beat_detector.md │ ├── align_ppg_ecg_beats.md │ ├── mimic_perform_dataset_converter.md │ ├── heartpy_beat_detector.md │ ├── assess_beat_detectors.md │ └── detect_ecg_beats.md ├── datasets │ ├── bidmc.md │ ├── capnobase.md │ ├── summary.md │ ├── wesad.md │ ├── mimic_perform_ethnicity.md │ ├── ppg_dalia.md │ ├── mimic_perform_af.md │ ├── mimic_perform_testing.md │ └── mimic_perform_training.md └── index.md ├── readthedocs.yaml ├── .gitignore ├── .all-contributorsrc ├── CODE_OF_CONDUCT.md ├── README.md ├── mkdocs.yml ├── CONTRIBUTING.md └── LICENSE.md /requirements.txt: -------------------------------------------------------------------------------- 1 | jinja2==3.0.3 2 | Markdown==3.3.7 -------------------------------------------------------------------------------- /source/beat_detection_algorithms/ecg_beat_detection/temp.hea: -------------------------------------------------------------------------------- 1 | temp 1 125 75001 2 | temp.dat 16 200 16 0 -60 6268 0 col 1 3 | -------------------------------------------------------------------------------- /docs/assets/images/ppg_and_beats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/docs/assets/images/ppg_and_beats.png -------------------------------------------------------------------------------- /source/low_pass_differentiator_64Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/source/low_pass_differentiator_64Hz.mat -------------------------------------------------------------------------------- /docs/assets/images/ecg_1_min_beat_detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/docs/assets/images/ecg_1_min_beat_detection.png -------------------------------------------------------------------------------- /docs/assets/images/ppg_1_min_beat_detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/docs/assets/images/ppg_1_min_beat_detection.png -------------------------------------------------------------------------------- /docs/assets/images/ppg_1_min_beat_detection_af.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/docs/assets/images/ppg_1_min_beat_detection_af.png -------------------------------------------------------------------------------- /docs/assets/images/ecg_1_min_beat_detection_noisy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/docs/assets/images/ecg_1_min_beat_detection_noisy.png -------------------------------------------------------------------------------- /docs/assets/images/ppg_1_min_beat_detection_noisy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/docs/assets/images/ppg_1_min_beat_detection_noisy.png -------------------------------------------------------------------------------- /docs/assets/images/ppg_1_min_beat_detection_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/docs/assets/images/ppg_1_min_beat_detection_normal.png -------------------------------------------------------------------------------- /readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.11" 7 | 8 | mkdocs: 9 | configuration: mkdocs.yml 10 | -------------------------------------------------------------------------------- /docs/assets/images/ppg_1_min_beat_detection_neonatal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/docs/assets/images/ppg_1_min_beat_detection_neonatal.png -------------------------------------------------------------------------------- /source/beat_detection_algorithms/ecg_beat_detection/temp.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/source/beat_detection_algorithms/ecg_beat_detection/temp.dat -------------------------------------------------------------------------------- /source/beat_detection_algorithms/ecg_beat_detection/temp.qrs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhcharlton/ppg-beats/HEAD/source/beat_detection_algorithms/ecg_beat_detection/temp.qrs -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | .DS_Store 3 | Docs/.DS_Store 4 | /source/low_pass_differentiator_*Hz.mat 5 | /source/archive/*.* 6 | /source/confidential/*.* 7 | /source/confidential_dev/*.* 8 | *.asv -------------------------------------------------------------------------------- /source/dev/msptdpc5_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc5_beat_detector(sig,fs) 2 | 3 | % version: default 4 | 5 | options = struct; 6 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 7 | 8 | end 9 | -------------------------------------------------------------------------------- /source/dev/msptdpc9_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc9_beat_detector(sig,fs) 2 | 3 | % version: win len of 4 secs 4 | 5 | options.win_len = 4; 6 | 7 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 8 | 9 | end 10 | -------------------------------------------------------------------------------- /source/dev/msptdpc10_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc10_beat_detector(sig,fs) 2 | 3 | % version: win len of 6 secs 4 | 5 | options.win_len = 6; 6 | 7 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 8 | 9 | end 10 | -------------------------------------------------------------------------------- /source/dev/msptdpc11_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc11_beat_detector(sig,fs) 2 | 3 | % version: win len of 10 secs 4 | 5 | options.win_len = 10; 6 | 7 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 8 | 9 | end 10 | -------------------------------------------------------------------------------- /source/dev/msptdpc14_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc14_beat_detector(sig,fs) 2 | 3 | % version: win len of 12 secs 4 | 5 | options.win_len = 12; 6 | 7 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 8 | 9 | end 10 | -------------------------------------------------------------------------------- /source/dev/msptdpc3_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc3_beat_detector(sig,fs) 2 | 3 | % version: vectorisation 4 | 5 | options.lms_calc_method = 2; 6 | 7 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 8 | 9 | end 10 | -------------------------------------------------------------------------------- /source/dev/msptdpc1_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc1_beat_detector(sig,fs) 2 | 3 | % version: pks only 4 | 5 | options.find_pks = 1; 6 | options.find_trs = 0; 7 | 8 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 9 | 10 | end 11 | -------------------------------------------------------------------------------- /source/dev/msptdpc2_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc2_beat_detector(sig,fs) 2 | 3 | % version: trs only 4 | 5 | options.find_pks = 0; 6 | options.find_trs = 1; 7 | 8 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 9 | 10 | end 11 | -------------------------------------------------------------------------------- /docs/toolbox/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | All are welcome to contribute. Please do so via the [GitHub repository](https://github.com/peterhcharlton/ppg-beats), either by [creating an issue](https://github.com/peterhcharlton/ppg-beats/issues) or submitting a pull request. -------------------------------------------------------------------------------- /source/dev/msptdpc15_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc15_beat_detector(sig,fs) 2 | 3 | % version: ds to 30 hz 4 | 5 | options.do_ds = true; 6 | options.ds_freq = 40; 7 | 8 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 9 | 10 | end 11 | -------------------------------------------------------------------------------- /source/dev/msptdpc16_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc16_beat_detector(sig,fs) 2 | 3 | % version: ds to 30 hz 4 | 5 | options.do_ds = true; 6 | options.ds_freq = 50; 7 | 8 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 9 | 10 | end 11 | -------------------------------------------------------------------------------- /source/dev/msptdpc6_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc6_beat_detector(sig,fs) 2 | 3 | % version: ds to 30 hz 4 | 5 | options.do_ds = true; 6 | options.ds_freq = 30; 7 | 8 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 9 | 10 | end 11 | -------------------------------------------------------------------------------- /source/dev/msptdpc7_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc7_beat_detector(sig,fs) 2 | 3 | % version: ds to 20 hz 4 | 5 | options.do_ds = true; 6 | options.ds_freq = 20; 7 | 8 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 9 | 10 | end 11 | -------------------------------------------------------------------------------- /source/dev/msptdpc8_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc8_beat_detector(sig,fs) 2 | 3 | % version: ds to 10 hz 4 | 5 | options.do_ds = true; 6 | options.ds_freq = 10; 7 | 8 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 9 | 10 | end 11 | -------------------------------------------------------------------------------- /source/dev/msptdpc4_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc4_beat_detector(sig,fs) 2 | 3 | % version: reduced lms scales (30 bpm) 4 | 5 | options.use_reduced_lms_scales = true; 6 | 7 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 8 | 9 | end 10 | -------------------------------------------------------------------------------- /docs/tutorials/time_alignment.md: -------------------------------------------------------------------------------- 1 | # Time Alignment 2 | 3 | Aligning electrocardiogram (ECG) and photoplethysmogram (PPG) signals to compare beat detections. 4 | 5 | --- 6 | 7 | ## Aligning an ECG and a PPG signal 8 | 9 | This tutorial demonstrates how to align an ECG and a PPG signal based on beats detected in each signal. -------------------------------------------------------------------------------- /source/dev/msptdpc12_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc12_beat_detector(sig,fs) 2 | 3 | % version: reduced lms scales (40 bpm) 4 | 5 | options.use_reduced_lms_scales = true; 6 | 7 | options.plaus_hr_bpm = [40, 200]; 8 | 9 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 10 | 11 | end 12 | -------------------------------------------------------------------------------- /source/dev/msptdpc13_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = msptdpc13_beat_detector(sig,fs) 2 | 3 | % version: optimal selection (2024-07-29) 4 | 5 | options.find_trs = true; 6 | options.find_pks = true; 7 | options.do_ds = true; 8 | options.ds_freq = 20; 9 | options.use_reduced_lms_scales = true; % default 30 bpm 10 | 11 | [peaks, onsets] = msptdpcref_beat_detector(sig,fs, options); 12 | 13 | end 14 | -------------------------------------------------------------------------------- /docs/toolbox/acknowledgments.md: -------------------------------------------------------------------------------- 1 | # Acknowledgments 2 | 3 | ## Authors 4 | 5 | The toolbox was created by [Peter H. Charlton](http://peterhcharlton.github.io/), with input from: 6 | 7 | - Elisa Mejia-Mejia 8 | - Philip J Aston 9 | - Callum Pettit 10 | - Joachim A Behar 11 | - Kevin Kotzen 12 | - Karthik Budidha 13 | - Jonathan Mant 14 | - Panicos Kyriacou 15 | 16 | ## Funders 17 | 18 | This work was supported by British Heart Foundation (BHF) grants [FS/20/20/34626] and [PG/15/104/31913], and an EPSRC Impact Acceleration Award. 19 | 20 | ## Citation 21 | When using this toolbox, please cite: 22 | 23 | Charlton PH _et al._, [Detecting beats in the photoplethysmogram: benchmarking open-source algorithms](https://doi.org/10.1088/1361-6579/ac826d), Physiological Measurement, 2022. -------------------------------------------------------------------------------- /docs/tutorials/summary.md: -------------------------------------------------------------------------------- 1 | # Tutorials 2 | 3 | Tutorials to get started with **PPG-beats**. 4 | 5 | --- 6 | 7 | ## Summary of tutorials 8 | 9 | Follow the links to access each tutorial. 10 | 11 | Tutorial | Description 12 | :--- | :--- 13 | **[PPG Beat Detection](../ppg_beat_detection)** | Demonstrates how to detect beats in a PPG signal using algorithms in the toolbox. 14 | **[Performance Assessment](../performance_assessment)** | Demonstrates how to assess the performance of PPG beat detectors using publicly available datasets. 15 | **[ECG Beat Detection](../ecg_beat_detection)** | Demonstrates how to detect beats in an ECG signal using algorithms in the toolbox. 16 | **[Designing a Beat Detector](../designing_beat_detector)** | Demonstrates how the MSPTDfast (v1) PPG beat detector was designed using the toolbox. 17 | **[Time Alignment](../time_alignment)** | Demonstrates how to align an ECG and a PPG signal based on beats detected in each signal. 18 | **[Creating Documentation](../creating_docs)** | Instructions for creating the documentation for this repository, and hosting it online. -------------------------------------------------------------------------------- /docs/functions/pwd_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `PWD_BEAT_DETECTOR` - PWD PPG beat detector. 2 | PWD_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Pulse Wave Delineator' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | B. N. Li et al., 'On an automatic delineator for arterial blood pressure waveforms,' Biomedical Signal Processing and Control, vol. 5, no. 1, pp. 76-81, 2010. 17 | 18 | ## Author 19 | + LI Bing Nan - created the original code 20 | 21 | + Peter H. Charlton - did very little, just wrote this wrapper 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## Source 30 | 31 | 32 | ## Licence 33 | The 2-Clause BSD License (see the copyright notice, list of conditions, and disclaimer below) 34 | -------------------------------------------------------------------------------- /docs/functions/tidy_beats.md: -------------------------------------------------------------------------------- 1 | # `TIDY_BEATS` - Cleans up beat detections. 2 | TIDY_BEATS makes the vector of beat indices into a column vector of 3 | unique values. 4 | 5 | ## Inputs 6 | + beat_indices : a vector containing the indices of beat detections 7 | 8 | ## Outputs 9 | + beat_indices : a cleaned up vector of indices of beat detections 10 | 11 | ## Author 12 | Peter H. Charlton, February 2022 13 | 14 | ## Documentation 15 | 16 | 17 | ## Version 18 | 1.0 19 | 20 | ## License - GPL-3.0 21 | Copyright (c) 2022 Peter H. Charlton 22 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 23 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 24 | You should have received a copy of the GNU General Public License along with this program. If not, see . 25 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "peterhcharlton", 10 | "name": "Peter H Charlton", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/9865941?v=4", 12 | "profile": "https://peterhcharlton.github.io/", 13 | "contributions": [ 14 | "code", 15 | "content", 16 | "data", 17 | "doc", 18 | "tutorial" 19 | ] 20 | }, 21 | { 22 | "login": "elisamejia", 23 | "name": "Elisa Mejía", 24 | "avatar_url": "https://avatars.githubusercontent.com/u/10887584?v=4", 25 | "profile": "https://github.com/elisamejia", 26 | "contributions": [ 27 | "code" 28 | ] 29 | }, 30 | { 31 | "login": "jbehar", 32 | "name": "jbehar", 33 | "avatar_url": "https://avatars.githubusercontent.com/u/7780023?v=4", 34 | "profile": "https://github.com/jbehar", 35 | "contributions": [ 36 | "code" 37 | ] 38 | } 39 | ], 40 | "contributorsPerLine": 7, 41 | "projectName": "ppg-beats", 42 | "projectOwner": "peterhcharlton", 43 | "repoType": "github", 44 | "repoHost": "https://github.com", 45 | "skipCi": true 46 | } 47 | -------------------------------------------------------------------------------- /docs/datasets/bidmc.md: -------------------------------------------------------------------------------- 1 | # BIDMC Dataset 2 | 3 | --- 4 | 5 | This dataset contains high-quality ECG and PPG recordings of 8-minute duration from critically-ill adults during routine clinical care. 6 | 7 | ## Overview 8 | 9 | Item | Details 10 | :--- | :--- 11 | **Links** | [Dataset](https://doi.org/10.13026/C2208R), [Publication](https://doi.org/10.1109/TBME.2016.2613124) 12 | **Signals** | PPG, ECG, respiration 13 | **No. Subjs** | 53 14 | **Protocol** | Recordings from critically-ill adults during routine clinical care (mostly high-quality recordings). Data were measured using a bedside monitor at 125 Hz. Data were extracted from the [MIMIC II Matched Waveform Database](https://physionet.org/physiobank/database/mimic2wdb/matched/). 15 | 16 | ## Downloading the dataset 17 | 18 | The dataset can be downloaded using the following links: 19 | 20 | - [MATLAB format](https://physionet.org/content/bidmc/1.0.0/bidmc_data.mat): a single file for download. 21 | - [CSV format](https://doi.org/10.13026/C2208R): the files should be downloaded using the instructions [here](https://physionet.org/content/bidmc/1.0.0/#files). 22 | - [WFDB format](https://doi.org/10.13026/C2208R): the files should be downloaded using the instructions [here](https://physionet.org/content/bidmc/1.0.0/#files). 23 | -------------------------------------------------------------------------------- /docs/functions/pulse_onsets_from_peaks.md: -------------------------------------------------------------------------------- 1 | # `PULSE_ONSETS_FROM_PEAKS` - Identifies pulse onsets. 2 | PULSE_ONSETS_FROM_PEAKS detects pulse onsets in a photoplethysmogram 3 | (PPG) signal from the locations of the pulse peaks 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + peaks : indices of detected pulse peaks 9 | 10 | ## Outputs 11 | + onsets : indices of detected pulse onsets 12 | 13 | ## Author 14 | Peter H. Charlton, University of Cambridge, February 2022 15 | 16 | ## Documentation 17 | 18 | 19 | ## Version 20 | 1.0 21 | 22 | ## License - GPL-3.0 23 | Copyright (c) 2022 Peter H. Charlton 24 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 25 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 26 | You should have received a copy of the GNU General Public License along with this program. If not, see . 27 | -------------------------------------------------------------------------------- /docs/functions/pulse_peaks_from_onsets.md: -------------------------------------------------------------------------------- 1 | # `PULSE_PEAKS_FROM_ONSETS` - Identifies pulse onsets. 2 | PULSE_PEAKS_FROM_ONSETS detects pulse peaks in a photoplethysmogram 3 | (PPG) signal from the locations of the pulse onsets 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + onsets : indices of detected pulse onsets 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | ## Author 14 | Peter H. Charlton, University of Cambridge, February 2022 15 | 16 | ## Documentation 17 | 18 | 19 | ## Version 20 | 1.0 21 | 22 | ## License - GPL-3.0 23 | Copyright (c) 2022 Peter H. Charlton 24 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 25 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 26 | You should have received a copy of the GNU General Public License along with this program. If not, see . 27 | -------------------------------------------------------------------------------- /docs/toolbox/performance_assessment.md: -------------------------------------------------------------------------------- 1 | # Performance Assessment 2 | 3 | Resources to assess the performance of PPG beat detectors. 4 | 5 | --- 6 | 7 | This page provides details of resources provided to assess the performance of PPG beat detectors. 8 | 9 | ## Datasets 10 | 11 | Several [datasets](/datasets/summary) are provided on which to assess the performance of PPG beat detectors. Each dataset contains: 12 | 13 | - PPG signals 14 | - ECG signals (to obtain reference heartbeats) 15 | 16 | ## Code 17 | 18 | **[`assess_beat_detectors.m`](/functions/assess_beat_detectors/)**: The main function for assessing the performance of PPG beat detectors. This can be used to assess the performance of beat detectors on a single dataset (as demonstrated in the tutorial [here](/tutorials/performance_assessment/)). 19 | 20 | **[`assess_multiple_datasets.m`](/functions/assess_multiple_datasets/)**: This is a wrapper for `assess_beat_detectors.m`. It allows PPG beat detectors to be assessed across multiple datasets by: calling the main assessment function (`assess_beat_detectors.m`) for each dataset in turn; and then creating figures to display the performance on each dataset. [This tutorial](/tutorials/performance_assessment/) provides an example of how to use this function to assess performance across multiple datasets. -------------------------------------------------------------------------------- /docs/functions/qppgfast_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `QPPGFAST_BEAT_DETECTOR` - QPPGFAST PPG beat detector. 2 | QPPGFAST_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the fast version of the 'Adapted Onset Detector' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | A. N. Vest et al., 'An open source benchmarked toolbox for cardiovascular waveform and interval analysis,' Physiological Measurement, vol. 39, no. 10, 2018. 17 | 18 | ## Author 19 | + Several authors have contributed to the code (see below) 20 | 21 | + Peter H. Charlton - did very little, just wrote this wrapper (which detects peaks given the onsets provided by the code) 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## Source 30 | qppg_fast.m, from the PhysioNet Cardiovascular Signal Toolbox (as of 4 May 2022) at: 31 | . 32 | The toolbox is licensed under the BSD-3-Clause License. 33 | 34 | ## Licence 35 | Please see BSD 3-Clause License and GNU GPL Licence below 36 | -------------------------------------------------------------------------------- /docs/functions/detect_ppg_beats.md: -------------------------------------------------------------------------------- 1 | # `DETECT_PPG_BEATS` - detects beats in PPG. 2 | DETECT_PPG_BEATS detects beats in a photoplethysmogram (PPG) signal 3 | using a specified beat detector 4 | 5 | ## Inputs 6 | + s : a structure containing the following fields: 7 | 8 | - v : a vector of PPG values 9 | - fs : the sampling frequency of the PPG in Hz 10 | 11 | + beat_detector - a string specifying the beat detector to be used 12 | 13 | ## Outputs 14 | + peaks : ...TBC... 15 | 16 | + onsets : ...TBC... 17 | 18 | + mid_amps : ...TBC... 19 | 20 | ## Documentation 21 | 22 | 23 | ## Author 24 | Peter H. Charlton, University of Cambridge, February 2022. 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## License - GPL-3.0 30 | Copyright (c) 2022 Peter H. Charlton 31 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 32 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 33 | You should have received a copy of the GNU General Public License along with this program. If not, see . 34 | -------------------------------------------------------------------------------- /docs/functions/detect_ppg_beats_old.md: -------------------------------------------------------------------------------- 1 | # `DETECT_PPG_BEATS` - detects beats in PPG. 2 | DETECT_PPG_BEATS detects beats in a photoplethysmogram (PPG) signal 3 | using a specified beat detector 4 | 5 | ## Inputs 6 | + s : a structure containing the following fields: 7 | 8 | - v : a vector of PPG values 9 | - fs : the sampling frequency of the PPG in Hz 10 | 11 | + beat_detector - a string specifying the beat detector to be used 12 | 13 | ## Outputs 14 | + peaks : ...TBC... 15 | 16 | + onsets : ...TBC... 17 | 18 | + mid_amps : ...TBC... 19 | 20 | ## Documentation 21 | 22 | 23 | ## Author 24 | Peter H. Charlton, University of Cambridge, February 2022. 25 | 26 | ## Version 27 | 0.1, and is still in development. 28 | 29 | ## Licence 30 | This file is part of PPG-beats. 31 | PPG-beats is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 32 | PPG-beats is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 33 | You should have received a copy of the GNU General Public License along with PPG-beats. If not, see . 34 | -------------------------------------------------------------------------------- /source/tidy_beats.m: -------------------------------------------------------------------------------- 1 | function beat_indices = tidy_beats(beat_indices) 2 | % TIDY_BEATS Cleans up beat detections. 3 | % TIDY_BEATS makes the vector of beat indices into a column vector of 4 | % unique values. 5 | % 6 | % # Inputs 7 | % 8 | % * beat_indices : a vector containing the indices of beat detections 9 | % 10 | % # Outputs 11 | % * beat_indices : a cleaned up vector of indices of beat detections 12 | % 13 | % # Author 14 | % Peter H. Charlton, February 2022 15 | % 16 | % # Documentation 17 | % 18 | % 19 | % # Version 20 | % 1.0 21 | % 22 | % # License - GPL-3.0 23 | % Copyright (c) 2022 Peter H. Charlton 24 | % This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 25 | % This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 26 | % You should have received a copy of the GNU General Public License along with this program. If not, see . 27 | 28 | % - ensure it's a column vector 29 | beat_indices = beat_indices(:); 30 | 31 | % - only keep unique values 32 | beat_indices = unique(beat_indices); 33 | 34 | end -------------------------------------------------------------------------------- /docs/datasets/capnobase.md: -------------------------------------------------------------------------------- 1 | # CapnoBase Dataset 2 | 3 | --- 4 | 5 | ## Overview 6 | 7 | Item | Details 8 | :--- | :--- 9 | **Links** | [Dataset](https://doi.org/10.5683/SP2/NLB8IT), [Publication](https://doi.org/10.1109/TBME.2013.2246160) 10 | **Signals** | PPG, ECG (inc. manual beat annotations), respiration 11 | **No. Subjs** | 42 12 | **Protocol** | Recordings from adults and children during elective surgery and routine anaesthesia (mostly high-quality recordings). 13 | 14 | ## Importing the data into MATLAB 15 | 16 | I took the following steps to import the CapnoBase dataset to MATLAB: 17 | 18 | 1. Download the data from [here](https://doi.org/10.5683/SP2/NLB8IT). Note that the dataset contains 337 files, but only the 42 files ending in __8min.mat_ are required. You can select these files by: (i) searching for "*_8min.mat" in the 'Find' box; (ii) setting the 'Files Per Page' to 50 (see the dropdown menu at the bottom of the page; and (iii) ticking the box at the header of the table to select all 42 listed files. Then use the 'Download' button on the right hand side of the header to download the files. 19 | 2. Unzip the downloaded zip file. 20 | 3. Download the [`collate_capnobase_dataset.m` MATLAB script](https://raw.githubusercontent.com/peterhcharlton/info/master/collating_datasets/collate_capnobase_dataset.m). 21 | 4. Modify the MATLAB script by inserting the `up.paths.root_folder` and `up.paths.save_folder` into the `setup_up` function. 22 | 5. Run the MATLAB script to collate all the individual MATLAB files into a single MATLAB data file, ready for analysis. 23 | -------------------------------------------------------------------------------- /source/pulse_onsets_from_peaks.m: -------------------------------------------------------------------------------- 1 | function onsets = pulse_onsets_from_peaks(sig, peaks) 2 | % PULSE_ONSETS_FROM_PEAKS Identifies pulse onsets. 3 | % PULSE_ONSETS_FROM_PEAKS detects pulse onsets in a photoplethysmogram 4 | % (PPG) signal from the locations of the pulse peaks 5 | % 6 | % # Inputs 7 | % 8 | % * sig : a vector of PPG values 9 | % * peaks : indices of detected pulse peaks 10 | % 11 | % # Outputs 12 | % * onsets : indices of detected pulse onsets 13 | % 14 | % # Author 15 | % Peter H. Charlton, University of Cambridge, February 2022 16 | % 17 | % # Documentation 18 | % 19 | % 20 | % # Version 21 | % 1.0 22 | % 23 | % # License - GPL-3.0 24 | % Copyright (c) 2022 Peter H. Charlton 25 | % This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 26 | % This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 27 | % You should have received a copy of the GNU General Public License along with this program. If not, see . 28 | 29 | onsets = nan(length(peaks)-1,1); 30 | for wave_no = 1 : length(peaks)-1 31 | [~, temp] = min(sig(peaks(wave_no):peaks(wave_no+1))); 32 | onsets(wave_no) = temp + peaks(wave_no) - 1; 33 | end 34 | 35 | end -------------------------------------------------------------------------------- /source/pulse_peaks_from_onsets.m: -------------------------------------------------------------------------------- 1 | function peaks = pulse_peaks_from_onsets(sig, onsets) 2 | % PULSE_PEAKS_FROM_ONSETS Identifies pulse onsets. 3 | % PULSE_PEAKS_FROM_ONSETS detects pulse peaks in a photoplethysmogram 4 | % (PPG) signal from the locations of the pulse onsets 5 | % 6 | % # Inputs 7 | % 8 | % * sig : a vector of PPG values 9 | % * onsets : indices of detected pulse onsets 10 | % 11 | % # Outputs 12 | % * peaks : indices of detected pulse peaks 13 | % 14 | % # Author 15 | % Peter H. Charlton, University of Cambridge, February 2022 16 | % 17 | % # Documentation 18 | % 19 | % 20 | % # Version 21 | % 1.0 22 | % 23 | % # License - GPL-3.0 24 | % Copyright (c) 2022 Peter H. Charlton 25 | % This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 26 | % This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 27 | % You should have received a copy of the GNU General Public License along with this program. If not, see . 28 | 29 | peaks = nan(length(onsets)-1,1); 30 | for wave_no = 1 : length(onsets)-1 31 | [~, temp] = max(sig(onsets(wave_no):onsets(wave_no+1))); 32 | peaks(wave_no) = temp + onsets(wave_no) - 1; 33 | end 34 | 35 | end -------------------------------------------------------------------------------- /docs/functions/atmax_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `ATMAX_BEAT_DETECTOR` - ATmax PPG beat detector. 2 | ATMAX_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Adaptive Threshold Method (Vmax)' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference (algorithm) 16 | H. S.Shin et al., 'Adaptive threshold method for the peak detection of photoplethysmographic waveform,' Comput Biol Med Elsevier, vol.39, no.12, pp.1145-52, 2009. <https://doi.org/10.1016/j.compbiomed.2009.10.006> 17 | 18 | ## Reference (implementation) 19 | D. Han et al., 'A Real-Time PPG Peak Detection Method for Accurate Determination of Heart Rate during Sinus Rhythm and Cardiac Arrhythmia,' Biosensors, vol.12, no.2, p.82, 2022. <https://doi.org/10.3390/bios12020082> 20 | 21 | ## Author 22 | + Dong Han - wrote the 'my_peak_compare_Shin_2009' function. 23 | 24 | + Peter H. Charlton - did very little, just wrote this wrapper (which detects onsets given the peaks provided by the code) 25 | 26 | ## Documentation 27 | 28 | 29 | ## Version 30 | 1.0 31 | 32 | ## Source 33 | my_peak_compare_Shin_2009.m, from the PPG_Peak_Detection GitHub Repository (accessed on 3 Mar 2022) at: 34 | 35 | ## Licence 36 | MIT Licence (see the licence at the top of the 'my_peak_compare_shin_2009' function in the code below). 37 | -------------------------------------------------------------------------------- /docs/functions/atmin_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `ATMIN_BEAT_DETECTOR` - ATmin PPG beat detector. 2 | ATMIN_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Adaptive Threshold Method (Vmin)' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference (algorithm) 16 | H. S.Shin et al., 'Adaptive threshold method for the peak detection of photoplethysmographic waveform,' Comput Biol Med Elsevier, vol.39, no.12, pp.1145-52, 2009. <https://doi.org/10.1016/j.compbiomed.2009.10.006> 17 | 18 | ## Reference (implementation) 19 | D. Han et al., 'A Real-Time PPG Peak Detection Method for Accurate Determination of Heart Rate during Sinus Rhythm and Cardiac Arrhythmia,' Biosensors, vol.12, no.2, p.82, 2022. <https://doi.org/10.3390/bios12020082> 20 | 21 | ## Author 22 | + Dong Han - wrote the 'my_peak_compare_Shin_2009' function. 23 | 24 | + Peter H. Charlton - did very little, just wrote this wrapper (which detects peaks given the onsets provided by the code) 25 | 26 | ## Documentation 27 | 28 | 29 | ## Version 30 | 1.0 31 | 32 | ## Source 33 | my_peak_compare_Shin_2009.m, from the PPG_Peak_Detection GitHub Repository (accessed on 3 Mar 2022) at: 34 | 35 | ## Licence 36 | MIT Licence (see the licence at the top of the 'my_peak_compare_shin_2009' function in the code below). 37 | -------------------------------------------------------------------------------- /docs/functions/collate_mimic_perform_af_dataset.md: -------------------------------------------------------------------------------- 1 | # `COLLATE_MIMIC_PERFORM_AF_DATASET` - Collate AF dataset. 2 | COLLATE_MIMIC_PERFORM_AF_DATASET downloads and collates data from the MIMIC-III Waveform 3 | Database Matched Subset during Atrial Fibrillation (AF) and sinus rhythm. 4 | 5 | ## Inputs 6 | + none (although see 'setup_up' for the parameters to be set) 7 | 8 | ## Working Files 9 | The script downloads several files from PhysioNet, which are saved locally. 10 | 11 | ## Outputs 12 | + files : Two MATLAB files containing the required data for PPG beat detector evaluation: 'mimic_af_data.mat', and 'mimic_non_af_data.mat'. 13 | 14 | ## Preparation: 15 | Modify the MATLAB script by inserting the 'up.paths.root_folder' and 'up.paths.save_folder' into the setup_up function. 16 | 17 | ## Documentation 18 | 19 | 20 | ## Author 21 | Peter H. Charlton, University of Cambridge, June 2022. 22 | 23 | ## License - GPL-3.0 24 | Copyright (c) 2022 Peter H. Charlton 25 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 26 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 27 | You should have received a copy of the GNU General Public License along with this program. If not, see . 28 | -------------------------------------------------------------------------------- /docs/functions/ppgpulses_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `PPGPULSES_BEAT_DETECTOR` - PPGPULSES PPG beat detector. 2 | PPGPULSES_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'PPG Pulses' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | J. Lazaro et al., 'Pulse rate variability analysis for discrimination of sleep-apnea-related decreases in the amplitude fluctuations of pulse photoplethysmographic signal in children,' IEEE Journal of Biomedical and Health Informatics, vol. 18, no. 1, pp. 240-246, 2014. 17 | 18 | ## Author 19 | + Jesus Lazaro - created the original code 20 | 21 | + Mariano Llamedo Soria - adapted it to the ecg-kit project 22 | 23 | + Peter H. Charlton - did very little, just wrote this wrapper 24 | 25 | ## Documentation 26 | 27 | 28 | ## Version 29 | 1.0 30 | 31 | ## Source 32 | This function was copied from the 'ecg-kit' repository by 'marianux' at: 33 | , where it is licensed under the 34 | GNU License v2.0. See also: 35 | This particular function was available at: 36 | 37 | ## Licence 38 | This is available under the GNU General Public License v2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html 39 | -------------------------------------------------------------------------------- /docs/functions/swt_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `SWT_BEAT_DETECTOR` - SWT PPG beat detector. 2 | SWT_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Stationary Wavelet Transform' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + onsets : indices of detected pulse onets 12 | 13 | + peaks : indices of detected pulse peaks 14 | 15 | ## Reference (algorithm) 16 | S. Vadrevu and M.Sabarimalai Manikandan, 'A robust pulse onset and peak detection method for automated PPG signal analysis system,' IEEE Trans Instrum Meas, vol.68, no.3, pp.807-817, 2019. 17 | 18 | ## Reference (implementation) 19 | D. Han et al., 'A Real-Time PPG Peak Detection Method for Accurate Determination of Heart Rate during Sinus Rhythm and Cardiac Arrhythmia,' Biosensors, vol.12, no.2, p.82, 2022. 20 | 21 | ## Author 22 | + Dong Han - wrote the 'my_Vadrevu_2019_peakdet' function. 23 | 24 | + Peter H. Charlton - did very little, just wrote this wrapper and made a few changes to how the zero-padding of the signal is performed (as commented in the code). 25 | 26 | ## Documentation 27 | 28 | 29 | ## Version 30 | 1.0 31 | 32 | ## Source 33 | my_Vadrevu_2019_peakdet.m, from the PPG_Peak_Detection GitHub Repository (accessed on 5 Mar 2022) at: 34 | 35 | ## Licence 36 | MIT Licence (see the licence at the top of the 'my_Vadrevu_2019_peakdet' function in the code below). 37 | -------------------------------------------------------------------------------- /docs/functions/assess_multiple_datasets.md: -------------------------------------------------------------------------------- 1 | # `ASSESS_MULTIPLE_DATASETS` - Assess on multiple datasets. 2 | ASSESS_MULTIPLE_DATASETS assesses the performance of multiple 3 | PPG beat detectors across multiple datasets. 4 | 5 | ## Inputs 6 | + none 7 | 8 | + although the datasets to be analysed should be specified in the 'setup_universal_params' function, and their paths should be specified in the 'specify_path_of_dataset_file' function. 9 | 10 | ## Outputs 11 | + Plots illustrating the performance of PPG beat detectors. 12 | 13 | + NB: the 'ppg_beat_detector_assessment' function, which is called by this function, creates its own outputs too 14 | 15 | ## Exemplary usage 16 | run_ppg_beat_detector_assessment % runs the PPG beat detector assessment 17 | 18 | ## Documentation 19 | 20 | 21 | ## Author 22 | Peter H. Charlton, University of Cambridge, August 2022. 23 | 24 | ## Version 25 | 1.0 26 | 27 | ## License - GPL-3.0 28 | Copyright (c) 2022 Peter H. Charlton 29 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 30 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | You should have received a copy of the GNU General Public License along with this program. If not, see . 32 | -------------------------------------------------------------------------------- /docs/functions/collate_mimic_perform_ethnicity_dataset.md: -------------------------------------------------------------------------------- 1 | # `COLLATE_MIMIC_PERFORM_ETHNICITY_DATASET` - Collates ethnicity dataset. 2 | COLLATE_MIMIC_PERFORM_ETHNICITY_DATASET Downloads and collates data from the MIMIC-III 3 | Waveform Database Matched Subset from Black and White subjects, for PPG beat 4 | detector performance evaluation. 5 | 6 | ## Inputs 7 | + none (although see 'setup_up' for the parameters to be set) 8 | 9 | ## Working Files 10 | The script downloads several files from PhysioNet, which are saved locally. 11 | 12 | ## Outputs 13 | + files : Two MATLAB files containing the required data for PPG beat detector evaluation: 'mimic_B_data.mat', and 'mimic_W_data.mat'. 14 | 15 | ## Preparation: 16 | Modify the MATLAB script by inserting the 'up.paths.root_folder' and 'up.paths.save_folder' into the setup_up function. 17 | 18 | ## Documentation 19 | 20 | 21 | ## Author 22 | Peter H. Charlton, University of Cambridge, June 2022. 23 | 24 | ## License - GPL-3.0 25 | Copyright (c) 2022 Peter H. Charlton 26 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 27 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 28 | You should have received a copy of the GNU General Public License along with this program. If not, see . 29 | -------------------------------------------------------------------------------- /docs/functions/export_sample_mimic_perform_data.md: -------------------------------------------------------------------------------- 1 | # `EXPORT_SAMPLE_MIMIC_PERFORM_DATA` - Export sample data. 2 | EXPORT_SAMPLE_MIMIC_PERFORM_DATA exports sample data excerpts from the 3 | MIMIC Perform datasets. 4 | 5 | ## Inputs 6 | None, although the script uses the following MIMIC PERform dataset files (which can be downloaded from ): 7 | 8 | + mimic_perform_train_all_data.mat 9 | 10 | + mimic_perform_train_n_data.mat 11 | 12 | + mimic_perform_test_all_data.mat 13 | 14 | + mimic_perform_af_data.mat 15 | 16 | + mimic_perform_non_af_data.mat 17 | 18 | ## Outputs 19 | + files : MATLAB files containing the data excerpts. 20 | 21 | ## Preparation: 22 | Modify the MATLAB script by specifying the filepaths of the data files and the folder in which to save excerpts (within the setup_up function). 23 | 24 | ## Documentation 25 | 26 | 27 | ## Author 28 | Peter H. Charlton, University of Cambridge, June 2022. 29 | 30 | ## License - GPL-3.0 31 | Copyright (c) 2022 Peter H. Charlton 32 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 33 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 34 | You should have received a copy of the GNU General Public License along with this program. If not, see . 35 | -------------------------------------------------------------------------------- /docs/datasets/summary.md: -------------------------------------------------------------------------------- 1 | # Datasets 2 | 3 | Publicly available datasets with which to assess the performance of PPG beat detectors. 4 | 5 | --- 6 | 7 | ## Summary of available datasets 8 | 9 | Follow the links to find out more about each dataset. 10 | 11 | Dataset | Description 12 | :--- | :--- 13 | **[CapnoBase](../capnobase)** | High-quality recordings from patients undergoing elective surgery and routine anaesthesia. 14 | **[BIDMC](../bidmc)** | High-quality recordings from critically-ill adults during routine clinical care. 15 | **[MIMIC PERform Training Dataset](../mimic_perform_training)** | Recordings from patients during routine clinical care, who are categorised as either adults or neonates. 16 | **[MIMIC PERform Testing Dataset](../mimic_perform_testing)** | Recordings from patients during routine clinical care, who are categorised as either adults or neonates. 17 | **[MIMIC PERform AF Dataset](../mimic_perform_af)** | Recordings from critically-ill adults during routine clinical care, categorised as either AF (atrial fibrillation) or non-AF. 18 | **[MIMIC PERform Ethnicity Dataset](../mimic_perform_ethnicity)** | Recordings from critically-ill adults during routine clinical care, who are categorised as either Black or White ethnicity. 19 | **[WESAD](../wesad)** | Recordings from volunteers during a laboratory-based protocol designed to induce different emotions. 20 | **[PPG-DaLiA](../ppg_dalia)** | Recordings from volunteers during a protocol of activities of daily living. 21 | 22 | --- 23 | 24 | _**Source:** descriptions are adapted from [DOI: 10.1088/1361-6579/ac826d](http://peterhcharlton.github.io/publication/assess_ppg_beat_detectors/) under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)._ 25 | 26 | --- -------------------------------------------------------------------------------- /docs/datasets/wesad.md: -------------------------------------------------------------------------------- 1 | # WESAD Dataset 2 | 3 | --- 4 | 5 | ## Overview 6 | 7 | Item | Details 8 | :--- | :--- 9 | **Links** | [Dataset](https://archive.ics.uci.edu/ml/datasets/WESAD+%28Wearable+Stress+and+Affect+Detection%29), [Publication](https://doi.org/10.1145/3242969.3242985) 10 | **Signals** | PPG, ECG, respiration, accelerometry, others 11 | **No. Subjs** | 15 12 | **Protocol** | Recordings during a laboratory-based protocol designed to induce different emotions, consisting of: baseline, amusement, stress and meditation 13 | 14 | ## Importing the data into MATLAB 15 | 16 | I took the following steps to import the WESAD dataset to MATLAB: 17 | 18 | 1. Download the data from [here](https://uni-siegen.sciebo.de/s/HGdUkoNlW1Ub0Gx) (as stated [here](https://ubicomp.eti.uni-siegen.de/home/datasets/icmi18/)). 19 | 2. Download the [`convert_subject_pickle_files_to_mat.py` python script](https://raw.githubusercontent.com/peterhcharlton/resources/master/collating_datasets/convert_subject_pickle_files_to_mat.py). 20 | 3. Copy the subject's _.pkl_ file to the same folder as this python script. 21 | 4. Run the python script to convert the _.pkl_ file into a MATLAB file. (I used Spyder - a Python program - through Anaconda-Navigator.) 22 | 5. Copy this MATLAB file back to the same folder as the _.pkl_ file was obtained from. 23 | 6. Download the [`collate_wesad_dataset.m` MATLAB script](https://raw.githubusercontent.com/peterhcharlton/info/master/collating_datasets/collate_wesad_dataset.m). 24 | 7. Modify the MATLAB script by inserting the `up.paths.root_folder` and `up.paths.save_folder` into the `setup_up` function. 25 | 8. Run the MATLAB script to collate all the individual MATLAB files into a single MATLAB data file, ready for analysis. 26 | -------------------------------------------------------------------------------- /docs/datasets/mimic_perform_ethnicity.md: -------------------------------------------------------------------------------- 1 | # MIMIC III 'Ethnicity' Subset 2 | 3 | --- 4 | 5 | This subset contains ECG and PPG recordings of 10-minute duration, some of which were acquired from Black adults, and some of which were acquired from White adults. 6 | 7 | ## Overview 8 | 9 | Item | Details 10 | :--- | :--- 11 | **Links** | [Publication](http://peterhcharlton.github.io/publication/assess_ppg_beat_detectors/) 12 | **Signals** | PPG, ECG, respiration 13 | **No. Subjs** | 200 (100 Black, 100 White) 14 | **Protocol** | 200 critically-ill adults during routine clinical care. Data were measured using a bedside monitor at 125 Hz. Data were extracted from the [MIMIC-III Waveform Database Matched Subset](https://physionet.org/files/mimic3wdb-matched/1.0/). 15 | 16 | ## Importing the data into MATLAB 17 | 18 | 1. Use the following MATLAB script to import the data: [`collate_mimic_perform_ethnicity_dataset`](/functions/collate_mimic_perform_ethnicity_dataset/). 19 | 2. Modify the MATLAB script by inserting the `up.paths.root_folder` and `up.paths.save_folder` into the `setup_up` function. 20 | - _Note:_ The duration of recordings provided by the script can be adjusted with the `up.settings.req_durn` variable within the `setup_up` function. 21 | - _Note:_ The number of subjects in each group can be adjusted with the `up.settings.no_subjs_per_ethnicity` variable within the `setup_up` function. 22 | 3. Run the MATLAB script to download the required files from PhysioNet, import the data into MATLAB, and collate the data into a single MATLAB data file, ready for analysis. 23 | 4. (optionally) convert the dataset into WFDB and/or CSV formats using this MATLAB script: [`mimic_perform_dataset_converter`](/functions/mimic_perform_dataset_converter/) 24 | -------------------------------------------------------------------------------- /docs/functions/spar_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `SPAR_BEAT_DETECTOR` - SPAR PPG beat detector. 2 | SPAR_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Symmetric Projection Attractor Reconstruction' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | C. Pettit and P. Aston, 'Photoplethysmogram (PPG) Beat Detection Using Symmetric Projection Attractor Reconstruction,' [in preparation]. 17 | 18 | ## Author 19 | + Callum Pettit and Philip Aston - wrote the code: 'SPARcycleTimesPPG' and 'find_acl' 20 | 21 | + Peter H. Charlton - modified the code slightly (as indicated), but mainly just wrote this wrapper 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## Licence 30 | SPAR PPG Beat Detector as [downloaded/received] is Copyright ©, 2021-2022 University of Surrey. 31 | 32 | All Rights Reserved - You are allowed to alter and use this code only for your own non-commercial research purposes and this excludes commercial use of any kind. You are not permitted to copy and distribute this code to anyone under any circumstances. All rights remain with the Copyright holders. 33 | 34 | If you wish to copy, distribute or make any commercial use of the Software please contact Surrey’s IP & Licensing team at techtransfer@surrey.ac.uk to request an appropriate licence. 35 | 36 | Please contact Philip Aston (P.Aston@surrey.ac.uk) with any technical questions or collaboration requests. 37 | -------------------------------------------------------------------------------- /docs/functions/collate_mimic_perform_train_test_datasets.md: -------------------------------------------------------------------------------- 1 | # `COLLATE_MIMIC_PERFORM_TRAIN_TEST_DATASETS` - Collates training and testing datasets. 2 | COLLATE_MIMIC_PERFORM_TRAIN_TEST_DATASETS downloads and collates data from the MIMIC-III 3 | Waveform Database, for PPG beat detector performance evaluation. 4 | 5 | ## Inputs 6 | + none (although see 'setup_up' for the parameters to be set) 7 | 8 | ## Working Files 9 | The script downloads several files from PhysioNet, which are saved locally. 10 | 11 | ## Outputs 12 | + files : MATLAB files containing the required data for PPG beat detector evaluation: 'mimic_train_a_data.mat', 'mimic_train_n_data.mat', 'mimic_train_all_data.mat', 'mimic_test_a_data.mat', 'mimic_test_n_data.mat', and 'mimic_test_all_data.mat'. 13 | 14 | ## Preparation 15 | Modify the MATLAB script by inserting the 'up.paths.root_folder' and 'up.paths.save_folder' into the setup_up function. 16 | 17 | ## Documentation 18 | 19 | 20 | ## Author 21 | Peter H. Charlton, University of Cambridge, June 2022. 22 | 23 | ## License - GPL-3.0 24 | Copyright (c) 2022 Peter H. Charlton 25 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 26 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 27 | You should have received a copy of the GNU General Public License along with this program. If not, see . 28 | -------------------------------------------------------------------------------- /docs/functions/ims_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `IMS_BEAT_DETECTOR` - IMS PPG beat detector. 2 | IMS_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Incremental Merge Segmentation' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : detected pulse peaks 12 | 13 | + onsets : detected pulse onsets 14 | 15 | ## Reference 16 | W. Karlen et al., 'Adaptive pulse segmentation and artifact detection in photoplethysmography for mobile applications,' in Proc. IEEE EMBS. IEEE, 2012, pp. 3131-4. 17 | 18 | ## Author 19 | + Marco A. Pimentel - wrote the code: 'adaptPulseSegment' 20 | 21 | + Peter H. Charlton - did very little, just wrote this wrapper 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## Source 30 | Original source: adaptPulseSegment.m , part of the RRest () repository which is covered by the GNU public licence. 31 | 32 | ## License - GPL-3.0 33 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 34 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 35 | You should have received a copy of the GNU General Public License along with this program. If not, see . 36 | -------------------------------------------------------------------------------- /docs/functions/abd_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `ABD_BEAT_DETECTOR` - ABD PPG beat detector. 2 | ABD_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Automatic Beat Detection' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | Aboy M et al., An automatic beat detection algorithm for pressure signals. IEEE Trans Biomed Eng 2005; 52: 1662-70. 17 | 18 | ## Author 19 | Peter H. Charlton: King's College London (August 2017), University of Cambridge (February 2022) 20 | 21 | ## Documentation 22 | 23 | 24 | ## Version 25 | 1.0 26 | 27 | ## Source 28 | This script contains items either copied or modified from the pulse-analyse 29 | () and RRest 30 | () repositories which are covered 31 | by the GNU public licence. 32 | 33 | ## License - GPL-3.0 34 | Copyright (c) 2022 Peter H. Charlton 35 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 36 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 37 | You should have received a copy of the GNU General Public License along with this program. If not, see . 38 | -------------------------------------------------------------------------------- /docs/functions/qppg_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `QPPG_BEAT_DETECTOR` - QPPG PPG beat detector. 2 | QPPG_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Adapted Onset Detector' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | A. N. Vest et al., 'An open source benchmarked toolbox for cardiovascular waveform and interval analysis,' Physiological Measurement, vol. 39, no. 10, 2018. 17 | 18 | ## Author 19 | + Several authors have contributed to the code (see below) 20 | 21 | + Peter H. Charlton - did very little, just wrote this wrapper (which detects peaks given the onsets provided by the code) 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## Source 30 | qppg.m, from the PhysioNet Cardiovascular Signal Toolbox 1.0.0 at: 31 | , which is licensed under 32 | the GNU General Public License v.2. 33 | 34 | ## License - GPL-3.0 35 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 36 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 37 | You should have received a copy of the GNU General Public License along with this program. If not, see . 38 | -------------------------------------------------------------------------------- /docs/functions/wepd_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `WEPD_BEAT_DETECTOR` - WEPD PPG beat detector. 2 | WEPD_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'waveform envelope peak detection' (WEPD) beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | D. Han et al., 'A Real-Time PPG Peak Detection Method for Accurate Determination of Heart Rate during Sinus Rhythm and Cardiac Arrhythmia,' Biosensors, vol. 12, no. 2, p. 82, 2022. 17 | 18 | ## Author 19 | Peter H. Charlton 20 | 21 | ## Documentation 22 | 23 | 24 | ## Version 25 | 1.1 26 | 27 | ## License - MIT 28 | Copyright (c) 2022 Peter H. Charlton 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 30 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | -------------------------------------------------------------------------------- /docs/datasets/ppg_dalia.md: -------------------------------------------------------------------------------- 1 | # PPG-DaLiA Dataset 2 | 3 | --- 4 | 5 | ## Overview 6 | 7 | Item | Details 8 | :--- | :--- 9 | **Links** | [Dataset](https://archive.ics.uci.edu/ml/datasets/PPG-DaLiA), [Publication](https://doi.org/10.3390/s19143079) 10 | **Signals** | PPG, ECG (inc. manual beat annotations), respiration, accelerometry, others 11 | **No. Subjs** | 15 12 | **Protocol** | Recordings during a protocol of activities of daily living (car driving, cycling, in a lunch break, sitting, stair climbing, playing table soccer, walking and working) 13 | 14 | ## Downloading the data in MATLAB format. 15 | 16 | The dataset can be downloaded in MATLAB format from [here](https://zenodo.org/doi/10.5281/zenodo.12793710). 17 | 18 | ## Importing the data into MATLAB 19 | 20 | If you want to reproduce the process used to obtain the data in MATLAB format from the original dataset, then follow these steps: 21 | 22 | 1. Download from [here](https://ubicomp.eti.uni-siegen.de/home/datasets/sensors19/) or [here](https://archive.ics.uci.edu/ml/datasets/PPG-DaLiA), and unzip. 23 | 2. Download the [`convert_subject_pickle_files_to_mat.py` python script](https://raw.githubusercontent.com/peterhcharlton/resources/master/collating_datasets/convert_subject_pickle_files_to_mat.py). 24 | 3. Copy the subject's _.pkl_ file to the same folder as this python script. 25 | 4. Run the python script to convert the _.pkl_ file into a MATLAB file. (I used Spyder - a Python program - through Anaconda-Navigator.) 26 | 5. Copy this MATLAB file back to the same folder as the _.pkl_ file was obtained from. 27 | 6. Download [`collate_ppg_dalia_dataset.m` MATLAB script](https://raw.githubusercontent.com/peterhcharlton/info/master/collating_datasets/collate_ppg_dalia_dataset.m). 28 | 7. Modify the MATLAB script by inserting the `up.paths.root_folder` and `up.paths.save_folder` into the `setup_up` function. 29 | 8. Run the MATLAB script to collate all the individual MATLAB files into a single MATLAB data file, ready for analysis. 30 | -------------------------------------------------------------------------------- /docs/functions/msptdfastv1_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `MSPTDFASTV1_BEAT_DETECTOR` - MSPTDfastv1 (v1.1) PPG beat detector. 2 | MSPTDFASTV1_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using a refinement of the 'Multi-Scale Peak and Trough Detection' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse troughs (i.e. onsets) 14 | 15 | ## Reference 16 | P. H. Charlton et al., 'MSPTDfast: An Efficient Photoplethysmography Beat Detection Algorithm,' Computing in Cardiology, 2024; 17 | 18 | ## Author 19 | Peter H. Charlton 20 | 21 | ## Documentation 22 | 23 | 24 | ## Version 25 | 1.1 26 | 27 | ## License - MIT 28 | Copyright (c) 2024 Peter H. Charlton 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 30 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | -------------------------------------------------------------------------------- /docs/functions/msptdfastv2_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `MSPTDFASTV2_BEAT_DETECTOR` - MSPTDfastv2 (v2.0) PPG beat detector. 2 | MSPTDFASTV2_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using a refinement of the 'Multi-Scale Peak and Trough Detection' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse troughs (i.e. onsets) 14 | 15 | ## Reference 16 | P. H. Charlton et al., 'The MSPTDfast photoplethysmography beat detection algorithm: Design, benchmarking, and open-source distribution,' Physiological Measurement, 17 | 18 | ## Author 19 | Peter H. Charlton 20 | 21 | ## Documentation 22 | 23 | 24 | ## Version 25 | 1.1 26 | 27 | ## License - MIT 28 | Copyright (c) 2024 Peter H. Charlton 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 30 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | -------------------------------------------------------------------------------- /docs/functions/msptd_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `MSPTD_BEAT_DETECTOR` - MSPTD PPG beat detector. 2 | MSPTD_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Multi-Scale Peak and Trough Detection' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse troughs (i.e. onsets) 14 | 15 | ## Reference 16 | S. M. Bishop and A. Ercole, 'Multi-scale peak and trough detection optimised for periodic and quasi-periodic neuroscience data,' in Intracranial Pressure and Neuromonitoring XVI. Acta Neurochirurgica Supplement, T. Heldt, Ed. Springer, 2018, vol. 126, pp. 189-195. 17 | 18 | ## Author 19 | Peter H. Charlton 20 | 21 | ## Documentation 22 | 23 | 24 | ## Version 25 | 1.0 26 | 27 | ## License - MIT 28 | Copyright (c) 2022 Peter H. Charlton 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 30 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | -------------------------------------------------------------------------------- /docs/functions/ampd_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `AMPD_BEAT_DETECTOR` - AMPD PPG beat detector. 2 | AMPD_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Automatic Multiscale-based Peak Detection' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | + lpf_freq : (optional) the frequency (in Hz) of low-pass filtering applied to the PPG 11 | 12 | ## Outputs 13 | + peaks : indices of detected pulse peaks 14 | 15 | + onsets : indices of detected pulse onsets 16 | 17 | ## Reference 18 | F. Scholkmann et al., 'An efficient algorithm for automatic peak detection in noisy periodic and quasi-periodic signals,' Algorithms, vol. 5, no. 4, pp. 588-603, 2012. 19 | 20 | ## Author 21 | Peter H. Charlton, University of Cambridge, February 2022. 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## License - MIT 30 | Copyright (c) 2022 Peter H. Charlton 31 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 32 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | -------------------------------------------------------------------------------- /docs/functions/coppg_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `COPPG_BEAT_DETECTOR` - COPPG PPG beat detector. 2 | COPPG_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Percentile Peak Detector' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | C. Orphanidou et al., 'Signal-quality indices for the electrocardiogram and photoplethysmogram: derivation and applications to wireless monitoring,' IEEE Journal of Biomedical and Health Informatics, vol. 19, no. 3, pp. 832-8, 2015. 17 | 18 | ## Author 19 | Adapted by Peter Charlton from code supplied by Christina Orphanidou. With grateful thanks to Christina Orphanidou and Alexander Darrell. 20 | 21 | ## Documentation 22 | 23 | 24 | ## Version 25 | 1.0 26 | 27 | ## Source 28 | + Original source: , part of the RRest () repository which is covered by the GNU public licence. 29 | 30 | + Modifications from original by PC: (i) Removed pre-processing; (ii) Hard-coded constants 31 | 32 | ## License - GPL-3.0 33 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 34 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 35 | You should have received a copy of the GNU General Public License along with this program. If not, see . 36 | -------------------------------------------------------------------------------- /docs/functions/mmpdv2_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `MMPDV2_BEAT_DETECTOR` - MMPD PPG beat detector. 2 | MMPDV2_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Mountaineer's Method for Peak Detection' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | E. J. A. Prada, 'The mountaineer's method for peak detection in photoplethysmographic signals,' Revista Facultad de Ingenieria, vol. 90, pp. 42-50, 2019. 17 | 18 | ## Author 19 | + E. J. Arguello Prada - wrote the code: 'Alpinista_simple_4_todos' 20 | 21 | + Peter H. Charlton - did very little, just wrote this wrapper 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## License - MIT 30 | Copyright (c) 2024 E. J. Arguello Prada and Peter H. Charlton 31 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 32 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | -------------------------------------------------------------------------------- /docs/functions/preprocess_ppg_signal.md: -------------------------------------------------------------------------------- 1 | # `PREPROCESS_PPG_SIGNAL` - pre-processes PPG signal. 2 | PREPROCESS_PPG_SIGNAL filters and downsamples a 3 | photoplethysmogram (PPG) signal ready for beat detection. 4 | 5 | ## Inputs 6 | + S : a structure containing three fields: 7 | 8 | - v : a vector of PPG values 9 | - fs : the sampling frequency of the PPG in Hz 10 | - no_signal : (optional) a logical indicating whether (1) or not (0) each PPG sample contains a physiological signal 11 | 12 | + options - A structure containing the settings used for the processing: 13 | 14 | - options.do_downsample - (1 or 0, default value of 0) A logical indicating whether or not to downsample the PPG signals prior to analysis. 15 | - options.downsample_freq - the frequency at which to downsample PPG signals (Hz). Only used if downsampling is enabled by options.do_downsample. 16 | - options.redo_analysis - A logical indicating whether or not to overwrite existing analysis files in order to redo the analysis 17 | - options.filtering.elim_high_freqs - ...TBC... 18 | - options.filtering.elim_low_freqs - ...TBC... 19 | 20 | ## Outputs 21 | + S : containing the filtered and downsampled PPG signal 22 | 23 | ## Documentation 24 | 25 | 26 | ## Author 27 | Peter H. Charlton, University of Cambridge, February 2022. 28 | 29 | ## Version 30 | 1.0 31 | 32 | ## License - GPL-3.0 33 | Copyright (c) 2022 Peter H. Charlton 34 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 35 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 36 | You should have received a copy of the GNU General Public License along with this program. If not, see . 37 | -------------------------------------------------------------------------------- /docs/functions/erma_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `ERMA_BEAT_DETECTOR` - ERMA PPG beat detector. 2 | ERMA_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Event-Related Moving Averages' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | M. Elgendi et al., 'Systolic peak detection in acceleration photoplethysmograms measured from emergency responders in tropical conditions,' PLoS ONE, vol. 8, no. 10, pp. 1-11, 2013. 17 | 18 | ## Author 19 | + Elisa Mejía Mejía - wrote the code: 'erma' 20 | 21 | + Peter H. Charlton - did very little, just wrote this wrapper and made slight edits (see 'erma') 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## MIT License 30 | Copyright (c) 2022 Elisa Mejía Mejía and Peter H. Charlton 31 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 32 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | -------------------------------------------------------------------------------- /docs/functions/wfd_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `WFD_BEAT_DETECTOR` - WFD PPG beat detector. 2 | WFD_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Wavelet Foot Delineation' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | N. J. Conn and D. A. Borkholder, 'Wavelet based photoplethysmogram foot delineation for heart rate variability applications,' in IEEE Signal Processing in Medicine and Biology Symposium. IEEE, 2013. 17 | 18 | ## Author 19 | + Elisa Mejía Mejía - wrote the code: 'wavelet' 20 | 21 | + Peter H. Charlton - did very little, just wrote this wrapper and made slight edits (see 'erma') 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## License - MIT 30 | Copyright (c) 2022 Elisa Mejía Mejía and Peter H. Charlton 31 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 32 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | -------------------------------------------------------------------------------- /docs/functions/pda_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `PDA_BEAT_DETECTOR` - PDA PPG beat detector. 2 | PDA_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'Peak Detection Algorithm' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | E. J. Arguello Prada and R. D. Serna Maldonado, 'A novel and low-complexity peak detection algorithm for heart rate estimation from low-amplitude photoplethysmographic (PPG) signals,' Journal of Medical Engineering and Technology, vol. 42, no. 8, pp. 569-577, 2018. 17 | 18 | ## Author 19 | + Elisa Mejía Mejía - wrote the code: 'upslopes' 20 | 21 | + Peter H. Charlton - did very little, just wrote this wrapper 22 | 23 | ## Documentation 24 | 25 | 26 | ## Version 27 | 1.0 28 | 29 | ## License - MIT 30 | Copyright (c) 2022 Elisa Mejía Mejía and Peter H. Charlton 31 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 32 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | -------------------------------------------------------------------------------- /docs/functions/align_ppg_ecg_beats.md: -------------------------------------------------------------------------------- 1 | # `ALIGN_PPG_ECG_BEATS` - aligns PPG and ECG beats. 2 | ALIGN_PPG_ECG_BEATS aligns beats detected in an electrocardiogram (ECG) 3 | signal with those detected in a simultaneous photoplethysmogram (PPG) signal. 4 | 5 | ## Inputs 6 | + ppg_beats_inds - indices of PPG beats 7 | 8 | + ecg_beats_inds - indices of ECG beats 9 | 10 | + ppg_fs - sampling frequency of PPG in Hz 11 | 12 | + ecg_fs - sampling frequency of ECG in Hz 13 | 14 | + options - (optional) A structure containing the settings for the analysis: 15 | 16 | - options.max_lag - max permissible lag between ECG and PPG in secs 17 | - options.lag_int - increment between tested lags 18 | - options.tol_window - acceptable tolerance between ECG and PPG beats 19 | - options.do_wins - whether or not to use windowing to find the time lag 20 | + ecg_exc_log - (optional) A logical indicating whether or not each ECG sample will be excluded from the analysis 21 | 22 | ## Outputs 23 | + ecg_beats_a_inds : the aligned indices of ECG beats 24 | 25 | + ecg_exc_a_log : the aligned ECG exclusion logical 26 | 27 | + lag_ecg_samps : the number of samples by which the ECG is in front / behind the PPG 28 | 29 | ## Documentation 30 | 31 | 32 | ## Author 33 | Peter H. Charlton, University of Cambridge, February 2022. 34 | 35 | ## Version 36 | 1.0 37 | 38 | ## License - GPL-3.0 39 | Copyright (c) 2022 Peter H. Charlton 40 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 41 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 42 | You should have received a copy of the GNU General Public License along with this program. If not, see . 43 | -------------------------------------------------------------------------------- /source/beat_detection_algorithms/ecg_beat_detection/rdeco/env_secant.m: -------------------------------------------------------------------------------- 1 | function [env] = env_secant(x_data, y_data, view, side) 2 | % Function call: env_secant(x_data, y_data, view, side) 3 | % Calculates the top envelope of data over . 4 | % Method used: 'secant-method' 5 | % env_secant() observates the max. slope of about points, 6 | % and joints them to the resulting envelope. 7 | % An interpolation over original x-values is done finally. 8 | % ('top' or 'bottom') defines which side to evolve. 9 | % Author: Andreas Martin, Volkswagen AG, Germany 10 | 11 | 12 | side = strcmpi ( {'top','bottom'}, side ) * [ 1 ; -1 ]; 13 | 14 | assert (view > 1, ... 15 | 'Parameter too small!'); 16 | assert (ndims (x_data) == 2, ... 17 | 'Parameter has to be vector type!' ); 18 | assert (size (x_data, 1) == 1 || size (x_data, 2) == 1, ... 19 | 'Parameter has to be vector type (Nx1)!' ); 20 | assert (ndims (y_data) == 2, ... 21 | 'Parameter has to be vector type (Nx1)!' ); 22 | assert (size (y_data, 1) == 1 || size (y_data, 2) == 1, ... 23 | 'Parameter has to be vector type (Nx1)!' ); 24 | assert (length (x_data) == length (y_data), ... 25 | 'Parameters and must have same length!' ); 26 | assert (side ~= 0, ... 27 | 'Parameter must be ''top'' or ''bottom''' ); 28 | 29 | data_len = length (y_data); 30 | x_new = x_data(1); 31 | y_new = y_data(1); 32 | 33 | i = 2; 34 | while i < data_len 35 | m_max = -Inf; % stores maximum slope in forward viewed neighbourhood 36 | for ii = i+1 : min(i+view, data_len) 37 | m = ( y_data(ii) - y_data(i) ) / (ii-i) * side; 38 | % Equidistant x_data assumed! Use next row instead, if not: 39 | % m = ( y_data(ii) - y_data(i) ) / ( x_data(ii) - x_data(i) ); 40 | if m >= m_max 41 | % New max. slope found: store new "observation point" 42 | % always traced when ii==1 43 | m_max = m; 44 | i_op = ii; 45 | end 46 | end 47 | x_new = [ x_new x_data(i_op) ]; 48 | y_new = [ y_new y_data(i_op) ]; 49 | i = i_op; 50 | end 51 | 52 | env = interp1 (x_new, y_new, x_data,'linear', 'extrap'); -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # PPG-beats 2 | 3 | **PPG-beats** is a Matlab library of algorithms to detect heartbeats in photoplethysmogram (PPG) signals. 4 | 5 | ## Background 6 | 7 | Photoplethysmography (PPG) sensors are the optical sensors which are now widely used for oxygen saturation and heart rate monitoring in clinical devices (such as pulse oximeters) and consumer devices (such as smartwatches). A wealth of information can be obtained from PPG signals, including heart rate, heart rhythm, and blood oxygen saturation. A fundamental step when deriving such parameters is the detection of individual heartbeats. Indeed, several algorithms have been developed to detect heartbeats in PPG signals. 8 | 9 | ## Purpose 10 | 11 | This software is designed to provide open-source algorithms for detecting heartbeats in PPG signals, and to provide a framework with which to assess their performance. The software is intended for use in research, and is therefore aimed at academic researchers. 12 | 13 | ## Features 14 | 15 | The key features of the library are: 16 | 17 | 1. **[PPG Beat Detection Algorithms](./toolbox/ppg_beat_detectors)**: A selection of open-source algorithms for detecting beats in PPG signals. 18 | 2. **[Performance Assessment Resources](./toolbox/performance_assessment)**: Resources to assess the performance of PPG beat detectors, including: 19 | - **[Datasets](./datasets/summary)**: several publicly available datasets containing PPG and reference electrocardiogram (ECG) signals. 20 | - **[Code](./toolbox/performance_assessment)**: MATLAB code with which to assess performance. 21 | 3. **[Tutorials](./tutorials/summary)** on how to use the algorithms, datasets, and code. 22 | 23 | ![PPG signal and detected beats](./assets/images/ppg_and_beats.png) 24 | 25 | ## Getting started 26 | 27 | Details of how to get started with the toolbox are available [here](./toolbox/getting_started/). 28 | 29 | ## Further Information 30 | 31 | Further details of the project, including publications produced using this toolbox, are available at the [project website](https://peterhcharlton.github.io/project/ppg-beats/). 32 | 33 | ## Citation 34 | When using this toolbox, please cite: 35 | 36 | Charlton PH _et al._, [Detecting beats in the photoplethysmogram: benchmarking open-source algorithms](https://doi.org/10.1088/1361-6579/ac826d), Physiological Measurement, 2022. 37 | 38 | -------------------------------------------------------------------------------- /docs/datasets/mimic_perform_af.md: -------------------------------------------------------------------------------- 1 | # MIMIC PERform AF Dataset 2 | 3 | --- 4 | 5 | This dataset contains ECG and PPG recordings of 20-minute duration, some of which were acquired during atrial fibrillation (AF), and the rest were acquired during normal sinus rhythm. 6 | 7 | ## Overview 8 | 9 | Item | Details 10 | :--- | :--- 11 | **Links** | [Dataset](https://doi.org/10.5281/zenodo.6807402), [Publication](http://peterhcharlton.github.io/publication/assess_ppg_beat_detectors/) 12 | **Signals** | PPG, ECG, respiration 13 | **No. Subjs** | 35 (19 in AF, 16 not in AF) 14 | **Protocol** | 35 critically-ill adults during routine clinical care. Data were measured using a bedside monitor at 125 Hz. Manual labels of AF and non-AF subjects were obtained from [here](https://doi.org/10.6084/m9.figshare.12149091.v1), as described in [[1](https://doi.org/10.1109/ACCESS.2019.2926199)]. Data were extracted from the MIMIC-III Waveform Database [Matched Subset](https://physionet.org/content/mimic3wdb-matched/1.0/). 15 | 16 | ## Downloading the dataset 17 | 18 | Links to download the dataset in different formats are provided in this table: 19 | 20 | Format | Links 21 | :--- | :--- 22 | Matlab | [AF subjects](https://zenodo.org/record/6807403/files/mimic_perform_af_data.mat?download=1), [non-AF subjects](https://zenodo.org/record/6807403/files/mimic_perform_non_af_data.mat?download=1) 23 | CSV | [AF subjects](https://zenodo.org/record/6807403/files/mimic_perform_af_csv.zip?download=1), [non-AF subjects](https://zenodo.org/record/6807403/files/mimic_perform_non_af_csv.zip?download=1) 24 | WFDB | [AF subjects](https://zenodo.org/record/6807403/files/mimic_perform_af_wfdb.zip?download=1), [non-AF subjects](https://zenodo.org/record/6807403/files/mimic_perform_non_af_wfdb.zip?download=1) 25 | 26 | ## Recreating the dataset 27 | 28 | The dataset can be recreated from scratch by taking the following steps: 29 | 30 | 1. Download the following MATLAB script: [`collate_mimic_perform_af_dataset`](https://ppg-beats.readthedocs.io/en/latest/functions/collate_mimic_perform_af_dataset/). 31 | 2. Modify the MATLAB script by inserting the `up.paths.root_folder` and `up.paths.save_folder` into the `setup_up` function. 32 | - _Note:_ The duration of recordings provided by the script can be adjusted with the `up.settings.req_durn` variable within the `setup_up` function. 33 | 3. Run the MATLAB script to download the required files from PhysioNet, import the data into MATLAB, and collate the data into a single MATLAB data file, ready for analysis. 34 | 35 | -------------------------------------------------------------------------------- /docs/functions/mimic_perform_dataset_converter.md: -------------------------------------------------------------------------------- 1 | # `MIMIC_PERFORM_DATASET_CONVERTER` - converts datasets into CSV and WFDB formats. 2 | MIMIC_PERFORM_DATASET_CONVERTER converts MIMIC PERform datasets from 3 | Matlab format into CSV and WFDB formats. 4 | 5 | ## Inputs 6 | + specify the path of the dataset for conversion in the "up.matlab_dataset.path" variable within "setup_params" below. 7 | 8 | ## Outputs 9 | + files : containing all the data written to new subfolders within the path specified by "up.matlab_dataset.path". 10 | 11 | ## Requirements: 12 | The WFDB Toolbox is required to convert to WFDB format, which can be 13 | downloaded from: 14 | 15 | 16 | ## Further Information 17 | This version is provided to convert the MIMIC PERform datasets originally 18 | described in: 19 | Charlton P.H. et al. Detecting beats in the photoplethysmogram: benchmarking open-source algorithms, Physiological Measurement, 2022. 20 | 21 | Further information on this study can be obtained at: 22 | 23 | 24 | In addition, further information on the ppg-beats toolbox, including 25 | future versions, can be obtained at: 26 | 27 | 28 | ## Feedback 29 | Questions, comments, criticisms, complements, and contributions are welcome at: 30 | 31 | 32 | ## Version 33 | v.1 - published on 1st August 2022 by Peter Charlton 34 | 35 | ## Source 36 | Adapted from: RRest_dataset_converter.m, part of the RRest () repository which is covered by the GNU public licence. 37 | 38 | ## Author 39 | Peter H. Charlton, University of Cambridge, June 2022. 40 | 41 | ## License - GPL-3.0 42 | Copyright (c) 2022 Peter H. Charlton 43 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 44 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 45 | You should have received a copy of the GNU General Public License along with this program. If not, see . 46 | -------------------------------------------------------------------------------- /docs/tutorials/creating_docs.md: -------------------------------------------------------------------------------- 1 | # Creating Documentation 2 | 3 | Creating the **PPG-beats** documentation. 4 | 5 | --- 6 | 7 | ## Create and preview the documentation locally 8 | 9 | Pre-requisites: 10 | 11 | - **MkDocs**: _MkDocs_ is a 'static site generator that's geared towards building project documentation (from [mkdocs.org](https://www.mkdocs.org/)). Installation instructions are available [here](https://www.mkdocs.org/user-guide/installation/). _e.g._ on my Mac, I installed MkDocs using the following command: ``pip install mkdocs`` 12 | 13 | Steps: 14 | 15 | 1. **Download repository:** Download the [GitHub repository](https://github.com/peterhcharlton/ppg-beats) (which contains both the code and documentation files): Use [this link](https://github.com/peterhcharlton/ppg-beats/archive/refs/heads/main.zip) to download a ZIP file. 16 | 2. **Extract:** Extract (unzip) the ZIP file. 17 | 3. **Set current directory:** Go to Command Prompt (on Windows) or Terminal (on MacOS). Set the current directory to the extracted folder. _e.g._ on my Mac, I use: ``cd /Users/petercharlton/Documents/GitHub/ppg-beats/`` 18 | 4. **Preview documentation:** Use the ``mkdocs serve`` command to view the documentation in your browser at [http://127.0.0.1:8000/](http://127.0.0.1:8000/). 19 | 20 | ## Upload the documentation to the web 21 | 22 | Pre-requisites: 23 | 24 | - **GitHub**: A GitHub account (and preferably some experience with GitHub). 25 | - **Read the Docs**: A _Read the Docs_ account (although this can be easily set up, and no experience is required). 26 | 27 | Steps: 28 | 29 | 1. **Upload to GitHub**: Commit the documentation to a GitHub repository. _e.g._ in my case, [peterhcharlton/ppg-beats](https://github.com/peterhcharlton/ppg-beats). Note that the documentation is committed to the main branch. It contains: 30 | - a file named _mkdocs.yml_ in the main folder 31 | - a subfolder named _docs_ which contains markdown documentation 32 | - a subfolder named _source_ which contains the source code 33 | 2. **Host on _Read the Docs_**: Host the documentation on _[Read the Docs](https://readthedocs.org/)_, allowing it to be viewed as a website. _e.g._ the _PPG-beats_ documentation is hosted by _Read the Docs_ [here](https://readthedocs.org/projects/ppg-beats/). The following are helpful for working out how to do this: 34 | - [Read the Docs Tutorial](https://docs.readthedocs.io/en/stable/tutorial/): Provides details of how to host content stored in a GitHub repository. 35 | - [MkDocs 'Deploying your docs' guide](https://mkdocs.readthedocs.io/en/stable/user-guide/deploying-your-docs/) 36 | 37 | -------------------------------------------------------------------------------- /docs/functions/heartpy_beat_detector.md: -------------------------------------------------------------------------------- 1 | # `HEARTPY_BEAT_DETECTOR` - HEARTPY PPG beat detector. 2 | HEARTPY_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 3 | using the 'HeartPy' beat detector 4 | 5 | ## Inputs 6 | + sig : a vector of PPG values 7 | 8 | + fs : the sampling frequency of the PPG in Hz 9 | 10 | ## Outputs 11 | + peaks : indices of detected pulse peaks 12 | 13 | + onsets : indices of detected pulse onsets 14 | 15 | ## Reference 16 | P. van Gent et al., 'HeartPy: A novel heart rate algorithm for the analysis of noisy signals,' Transportation Research Part F: Traffic Psychology and Behaviour, vol. 66, pp. 368-378, 2019. https://doi.org/10.1016/j.trf.2019.09.015> 17 | 18 | ## Author 19 | Peter H. Charlton, University of Cambridge, February 2022. 20 | 21 | ## Documentation 22 | 23 | 24 | ## Version 25 | 1.0 26 | 27 | ## Source 28 | This is a Matlab implementation of the original Python algorithm, described in: 29 | van Gent P. et al., Analysing noisy driver physiology real-time using off-the-shelf sensors: Heart rate analysis software from the taking the fast lane project. J. Open Res. Softw. 2019, 7, 30 | It was created based on v.1.2.5 of HeartPy downloaded on 7-June-2021 from: 31 | NB: The original HeartPy algorithm contains several extra features (such as artifact detection), which are not included in this code. 32 | 33 | ## MIT License 34 | Original: 35 | MIT License 36 | Copyright (c) 2021 Paul van Gent 37 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 38 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 40 | 41 | The code has since been implemented in Matlab format, and modified, by Peter H. Charlton. 42 | -------------------------------------------------------------------------------- /docs/functions/assess_beat_detectors.md: -------------------------------------------------------------------------------- 1 | # `ASSESS_BEAT_DETECTORS` - Assess beat detectors. 2 | ASSESS_BEAT_DETECTORS assesses the performance of multiple 3 | photoplethysmogram (PPG) beat detectors on a dataset. 4 | 5 | ## Inputs 6 | + dataset - The name of the dataset with which to assess performance, e.g. 7 | 8 | 'ppg_dalia_working' 9 | 'ppg_dalia_walking' 10 | 'ppg_dalia_sitting' 11 | 'capnobase' 12 | 'mimic_af' 13 | 'mimic_non_af' 14 | 'wesad_baseline' 15 | You must have downloaded and collated the dataset in order 16 | to use it. Instructions on how to download and collate datasets 17 | are provided here: 18 | 19 | + options - (optional) A structure of options which determine the settings used for the analysis: 20 | 21 | options.dataset_file - a string containing the path of the Matlab file containing the collated dataset. 22 | options.beat_detectors - a cell array containing strings corresponding to the names of the beat detectors to be used. 23 | options.do_downsample - (1 or 0, default value of 0) A logical indicating whether or not to downsample the PPG signals prior to analysis. 24 | options.downsample_freq - the frequency at which to downsample PPG signals (Hz). Only used if downsampling is enabled by options.do_downsample. 25 | options.redo_analysis - A logical indicating whether or not to overwrite existing analysis files in order to redo the analysis 26 | options.redo_selected_beat_detectors - A cell containing the beat detectors for which to redo the analysis 27 | 28 | ## Outputs 29 | + ... 30 | 31 | ## Exemplary usage 32 | assess_beat_detectors('mimic_non_af') % assesses the performance of beat detectors on the 'mimic_non_af' dataset using default analysis options 33 | 34 | ## Documentation 35 | 36 | 37 | ## Author 38 | Peter H. Charlton, University of Cambridge, February 2022. 39 | 40 | ## Version 41 | 1.0 42 | 43 | ## License - GPL-3.0 44 | Copyright (c) 2022 Peter H. Charlton 45 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 46 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 47 | You should have received a copy of the GNU General Public License along with this program. If not, see . 48 | -------------------------------------------------------------------------------- /docs/datasets/mimic_perform_testing.md: -------------------------------------------------------------------------------- 1 | # MIMIC 'PERform' Testing Dataset 2 | 3 | --- 4 | 5 | This dataset contains ECG and PPG recordings of 10-minute duration, some of which were acquired from adults, and some of which were acquired neonates. 6 | 7 | ## Overview 8 | 9 | Item | Details 10 | :--- | :--- 11 | **Links** | [Dataset](https://doi.org/10.5281/zenodo.6807402), [Publication](http://peterhcharlton.github.io/publication/assess_ppg_beat_detectors/) 12 | **Signals** | PPG, ECG, resp 13 | **No. Subjs** | 200 (100 adults, 100 neonates) 14 | **Protocol** | Critically-ill patients during routine clinical care. Data were measured using a bedside monitor at 125 Hz. Data were extracted from the [MIMIC-III Waveform Database](https://physionet.org/content/mimic3wdb/1.0/). 15 | 16 | ## Downloading the dataset 17 | 18 | Links to download the dataset in different formats are provided in this table: 19 | 20 | Format | Links 21 | :--- | :--- 22 | Matlab | [all subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_all_data.mat?download=1), [adult subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_a_data.mat?download=1), [neonate subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_n_data.mat?download=1) 23 | CSV | [all subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_all_csv.zip?download=1), [adult subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_a_csv.zip?download=1), [neonate subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_n_csv.zip?download=1) 24 | WFDB | [all subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_all_wfdb.zip?download=1), [adult subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_a_wfdb.zip?download=1), [neonate subjects](https://zenodo.org/record/6950488/files/mimic_perform_test_n_wfdb.zip?download=1) 25 | 26 | ## Recreating the dataset 27 | 28 | The dataset can be recreated from scratch by taking the following steps: 29 | 30 | 1. Use the following MATLAB script to import the data: [`collate_mimic_perform_train_test_datasets`](/functions/collate_mimic_perform_train_test_datasets/). 31 | 2. Modify the MATLAB script by inserting the `up.paths.root_folder` and `up.paths.save_folder` into the `setup_up` function. 32 | - _Note:_ The duration of recordings provided by the script can be adjusted with the `up.settings.req_durn` variable within the `setup_up` function. 33 | - _Note:_ The number of subjects in each group can be adjusted with the `up.settings.no_subjs_per_group` variable within the `setup_up` function. 34 | 3. Run the MATLAB script to download the required files from PhysioNet, import the data into MATLAB, and collate the data into a single MATLAB data file, ready for analysis. 35 | 4. (optionally) convert the dataset into WFDB and/or CSV formats using this MATLAB script: [`mimic_perform_dataset_converter`](/functions/mimic_perform_dataset_converter/) 36 | 37 | -------------------------------------------------------------------------------- /docs/datasets/mimic_perform_training.md: -------------------------------------------------------------------------------- 1 | # MIMIC PERform Training Dataset 2 | 3 | --- 4 | 5 | This dataset contains ECG and PPG recordings of 10-minute duration, some of which were acquired from adults, and some of which were acquired neonates. 6 | 7 | ## Overview 8 | 9 | Item | Details 10 | :--- | :--- 11 | **Links** | [Dataset](https://doi.org/10.5281/zenodo.6807402), [Publication](http://peterhcharlton.github.io/publication/assess_ppg_beat_detectors/) 12 | **Signals** | PPG, ECG, respiration 13 | **No. Subjs** | 200 (100 adults, 100 neonates) 14 | **Protocol** | Critically-ill patients during routine clinical care. Data were measured using a bedside monitor at 125 Hz. Data were extracted from the [MIMIC-III Waveform Database](https://physionet.org/content/mimic3wdb/1.0/). 15 | 16 | ## Downloading the dataset 17 | 18 | Links to download the dataset in different formats are provided in this table: 19 | 20 | Format | Links 21 | :--- | :--- 22 | Matlab | [all subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_all_data.mat?download=1), [adult subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_a_data.mat?download=1), [neonate subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_n_data.mat?download=1) 23 | CSV | [all subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_all_csv.zip?download=1), [adult subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_a_csv.zip?download=1), [neonate subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_n_csv.zip?download=1) 24 | WFDB | [all subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_all_wfdb.zip?download=1), [adult subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_a_wfdb.zip?download=1), [neonate subjects](https://zenodo.org/record/6950488/files/mimic_perform_train_n_wfdb.zip?download=1) 25 | 26 | ## Recreating the dataset 27 | 28 | The dataset can be recreated from scratch by taking the following steps: 29 | 30 | 1. Use the following MATLAB script to import the data: [`collate_mimic_perform_train_test_datasets`](/functions/collate_mimic_perform_train_test_datasets/). 31 | 2. Modify the MATLAB script by inserting the `up.paths.root_folder` and `up.paths.save_folder` into the `setup_up` function. 32 | - _Note:_ The duration of recordings provided by the script can be adjusted with the `up.settings.req_durn` variable within the `setup_up` function. 33 | - _Note:_ The number of subjects in each group can be adjusted with the `up.settings.no_subjs_per_group` variable within the `setup_up` function. 34 | 3. Run the MATLAB script to download the required files from PhysioNet, import the data into MATLAB, and collate the data into a single MATLAB data file, ready for analysis. 35 | 4. (optionally) convert the dataset into WFDB and/or CSV formats using this MATLAB script: [`mimic_perform_dataset_converter`](/functions/mimic_perform_dataset_converter/) 36 | 37 | -------------------------------------------------------------------------------- /docs/toolbox/ecg_beat_detectors.md: -------------------------------------------------------------------------------- 1 | # ECG Beat Detectors 2 | 3 | Algorithms to detect beats in electrocardiogram (ECG) signals, to act as a reference against which to compare beats detected in photoplethysmogram (PPG) signals. 4 | 5 | --- 6 | 7 | **Overview:** The performance of PPG beat detection algorithms in **PPG-beats** can be assessed by comparing the beat detections against reference beats obtained from simultaneous electrocardiogram (ECG) signals. To facilitate this, **PPG-beats** contains algorithms to detect beats in the ECG, and assess the quality of these beat detections. 8 | 9 | **Accompanying tutorial:** See [this tutorial](./tutorials/ecg_beat_detection) for an example of how to use the ECG beat detectors, which includes an [additional step](./tutorials/ecg_beat_detection/#assessing-the-quality-of-beat-detections) on assessing the quality of beat detections. 10 | 11 | --- 12 | 13 | ## _gqrs_ Beat Detector 14 | 15 | **Original publication:** The methodology used by this beat detector isn't described in full in a publication. However, I've found [this blog post](https://c4science.ch/source/Event_based_gQRS/browse/master/python_gQRS/gQRS-lvlCrossingsampling.ipynb) helpful for understanding how it works. I would also recommend reading [this publication](https://doi.org/10.1088/0967-3334/36/8/1665) and [this assessment of ECG beat detectors](https://ieeexplore.ieee.org/document/7043053). 16 | 17 | **Description:** "QRS matched filter with a custom-built set of heuristics" [[ref](https://doi.org/10.1088/0967-3334/36/8/1665)] 18 | 19 | **Link:** [c-code](https://www.physionet.org/physiotools/wag/gqrs-1.htm), [MATLAB wrapper](https://archive.physionet.org/physiotools/matlab/wfdb-app-matlab/html/gqrs.m) 20 | 21 | 22 | ## _jqrs_ Beat Detector 23 | 24 | **Original publications:** 25 | 26 | 1. J. Behar et al., 'A comparison of single channel fetal ecg extraction methods,' _Ann Biomed Eng_ 2014; 42: 1340-53. DOI: [10.1007/s10439-014-0993-9](https://doi.org/10.1007/s10439-014-0993-9) 27 | 2. A.E.W. Johnson _et al._, 'R-peak estimation using multimodal lead switching,' _Proc CinC._, 2014. p. 281-4. [Link](https://ieeexplore.ieee.org/document/7043034) 28 | 29 | **Description:** "Window-based peak energy detector" [[ref](https://doi.org/10.1088/0967-3334/36/8/1665)] 30 | 31 | **Link:** [run_qrsdet_by_seg.m](https://physionet.org/content/pcst/1.0.0/Tools/ECG_Analysis_Tools/PeakDetection_SQI/run_qrsdet_by_seg.m); [jqrs.m](https://physionet.org/content/pcst/1.0.0/Tools/ECG_Analysis_Tools/PeakDetection_SQI/jqrs.m) 32 | 33 | 34 | ## _rpeakdetect_ Beat Detector 35 | 36 | **Original publication:** _unknown_ 37 | 38 | **Description:** Based on Pan, Hamilton and Tompkins. 39 | 40 | **Link:** [rpeakdetect.m](http://www.mit.edu/~gari/CODE/ECGtools/ecgBag/rpeakdetect.m) 41 | 42 | ## Quality Assessment 43 | 44 | The toolbox can assess the quality of beat detections by comparing the beats detected by two different beat detectors (as described in [[ref](https://doi.org/10.1088/0967-3334/36/8/1665)]). 45 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project lead using the address [here][contact]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project lead is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from [Atom's Code of Conduct](https://github.com/atom/atom/blob/master/CODE_OF_CONDUCT.md) under the MIT License, which in turn was adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] 44 | 45 | [contact]: https://peterhcharlton.github.io/#contact 46 | [homepage]: https://contributor-covenant.org 47 | [version]: https://contributor-covenant.org/version/1/4/ 48 | -------------------------------------------------------------------------------- /docs/functions/detect_ecg_beats.md: -------------------------------------------------------------------------------- 1 | # `DETECT_ECG_BEATS` - detects beats in ECG. 2 | DETECT_ECG_BEATS detects beats in an electrocardiogram (ECG) signal 3 | using two beat detectors, and uses the agreement between beat detectors to 4 | assess the quality of beat detections. 5 | 6 | ## Inputs 7 | + ecg : a vector of ECG values 8 | 9 | + fs : the sampling frequency of the ECG in Hz 10 | 11 | + options : (optional) a structure of options which determine the settings used for the analysis: 12 | 13 | - options.win_durn - The duration of windows over which to perform quality assessment (in secs) 14 | - options.win_overlap - The overlap of windows (in secs) 15 | - options.verbose - A logical indicating whether (1) or not (0) to display text in the Command Window 16 | - options.qrs_tol_window - The acceptable tolerance window around QRS spikes (in secs) 17 | - options.beat_detectors - The ECG beat detectors to be used. Provide a cell containing either 1 or 2 out of: {'gqrs', 'jqrs', 'rpeakdetect'} 18 | - options.hpf - A logical indicating whether (1) or not (0) to high-pass filter the ECG to remove baseline wander 19 | 20 | + no_signal_vector : (optional) a vector of the same length as the ECG vector, which indicates whether (1) or not (0) there was a signal at each sample 21 | 22 | ## Outputs 23 | + agreed_beats_inds : the indices of detected beats which both beat detectors agree on 24 | 25 | + qual : a logical indicating whether (1) or not (0) beats agreed between beat detectors for each ECG sample 26 | 27 | + temporary file - this script creates a temporary file in the current directory 28 | 29 | ## Exemplary usage: 30 | options.win_durn = 20; 31 | [agreed_beats_inds, qual] = detect_ecg_beats(ecg, fs, options) 32 | 33 | ## Requirements: 34 | When using the gqrs beat detector, the Waveform Database Software Package (WFDB) for MATLAB and Octave is required, available from: 35 | 36 | 37 | ## Documentation 38 | 39 | 40 | ## Author 41 | Peter H. Charlton, University of Cambridge, February 2022. 42 | 43 | ## Acknowledgment: 44 | This script uses scripts from the PhysioNet Cardiovascular Signal Toolbox (under the GNU public licence): 45 | 46 | + Scripts: ExportHRVparams.m, run_qrsdet_by_seg.m, jqrs.m 47 | 48 | + Some of the parameters included in this script are taken from InitializeHRVparams.m 49 | 50 | + Link: 51 | 52 | + Reference: Vest et al. 'An open source benchmarked toolbox for cardiovascular waveform and interval analysis'. Physiol Meas, 39, 2018. 53 | 54 | It also uses the 'rpeakdetect.m' script (under the GNU public licence): 55 | 56 | + Link: 57 | 58 | + Author: Gari Clifford 59 | 60 | ## Version 61 | 1.0 62 | 63 | ## License - GPL-3.0 64 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 65 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 66 | You should have received a copy of the GNU General Public License along with this program. If not, see . 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PPG-beats README 2 | 3 | [![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-) 4 | 5 | 6 | **PPG-beats** is a Matlab library of algorithms to detect heartbeats in photoplethysmogram (*PPG*) signals. 7 | 8 | ## Purpose 9 | This software is designed to provide open-source algorithms for detecting heartbeats in PPG signals, and to provide a framework with which to assess their performance. The software is intended for use in research, and is therefore aimed at academic researchers. 10 | 11 | ## Getting Started 12 | To get started, see the accompanying documentation [here](https://ppg-beats.readthedocs.io/en/latest/) (and in particular, the [Getting Started](https://ppg-beats.readthedocs.io/en/latest/toolbox/getting_started/) page). 13 | 14 | ## Further Information 15 | For further information, please see: 16 | - The accompanying [documentation](https://ppg-beats.readthedocs.io/en/latest/) 17 | - The repository [license](LICENSE.md) 18 | - The [project website](https://peterhcharlton.github.io/project/ppg-beats/). 19 | - [Contributing guidelines](CONTRIBUTING.md) and the [Contributor Code of Conduct](CODE_OF_CONDUCT.md). 20 | 21 | ## Citation 22 | When using this toolbox, please cite: 23 | 24 | Charlton PH _et al._, [Detecting beats in the photoplethysmogram: benchmarking open-source algorithms](https://doi.org/10.1088/1361-6579/ac826d), Physiological Measurement, 2022. 25 | 26 | ## Contributors ✨ 27 | 28 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |

Peter H Charlton

💻 🖋 🔣 📖

Elisa Mejía

💻

jbehar

💻
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |

Philip Aston

💻
51 | 52 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! See [here](CONTRIBUTING.md) for details of how to contribute. 53 | -------------------------------------------------------------------------------- /docs/tutorials/ecg_beat_detection.md: -------------------------------------------------------------------------------- 1 | # ECG Beat Detection 2 | 3 | This page provides examples of how to use the toolbox for ECG beat detection. 4 | 5 | ## Detecting beats in an ECG signal 6 | 7 | This tutorial demonstrates how to detect beats in an ECG signal using algorithms in the toolbox. 8 | 9 | - Download a minute of adult data from [here](https://zenodo.org/record/6967256/files/MIMIC_PERform_1_min_normal.mat?download=1). 10 | - Load this data file into Matlab. The data in this file is contained within a structure named _data_, which contains ECG and other signals. The ECG data can be found at _data.ekg_ (with the ECG signal given in two fields: _v_ contains the ECG samples, and _fs_ is the sampling frequency in Hz). 11 | - Use the following Matlab commands to detect beats in the ECG, and then plot the ECG signal and detected beats: 12 | 13 | ```matlab 14 | S = data.ekg; % extract the ECG data 15 | options.win_durn = 20; % assess agreement between beat detectors using 20-second windows 16 | options.beat_detectors = {'rpeakdetect'}; % specify the ECG beat detector to be used 17 | [beat_inds, qual] = detect_ecg_beats(S.v, S.fs, options); % detect beats and assess quality of beat detections 18 | 19 | figure('Position', [20,20,1000,350]) % Setup figure 20 | subplot('Position', [0.05,0.17,0.92,0.82]) 21 | t = [0:length(S.v)-1]/S.fs; % Make time vector 22 | plot(t, S.v, 'b'), hold on, % Plot ECG signal 23 | plot(t(find(~qual)), S.v(~qual), 'k') % Plot low quality segments 24 | plot(t(beat_inds), S.v(beat_inds), 'or'), % Plot detected beats 25 | ftsize = 20; % Tidy up plot 26 | set(gca, 'FontSize', ftsize, 'YTick', [], 'Box', 'off'); 27 | ylabel('ECG', 'FontSize', ftsize), 28 | xlabel('Time (s)', 'FontSize', ftsize) 29 | ``` 30 | 31 | This results in the following detected beats: 32 | ![1-minute ECG signal and detected beats](../assets/images/ecg_1_min_beat_detection.png) 33 | 34 | 35 | ## Assessing the quality of beat detections 36 | 37 | This tutorial demonstrates how to assess the quality of beats detected in an ECG signal using algorithms in the toolbox. 38 | 39 | - Download a minute of noisy data from [here](https://zenodo.org/record/6967256/files/MIMIC_PERform_1_min_noisy.mat?download=1). 40 | - Load this data file into Matlab. The data in this file is contained within a structure named _data_, which contains ECG and other signals. The ECG data can be found at _data.ekg_ (with the ECG signal given in two fields: _v_ contains the ECG samples, and _fs_ is the sampling frequency in Hz). 41 | - Use the following Matlab commands to detect beats in the ECG, and then plot the ECG signal and detected beats: 42 | 43 | ```matlab 44 | S = data.ekg; % extract the ECG data 45 | options.win_durn = 10; % assess agreement between beat detectors using 20-second windows 46 | options.beat_detectors = {'jqrs', 'rpeakdetect'}; % specify the ECG beat detector to be used 47 | [beat_inds, qual] = detect_ecg_beats(S.v, S.fs, options); % detect beats and assess quality of beat detections 48 | 49 | figure('Position', [20,20,1000,350]) % Setup figure 50 | subplot('Position', [0.05,0.17,0.92,0.82]) 51 | t = [0:length(S.v)-1]/S.fs; % Make time vector 52 | plot(t, S.v, 'b'), hold on, % Plot ECG signal 53 | plot(t(find(~qual)), S.v(~qual), 'k') % Plot low quality segments 54 | plot(t(beat_inds), S.v(beat_inds), 'or'), % Plot detected beats 55 | ftsize = 20; % Tidy up plot 56 | set(gca, 'FontSize', ftsize, 'YTick', [], 'Box', 'off'); 57 | ylabel('ECG', 'FontSize', ftsize), 58 | xlabel('Time (s)', 'FontSize', ftsize) 59 | legend({'High quality', 'Low quality', 'Beats'}, 'FontSize', ftsize, 'Location', 'northoutside','NumColumns',3) % Make plot legend 60 | ``` 61 | 62 | This results in the following detected beats, of which those in the blue periods of signal are deemed to be high quality detections (because the beat detectors agreed in these periods): 63 | ![1-minute ECG signal and detected beats and quality](../assets/images/ecg_1_min_beat_detection_noisy.png) -------------------------------------------------------------------------------- /source/beat_detection_algorithms/ecg_beat_detection/rdeco/calc_pos_opt.m: -------------------------------------------------------------------------------- 1 | function [pos_opt] = calc_pos_opt(signal,step,maxmin) 2 | % Calculation of optima 3 | % 4 | % Input: signal signal 5 | % step stepsize according to application 6 | % maxmin for finding maxima: maxmin = 1 7 | % for finding minima: maxmin = -1 8 | % Output: pos_opt positions of the optima 9 | % 10 | % Author(s): Jonathan Moeyersons (Jonathan.Moeyersons@esat.kuleuven.be) 11 | % Sabine Van Huffel (Sabine.Vanhuffel@esat.kuleuven.be) 12 | % Carolina Varon (Carolina.Varon@esat.kuleuven.be) 13 | % 14 | % Version History: 15 | % - 06/05/2019 JM Initial version 16 | % 17 | % Copyright (c) 2019, Jonathan Moeyersons, KULeuven-ESAT-STADIUS 18 | % 19 | % This software is made available for non commercial research purposes only 20 | % under the GNU General Public License. However, notwithstanding any 21 | % provision of the GNU General Public License, this software may not be 22 | % used for commercial purposes without explicit written permission after 23 | % contacting jonathan.moeyersons@esat.kuleuven.be 24 | % 25 | % This program is free software: you can redistribute it and/or modify 26 | % it under the terms of the GNU General Public License as published by 27 | % the Free Software Foundation, either version 3 of the License, or 28 | % (at your option) any later version. 29 | % 30 | % This program is distributed in the hope that it will be useful, 31 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | % GNU General Public License for more details. 34 | % 35 | % You should have received a copy of the GNU General Public License 36 | % along with this program. If not, see . 37 | 38 | % Get the length of the signal 39 | sze = length(signal); 40 | 41 | %% Select the upward slopes 42 | 43 | % Pre-allocate 44 | difference = zeros(sze-1,1); 45 | 46 | for ii = 1:sze-step 47 | % If the amplitude of the selected sample is smaller than the amplitude 48 | % of the selected sample plus the step size 49 | if (signal(ii+step)-signal(ii))*maxmin > 0 50 | 51 | difference(ii,1) = 1; 52 | else 53 | difference(ii,1) = 0; 54 | end 55 | end 56 | 57 | % time = 1:length(signal); 58 | % figure; plot(signal); hold on; scatter(time(difference==1),signal(difference==1)) 59 | 60 | %% Select the optima 61 | 62 | % Pre-allocate 63 | pos_opt = zeros(1,sze-step); 64 | no_opt = 0; 65 | 66 | for ii = 2:sze-step %step/2+1:sze-step 67 | real_opt = 1; 68 | 69 | % If you are at the end of the upwards slope 70 | if difference(ii-1,1) == 1 && difference(ii,1) == 0 71 | count = 1; 72 | 73 | % Check if the upward slope lasts long enough 74 | while real_opt == 1 && count < step && count < ii 75 | if difference(ii-count,1) == 1 76 | real_opt = 1; 77 | else 78 | real_opt = 0; 79 | end 80 | count = count + 1; 81 | end 82 | 83 | % If the upward slope is long enough 84 | if real_opt == 1 && count >= 0.75*step 85 | 86 | % Select the interval containing the peak 87 | interval = ii : ii + step; 88 | 89 | % Select the extremum in that interval 90 | if maxmin == 1 91 | [~,index] = max(signal(interval)); 92 | else 93 | [~,index] = min(signal(interval)); 94 | end 95 | 96 | % Select the position of the peak 97 | no_opt = no_opt + 1; 98 | pos_opt(no_opt) = interval(index); 99 | end 100 | end 101 | end 102 | 103 | pos_opt = pos_opt(1:no_opt); 104 | 105 | % figure 106 | % plot(signal); 107 | % hold on 108 | % plot(pos_opt,signal(pos_opt),'ro') 109 | % title('Optima') -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: PPG-beats 2 | repo_url: https://github.com/peterhcharlton/ppg-beats 3 | edit_uri: '' 4 | site_description: PPG-beats, a Matlab library of algorithms to detect heartbeats in photoplethysmogram (PPG) signals 5 | site_author: Peter H Charlton 6 | copyright: Made available under the CC BY 4.0 license 7 | 8 | nav: 9 | - 'Home': 'index.md' 10 | - 'Toolbox': 11 | - 'Getting Started': './toolbox/getting_started.md' 12 | - 'PPG Beat Detectors': './toolbox/ppg_beat_detectors.md' 13 | - 'ECG Beat Detectors': './toolbox/ecg_beat_detectors.md' 14 | - 'Performance Assessment': './toolbox/performance_assessment.md' 15 | - 'Contributing': './toolbox/contributing.md' 16 | - 'Acknowledgments': './toolbox/acknowledgments.md' 17 | - 'Datasets': 18 | - 'Summary': './datasets/summary.md' 19 | - 'CapnoBase': './datasets/capnobase.md' 20 | - 'BIDMC': './datasets/bidmc.md' 21 | - 'MIMIC PERform Training': './datasets/mimic_perform_training.md' 22 | - 'MIMIC PERform Testing': './datasets/mimic_perform_testing.md' 23 | - 'MIMIC PERform AF': './datasets/mimic_perform_af.md' 24 | - 'MIMIC PERform Ethnicity': './datasets/mimic_perform_ethnicity.md' 25 | - 'PPG-DaLiA': './datasets/ppg_dalia.md' 26 | - 'WESAD': './datasets/wesad.md' 27 | - 'Functions': 28 | - 'detect_ppg_beats': './functions/detect_ppg_beats.md' 29 | - 'detect_ecg_beats': './functions/detect_ecg_beats.md' 30 | - 'assess_beat_detectors': './functions/assess_beat_detectors.md' 31 | - 'assess_multiple_datasets': './functions/assess_multiple_datasets.md' 32 | - 'preprocess_ppg_signal': './functions/preprocess_ppg_signal.md' 33 | - 'align_ppg_ecg_beats': './functions/align_ppg_ecg_beats.md' 34 | - 'abd_beat_detector': './functions/abd_beat_detector.md' 35 | - 'ampd_beat_detector': './functions/ampd_beat_detector.md' 36 | - 'atmax_beat_detector': './functions/atmax_beat_detector.md' 37 | - 'atmin_beat_detector': './functions/atmin_beat_detector.md' 38 | - 'coppg_beat_detector': './functions/coppg_beat_detector.md' 39 | - 'erma_beat_detector': './functions/erma_beat_detector.md' 40 | - 'heartpy_beat_detector': './functions/heartpy_beat_detector.md' 41 | - 'ims_beat_detector': './functions/ims_beat_detector.md' 42 | - 'msptd_beat_detector': './functions/msptd_beat_detector.md' 43 | - 'msptdfastv1_beat_detector': './functions/msptdfastv1_beat_detector.md' 44 | - 'msptdfastv2_beat_detector': './functions/msptdfastv2_beat_detector.md' 45 | - 'pda_beat_detector': './functions/pda_beat_detector.md' 46 | - 'ppgpulses_beat_detector': './functions/ppgpulses_beat_detector.md' 47 | - 'pwd_beat_detector': './functions/pwd_beat_detector.md' 48 | - 'qppg_beat_detector': './functions/qppg_beat_detector.md' 49 | - 'qppgfast_beat_detector': './functions/qppgfast_beat_detector.md' 50 | - 'spar_beat_detector': './functions/spar_beat_detector.md' 51 | - 'swt_beat_detector': './functions/swt_beat_detector.md' 52 | - 'wfd_beat_detector': './functions/wfd_beat_detector.md' 53 | - 'collate_mimic_perform_train_test_datasets': './functions/collate_mimic_perform_train_test_datasets.md' 54 | - 'collate_mimic_perform_af_dataset': './functions/collate_mimic_perform_af_dataset.md' 55 | - 'collate_mimic_perform_ethnicity_dataset': './functions/collate_mimic_perform_ethnicity_dataset.md' 56 | - 'mimic_perform_dataset_converter': './functions/mimic_perform_dataset_converter.md' 57 | - 'export_sample_mimic_perform_data': './functions/export_sample_mimic_perform_data.md' 58 | - 'Tutorials': 59 | - 'Summary': './tutorials/summary.md' 60 | - 'PPG Beat Detection': './tutorials/ppg_beat_detection.md' 61 | - 'Performance Assessment': './tutorials/performance_assessment.md' 62 | - 'ECG Beat Detection': './tutorials/ecg_beat_detection.md' 63 | - 'Designing a Beat Detector': './tutorials/designing_beat_detector.md' 64 | - 'Time Alignment': './tutorials/time_alignment.md' 65 | - 'Creating Documentation': './tutorials/creating_docs.md' 66 | -------------------------------------------------------------------------------- /docs/tutorials/performance_assessment.md: -------------------------------------------------------------------------------- 1 | # Performance Assessment 2 | 3 | The tutorials on this page demonstrate how to assess the performance of PPG beat detectors using publicly available datasets. 4 | 5 | ## Assessing performance on a single dataset 6 | 7 | This tutorial demonstrates how to assess the performance of beat detectors on a single dataset. 8 | 9 | - Install the PPG-beats toolbox by following the instructions [here](https://ppg-beats.readthedocs.io/en/latest/toolbox/getting_started/). 10 | - Download the truncated version of the MIMIC PERform Training Dataset from [here](https://zenodo.org/record/6967256/files/MIMIC_PERform_truncated_train_all_data.mat?download=1). 11 | - Use the `assess_beat_detectors.m` script to run the analysis using the following commands: 12 | ```matlab 13 | options.dataset_file = 'filepath'; % NB: Here, instead of 'filepath' insert the path of the `MIMIC_PERform_truncated_train_all_data.mat` file on your computer, _e.g._ `/Users/petercharlton/Downloads/MIMIC_PERform_truncated_train_all_data.mat` 14 | assess_beat_detectors('MIMIC_PERform_truncated_train_all_data', options) 15 | ``` 16 | 17 | - During this process a new folder will have been created called `proc_data_MIMIC_PERform_truncated_train_all_data`. Within this folder you will find files storing the analysis steps, including the file containing the results: the `ppg_detect_stats.mat` file. Open this file in Matlab, and you will see it contains a variable called `res` (for results). 18 | - Have a look at the table containing the results by typing `res.noQual.txt` at the command window. This provides the median and inter-quartile range for a set of statistics describing the performance of each beat detector (each 'strategy' in this table). 19 | 20 | This tutorial is demonstrated in the following video: 21 | 22 | 23 | 24 | 25 | ## Assessing performance on multiple datasets 26 | 27 | This tutorial demonstrates how to assess the performance of beat detectors across multiple datasets. 28 | 29 | - Install the PPG-beats toolbox by following the instructions [here](https://ppg-beats.readthedocs.io/en/latest/toolbox/getting_started/). 30 | - Download the required datasets: 31 | - [MIMIC PERform Truncated Training dataset](https://zenodo.org/record/6967256/files/MIMIC_PERform_truncated_train_all_data.mat?download=1) 32 | - [MIMIC PERform Truncated Testing dataset](https://zenodo.org/record/6973963/files/MIMIC_PERform_truncated_test_all_data.mat?download=1) 33 | - MIMIC PERform Truncated AF dataset (both [AF subjects](https://zenodo.org/record/6973963/files/MIMIC_PERform_truncated_af_data.mat?download=1) and [non-AF subjects](https://zenodo.org/record/6973963/files/MIMIC_PERform_truncated_non_af_data.mat?download=1)). 34 | - Open the `assess_multiple_datasets.m` script (one of the scripts within the **PPG-beats** toolbox) in Matlab. Edit it as follows: 35 | - Specify the path of the Matlab file for each dataset in the `specify_path_of_dataset_file` function within the `assess_multiple_datasets.m` script. 36 | - Specify the datasets to be analysed. This can be quickly done by enabling the `do_tutorial` option within the `setup_universal_params` function within the `assess_multiple_datasets.m` script. 37 | - Specify the folder in which to save results figures by modifying the `up.paths.plots_root_folder` variable within the `setup_universal_params` function within the `assess_multiple_datasets.m` script. 38 | - Run the `assess_multiple_datasets.m` script to assess the performance of PPG beat detectors across all of the datasets. This will: 39 | - Analyse each dataset in turn 40 | - Generate results (in the command window) and results figures (saved in the specified folder). 41 | 42 | This tutorial is demonstrated in the following video: 43 | 44 | -------------------------------------------------------------------------------- /docs/tutorials/ppg_beat_detection.md: -------------------------------------------------------------------------------- 1 | # PPG Beat Detection 2 | 3 | Getting started with the PPG beat detectors. 4 | 5 | --- 6 | 7 | ## Detecting beats in a PPG signal 8 | 9 | This tutorial demonstrates how to detect beats in a PPG signal using algorithms in the toolbox. 10 | 11 | - Download a minute of [sample data](https://zenodo.org/record/6967256/files/MIMIC_PERform_1_min_normal.mat?download=1). 12 | - Load this data file into Matlab. The data in this file is contained within a structure named _data_, which contains PPG and other signals. The PPG data can be found at _data.ppg_ (with the PPG signal given in two fields: _v_ contains the PPG samples, and _fs_ is the sampling frequency in Hz). 13 | - Use the following Matlab commands to detect beats in the PPG (using the 'IMS' beat detector), and then plot the PPG signal and detected beats: 14 | 15 | ```matlab 16 | S = data.ppg; % extract PPG data 17 | beat_detector = 'IMS'; % Select Incremental-Merge Segmentation beat detector 18 | [peaks, onsets, mid_amps] = detect_ppg_beats(S, beat_detector); % detect beats in PPG 19 | 20 | figure('Position', [20,20,1000,350]) % Setup figure 21 | subplot('Position', [0.05,0.17,0.92,0.82]) 22 | t = [0:length(S.v)-1]/S.fs; % Make time vector 23 | plot(t, S.v, 'b'), hold on, % Plot PPG signal 24 | plot(t(peaks), S.v(peaks), 'or'), % Plot detected beats 25 | ftsize = 20; % Tidy up plot 26 | set(gca, 'FontSize', ftsize, 'YTick', [], 'Box', 'off'); 27 | ylabel('PPG', 'FontSize', ftsize), 28 | xlabel('Time (s)', 'FontSize', ftsize) 29 | ``` 30 | 31 | This results in the following detected beats: 32 | 33 | ![1-minute PPG signal and detected beats](../assets/images/ppg_1_min_beat_detection_normal.png) 34 | 35 | This tutorial is demonstrated in the following video: 36 | 37 | 38 | 39 | ## Detecting beats in different types of signals 40 | 41 | - Download a minute of sample data, choosing from: [normal data](https://zenodo.org/record/6967256/files/MIMIC_PERform_1_min_normal.mat?download=1), [neonatal data](https://zenodo.org/record/6967256/files/MIMIC_PERform_1_min_neonate.mat?download=1), [atrial fibrillation data](https://zenodo.org/record/6967256/files/MIMIC_PERform_1_min_AF.mat?download=1), or [noisy data](https://zenodo.org/record/6967256/files/MIMIC_PERform_1_min_noisy.mat?download=1). 42 | - Load this data file into Matlab. The data in this file is contained within a structure named _data_, which contains PPG and other signals. The PPG data can be found at _data.ppg_ (with the PPG signal given in two fields: _v_ contains the PPG samples, and _fs_ is the sampling frequency in Hz). 43 | - Use the following Matlab commands to detect beats in the PPG (using the 'IMS' beat detector), and then plot the PPG signal and detected beats: 44 | 45 | ```matlab 46 | S = data.ppg; % extract PPG data 47 | beat_detector = 'IMS'; % Select Incremental-Merge Segmentation beat detector 48 | [peaks, onsets, mid_amps] = detect_ppg_beats(S, beat_detector); % detect beats in PPG 49 | 50 | figure('Position', [20,20,1000,350]) % Setup figure 51 | subplot('Position', [0.05,0.17,0.92,0.82]) 52 | t = [0:length(S.v)-1]/S.fs; % Make time vector 53 | plot(t, S.v, 'b'), hold on, % Plot PPG signal 54 | plot(t(peaks), S.v(peaks), 'or'), % Plot detected beats 55 | ftsize = 20; % Tidy up plot 56 | set(gca, 'FontSize', ftsize, 'YTick', [], 'Box', 'off'); 57 | ylabel('PPG', 'FontSize', ftsize), 58 | xlabel('Time (s)', 'FontSize', ftsize) 59 | ``` 60 | 61 | This results in the following detected beats: 62 | 63 | _**Neonatal data**_ 64 | ![1-minute neonatal PPG signal and detected beats](../assets/images/ppg_1_min_beat_detection_neonatal.png) 65 | 66 | _**Atrial fibrillation data**_ 67 | ![1-minute atrial fibrillation PPG signal and detected beats](../assets/images/ppg_1_min_beat_detection_af.png) 68 | 69 | _**Noisy data**_ 70 | ![1-minute noisy PPG signal and detected beats](../assets/images/ppg_1_min_beat_detection_noisy.png) 71 | 72 | -------------------------------------------------------------------------------- /source/beat_detection_algorithms/ecg_beat_detection/run_qrsdet_by_seg.m: -------------------------------------------------------------------------------- 1 | function QRS = run_qrsdet_by_seg(ecg,HRVparams) 2 | % this function is used to run the QRS detector for each window window (non overlapping) 3 | % this is needed because in the case of big artefacts at the begining of 4 | % the record (i.e. where the P&T threshold is computed) it can make the detection fail. 5 | % 6 | % inputs 7 | % ecg: ecg signal 8 | % fs: sampling frequency 9 | % window: size of the window onto which to perform QRS detection (in seconds) 10 | % thres: threshold to be used 11 | % ecgType: 'FECG' or 'MECG' 12 | % 13 | % output 14 | % QRS: QRS location in nb samples (ms) 15 | % 16 | % 17 | % FECG-ESN toolbox, version 1.0 18 | % Released under the GNU General Public License 19 | % 20 | % Copyright (C) 2014 Joachim Behar 21 | % Oxford university, Intelligent Patient Monitoring Group 22 | % joachim.behar@eng.ox.ac.uk 23 | % 24 | % Last updated : 28-01-2014 25 | % 26 | % This program is free software; you can redistribute it and/or modify it 27 | % under the terms of the GNU General Public License as published by the 28 | % Free Software Foundation; either version 2 of the License, or (at your 29 | % option) any later version. 30 | % This program is distributed in the hope that it will be useful, but 31 | % WITHOUT ANY WARRANTY; without even the implied warranty of 32 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 33 | % Public License for more details. 34 | % 35 | % 36 | % 12-07-2017 Modified by Giulia Da Poian to be used with VOSIM HRV Toolbox 37 | % use HRVparsms struct 38 | 39 | 40 | 41 | % == managing inputs 42 | if nargin<2; error('run_qrsdet_by_seg: wrong number of input arguments \n'); end; 43 | 44 | fs = HRVparams.Fs; 45 | window = HRVparams.PeakDetect.windows; 46 | thres = HRVparams.PeakDetect.THRES; 47 | ecgType = HRVparams.PeakDetect.ecgType; 48 | 49 | 50 | % == general 51 | segsizeSamp = window*fs; % convert window into nb of samples 52 | NbSeg = floor(length(ecg)/segsizeSamp); % nb of segments 53 | QRS = []; 54 | start = 1; 55 | stop = segsizeSamp; 56 | signForce = 0; % if we want to force the sign of the peak we are looking for 57 | 58 | % == core function 59 | try 60 | for ch=1:NbSeg 61 | % for each segment perform QRS detection 62 | QRStemp = 0; 63 | 64 | % take +/-1sec around selected subsegment exept for the borders. This 65 | % is in case there is a QRS in between segments -> allows to locate 66 | % them well. 67 | if ch==1 68 | % first subsegment 69 | dTplus = fs; 70 | dTminus = 0; 71 | elseif ch==NbSeg 72 | % last subsegment 73 | dTplus = 0; 74 | dTminus = fs; 75 | else 76 | % any other subsegment 77 | dTplus = fs; 78 | dTminus = fs; 79 | end 80 | 81 | % lowering the threshold in case not enough beats are 82 | % detected. Also changed the refractory period to be different between 83 | % mother and foetus. sign of peaks is determined by the sign on the 84 | % first window and then is forced for the following windows. 85 | if strcmp(ecgType,'FECG') 86 | thresTrans = thres; 87 | while length(QRStemp)<20 && thresTrans>0.1 88 | [QRStemp,signForce] = jqrs(ecg(start-dTminus:stop+dTplus),HRVparams); 89 | thresTrans = thresTrans-0.1; 90 | end 91 | else 92 | [QRStemp,signForce] = jqrs(ecg(start-dTminus:stop+dTplus),HRVparams); 93 | end 94 | 95 | NewQRS = (start-1)-dTminus+QRStemp; 96 | NewQRS(NewQRS>stop) = []; 97 | NewQRS(NewQRS 16 | 17 | 18 | 19 | 20 | 21 | After downloading the toolbox: 22 | 23 | 1. Unzip the ZIP folder 24 | 2. Add the extracted files and folders to the Matlab path, using for instance `addpath(genpath(''))`, where `` is replaced with the path of the extracted files. 25 | 26 | ### (ii) Automatic Installation 27 | 28 | Alternatively, the toolbox can be automatically downloaded and installed by: 29 | 30 | 1. Opening Matlab 31 | 2. Setting the current directory as the one where you want to save the toolbox, _e.g._ 32 | ```cd C:/directoryname/``` 33 | 3. Entering the following commands at the Matlab command window: 34 | 35 | ``` 36 | [old_path]=which('assess_beat_detectors'); if(~isempty(old_path)) rmpath(old_path(1:end-8)); end 37 | toolbox_url='https://github.com/peterhcharlton/ppg-beats/archive/refs/heads/main.zip'; 38 | [filestr,status] = urlwrite(toolbox_url,'main.zip'); 39 | unzip('main.zip'); 40 | cd ppg-beats-main 41 | addpath(genpath(pwd)) 42 | savepath 43 | ``` 44 | _NB: These instructions are adapted from those provided for the WFDB Toolbox [here](https://archive.physionet.org/physiotools/matlab/wfdb-app-matlab/)._ 45 | 46 | ## Detecting beats in the PPG 47 | 48 | The toolbox contains several PPG beat detectors, which are detailed [here](../../toolbox/ppg_beat_detectors/). 49 | 50 | These [PPG Beat Detection Tutorials](../../tutorials/ppg_beat_detection/) provide instructions and code to quickly start detecting beats on sample data. 51 | 52 | ## Assessing the performance of PPG beat detectors 53 | 54 | The toolbox contains tools to assess the performance of PPG beat detectors, which are detailed [here](../../toolbox/performance_assessment/). 55 | 56 | These [Performance Assessment Tutorials](../../tutorials/performance_assessment/) provide instructions and sample data to quickly start using the toolbox to assess the performance of PPG beat detection algorithms. 57 | 58 | ## Matlab Requirements 59 | 60 | The toolbox is run in Matlab, and requires the following add-on: 61 | 62 | - **Signal Processing Toolbox**: This toolbox is used by both the performance assessment code, by several PPG beat detectors, and by an ECG beat detector. Therefore, it is required. If necessary, its use could be avoided if not pre-processing the PPG signal (as it is used several times by `preprocess_ppg_signal`), and by not using those beat detectors which require it. 63 | 64 | In addition, the following add-ons are also required to enable the full functionality of the toolbox: 65 | 66 | - **Statistics and Machine Learning Toolbox**: This toolbox is used by the `assess_multiple_datasets` code, and is only required when using this function to compare results across multiple datasets. It is also used by a couple of PPG beat detectors, although since these only use the `quantile` function from this toolbox, it wouldn't be too tricky to avoid the need for it in these beat detectors. 67 | - **Econometrics Toolbox**: This toolbox is only used by one PPG beat detector. 68 | - **Wavelet Toolbox**: This toolbox is only used by one or two PPG beat detectors. 69 | - **WFDB Toolbox**: The Waveform Database Software Package (WFDB) for MATLAB and Octave is required when using the 'gqrs' ECG beat detector in the `detect_ecg_beats` function (although this beat detector isn't used by default), and when running the scripts to download the MIMIC datasets from scratch (`collate_train_test_datasets`, `collate_ethnicity_dataset`, and `collate_af_dataset`, although these are not required to download and use the publicly available datasets). This package is available from: https://doi.org/10.13026/6zcz-e163 . 70 | 71 | _NB: You can obtain details of which functions use which Matlab toolboxes by running a Dependency Report in Matlab._ 72 | 73 | ## Finding the toolbox online 74 | 75 | The toolbox is hosted by: 76 | 77 | - [GitHub](https://github.com/peterhcharlton/ppg-beats/) 78 | - [Mathworks File Exchange](https://www.mathworks.com/matlabcentral/fileexchange/107644-ppg-beats) 79 | - [Zenodo](https://doi.org/10.5281/zenodo.6037646) -------------------------------------------------------------------------------- /docs/tutorials/designing_beat_detector.md: -------------------------------------------------------------------------------- 1 | # Designing a Beat Detector 2 | 3 | The tutorials on this page demonstrate the processes undertaken to design MSPTDfast, an efficient PPG beat detection algorithm. 4 | 5 | ## Designing MSPTDfast (v.1) 6 | 7 | This tutorial demonstrates the processes undertaken to design MSPTDfast (v.1), which was designed using a single dataset. The publication describing this work is available [here](https://doi.org/10.1101/2024.07.18.24310627). 8 | 9 | - Install the PPG-beats toolbox. _The usual instructions [here](https://ppg-beats.readthedocs.io/en/latest/toolbox/getting_started/) are for downloading the latest version of the toolbox, whereas you will need the [v.2.0](https://github.com/peterhcharlton/ppg-beats/releases/tag/v.2.0) release to replicate the analysis exactly. This can be downloaded from [here](https://github.com/peterhcharlton/ppg-beats/archive/refs/tags/v.2.0.zip)._ 10 | - Download the PPG-DaLiA dataset in Matlab format from [here](https://zenodo.org/records/12793711/files/ppg_dalia_lunch_break_data.mat?download=1). 11 | - Use the `assess_multiple_datasets.m` script to run the analysis. 12 | - During this process a new folder will have been created called `proc_data_ppg_dalia_lunch_break`. Within this folder you will find files storing the analysis steps, including the file containing the results: the `ppg_detect_stats.mat` file. Note down the location of this file. 13 | - Finally, analyse the performance of the different algorithm configuration options by running the [msptdfast_cinc_analysis.m](https://raw.githubusercontent.com/peterhcharlton/ppg-beats/main/source/publication_specific_scripts/msptdfast_cinc_analysis_post_20240701.m) script. 14 | 15 | This tutorial is demonstrated in the following video: 16 | 17 | 18 | 19 | ## Designing MSPTDfast (v.2) 20 | 21 | This tutorial demonstrates the processes undertaken to design MSPTDfast (v.2), which was designed using multiple development datasets, and then benchmarked against state-of-the-art algorithms on multiple testing datasets. The publication describing this work is available [here](https://peterhcharlton.github.io/publication/charlton2024-msptdfast/). 22 | 23 | - Install the PPG-beats toolbox. _The usual instructions [here](https://ppg-beats.readthedocs.io/en/latest/toolbox/getting_started/) are for downloading the latest version of the toolbox, whereas you will need the [v.2.2](https://github.com/peterhcharlton/ppg-beats/releases/tag/v.2.2) release to replicate the analysis exactly. This can be downloaded from [here](https://github.com/peterhcharlton/ppg-beats/archive/refs/tags/v.2.2.zip)._ 24 | - Download the required datasets in Matlab format by following the instructions [here](https://ppg-beats.readthedocs.io/en/latest/datasets/summary/). The required datasets are: CapnoBase, BIDMC, MIMIC PERform Training, MIMIC PERform Testing, MIMIC PERform AF, MIMIC PERform Ethnicity, WESAD, and PPG-DaLiA. 25 | - Use the `assess_multiple_datasets.m` script to run the analysis. Use one of the following options: 26 | - To follow the design process: set `do_msptdfastv2_design` to 1 on lines 633 and 1143 of `assess_multiple_datasets.m`. 27 | - To follow the internal validation process: set `do_msptdfastv2_internal_validation` to 1 on lines 638 and 1149 of `assess_multiple_datasets.m`. 28 | - To follow the benchmarking process: set `do_msptdfastv2_benchmarking` to 1 on lines 643 and 1155 of `assess_multiple_datasets.m`. 29 | - During this process a new folder will have been created called `proc_data_`. Within this folder you will find files storing the analysis steps, including the file containing the results: the `ppg_detect_stats.mat` file. Note down the location of this file for each dataset. 30 | - Then, run further analyses using the following additional scripts (adjusting the file paths in the scripts to provide the paths to the files mentioned above): 31 | - To follow the design analysis: use `msptdfastv2_design.m`. 32 | - To follow the internal validation analysis: use `msptdfastv2_internal_validation.m`. 33 | - To follow the benchmarking analysis: use `msptdfastv2_testing.m`. To generate the results for performance at different SNRs, you will need to set: `uParams.analysis.sig_qual_tools = {'accel'; 'ppgq'};` on line 152 of `assess_beat_detectors.m`. You will also require the [ppg-quality](https://ppg-quality.readthedocs.io/) toolbox. 34 | - To produce the examples of PPG beat detection challenges (shown in Fig. 7 of the publication), use `make_plot_of_ppg_beat_detection_challenges_msptdfastv2.m` -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PPG-beats 2 | 3 | Thanks for your interest in contributing! 4 | 5 | The following guidelines are intended to provide an efficient way to contribute to the project. They are just guidelines, and if you think of better or additional ways to contribute then feel free to propose changes to this document in a pull request. 6 | 7 | ## Contributing to the software 8 | 9 | Contributions to the software are most welcome. Here are some guidelines for common types of contributions. 10 | 11 | ### New PPG beat detection algorithms 12 | 13 | We very much value contributions of new PPG beat detection algorithms. If you are considering contributing a new beat detection algorithm, then please: 14 | 15 | 1. Check to see whether this algorithm is already available in the toolbox by consulting the list of existing beat detectors [here](https://ppg-beats.readthedocs.io/en/latest/toolbox/ppg_beat_detectors/). 16 | 2. Prepare a submission: 17 | 18 | **Code** 19 | 20 | The algorithm should be provided in Matlab format, in a `.m` file, laid out as follows: 21 | 22 | ``` 23 | function [peaks, onsets] = _beat_detector(sig,fs) 24 | 25 | 26 | 27 | 28 | 29 | end 30 | ``` 31 | 32 | Note the following: 33 | 34 | - the main function takes the PPG signal (`sig`, a numerical column vector) and its sampling frequency (`fs`, a numerical variable) as inputs. 35 | - the main function is named "_beat_detector". 36 | - the main function outputs two variables: `peaks` and `onsets`, which are column vectors containing the indices of detected pulse peaks / onsets in the signal. 37 | - additional functions can be used in the same file, and should appear below this main function. 38 | - the documentation in the file should include: (i) a reference to a publication describing the algorithm (if such a publication exists); (ii) the author's name; (iii) details of the license applied to the code, which should be an OSI-approved license (see [here](https://opensource.org/licenses/alphabetical)). The documentation will be automatically parsed to generate a webpage in the docs (e.g. [this webpage](https://ppg-beats.readthedocs.io/en/latest/functions/ampd_beat_detector/)), so please try to follow the documentation used in a template beat detector file ... 39 | - [This file](https://github.com/peterhcharlton/ppg-beats/blob/main/source/ampd_beat_detector.m) could be used as a template. 40 | 41 | **Description** 42 | 43 | Please prepare provide a short description of the algorithm (see [here](https://ppg-beats.readthedocs.io/en/latest/toolbox/ppg_beat_detectors/#automatic-multiscale-based-peak-detection) for an example) to be uploaded to the list of PPG beat detectors in the project documentation ([here](https://ppg-beats.readthedocs.io/en/latest/toolbox/ppg_beat_detectors/)). 44 | 45 | 3. Submit the new beat detector algorithm: 46 | 47 | **Code** 48 | 49 | Submit via a pull request. 50 | 51 | **Description** 52 | 53 | Open a new issue, referring to the submitted pull request, and providing the algorithm description. 54 | 55 | ### Specific improvements 56 | 57 | You may well identify specific ways in which the code could be improved. Please submit revised code via a pull request. 58 | 59 | _Improvements to beat detection algorithms:_ Note that most of the beat detection algorithms in this repository are either implementations of beat detectors which have been described in the literature, or implementations that have been previously provided by algorithm authors. Improvements which are intended to correct errors in these implementations will be considered. Improvements which change the design of the algorithm should instead be submitted following the approach described above for new beat detection algorithms. 60 | 61 | ### New feature suggestions 62 | 63 | Please contribute new feature suggestions by opening a new issue. 64 | 65 | ## Reporting issues or problems with the software 66 | 67 | Please report issues or problems with the software (e.g. bugs) by opening a new issue. 68 | 69 | ## Seeking support 70 | 71 | If you would like support in using the code then please make your request by opening a new issue, and providing details of any problems you have encountered (ideally with an example which reproduces the problem). These will be addressed time-permitting. 72 | 73 | ## Improving the documentation 74 | 75 | The documentation is written in markdown and stored in the [docs](https://github.com/peterhcharlton/ppg-beats/tree/main/docs) folder. Please submit improvements via a pull request. 76 | 77 | _functions documentation_: Please note that all documentation in the [functions folder](https://github.com/peterhcharlton/ppg-beats/tree/main/docs/functions) is automatically generated from the comments within the matlab files in the [source folder](https://github.com/peterhcharlton/ppg-beats/tree/main/source). Therefore, any improvements to the functions documentation should be made by adjusting the matlab source files, rather than the automatically generated functions documentation. 78 | 79 | --- 80 | 81 | **Acknowledgment:** _This document is inspired in part by the [guidelines for contributing to Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md)._ 82 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Code in the PPG-beats repository is licensed under the following licenses: 4 | - GNU General Public License version 3 (GPL-3.0) 5 | - GNU General Public License version 2 (GPL-2.0) 6 | - MIT License (MIT) 7 | - 2-clause BSD License 8 | - 3-clause BSD License 9 | 10 | Please see each individual file for the license applied to that file. 11 | 12 | --- 13 | 14 | # GNU General Public License version 3 (GPL-3.0) 15 | 16 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 17 | 18 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License along with this program. If not, see . 21 | 22 | --- 23 | 24 | # GNU General Public License version 2 (GPL-2.0) 25 | 26 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 27 | 28 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 31 | 32 | --- 33 | 34 | # MIT License (MIT) 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 39 | 40 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 41 | 42 | --- 43 | 44 | # 2-clause BSD License 45 | 46 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 47 | 48 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 49 | 50 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 51 | 52 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | 54 | --- 55 | 56 | # 3-clause BSD License 57 | 58 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 59 | 60 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 61 | 62 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 63 | 64 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 65 | 66 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 67 | -------------------------------------------------------------------------------- /source/ampd_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = ampd_beat_detector(sig,fs) 2 | % AMPD_BEAT_DETECTOR AMPD PPG beat detector. 3 | % AMPD_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 4 | % using the 'Automatic Multiscale-based Peak Detection' beat detector 5 | % 6 | % # Inputs 7 | % 8 | % * sig : a vector of PPG values 9 | % * fs : the sampling frequency of the PPG in Hz 10 | % * lpf_freq : (optional) the frequency (in Hz) of low-pass filtering applied to the PPG 11 | % 12 | % # Outputs 13 | % * peaks : indices of detected pulse peaks 14 | % * onsets : indices of detected pulse onsets 15 | % 16 | % # Reference 17 | % F. Scholkmann et al., 'An efficient algorithm for automatic peak detection in noisy periodic and quasi-periodic signals,' Algorithms, vol. 5, no. 4, pp. 588-603, 2012. 18 | % 19 | % # Author 20 | % Peter H. Charlton, University of Cambridge, February 2022. 21 | % 22 | % # Documentation 23 | % 24 | % 25 | % # Version 26 | % 1.0 27 | % 28 | % # License - MIT 29 | % Copyright (c) 2022 Peter H. Charlton 30 | % Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 31 | % The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 32 | % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | 34 | %% Window signal 35 | % split into overlapping 6 s windows 36 | win_len = 6; % in secs 37 | overlap = 0.2; % proportion of overlap between consecutive windows 38 | no_samps_in_win = win_len*fs; 39 | if length(sig) <= no_samps_in_win 40 | win_starts = 1; 41 | win_ends = length(sig); 42 | else 43 | win_offset = round(no_samps_in_win*(1-overlap)); 44 | win_starts = 1:win_offset:length(sig)-no_samps_in_win; 45 | win_ends = win_starts + no_samps_in_win; 46 | if win_ends(end) < length(sig) 47 | win_starts(end+1) = length(sig) - no_samps_in_win; 48 | win_ends(end+1) = length(sig); 49 | end % this ensures that the windows include the entire signal duration 50 | end 51 | 52 | %% Downsample signal 53 | 54 | % Set up downsampling if the sampling frequency is particularly high 55 | do_ds = 0; 56 | if do_ds 57 | min_fs = 2*10; 58 | if fs > min_fs 59 | ds_factor = floor(fs/min_fs); 60 | ds_fs = fs/floor(fs/min_fs); 61 | else 62 | do_ds = 0; 63 | end 64 | end 65 | 66 | %% detect peaks in each window 67 | 68 | peaks = []; 69 | for win_no = 1 : length(win_starts) 70 | 71 | % - extract this window's data 72 | win_sig = sig(win_starts(win_no):win_ends(win_no)); 73 | 74 | % - downsample signal 75 | if do_ds 76 | rel_sig = downsample(win_sig, ds_factor); 77 | else 78 | rel_sig = win_sig; 79 | end 80 | 81 | % - detect peaks 82 | p = detect_peaks_using_ampd(win_sig); 83 | 84 | % - resample peaks 85 | if do_ds 86 | p = p*ds_factor; 87 | end 88 | 89 | % - store peaks 90 | win_peaks = p + win_starts(win_no) -1; 91 | peaks = [peaks; win_peaks]; 92 | end 93 | 94 | % tidy up detected peaks and onsets (by ordering them and only retaining unique ones) 95 | peaks = unique(peaks(:)); 96 | 97 | %% correct peak indices 98 | 99 | % (i) peaks always seem to be one index too high 100 | peaks = peaks-1; 101 | 102 | % (ii) to account for downsampling 103 | if do_ds 104 | peaks = peaks*ds_factor; 105 | % find highest point within tolerance either side of detected peaks 106 | for pk_no = 1 : length(peaks) 107 | curr_peak = peaks(pk_no); 108 | tol_start = curr_peak - ds_factor; 109 | tol_end = curr_peak + ds_factor; 110 | [~, temp] = max(sig(tol_start:tol_end)); 111 | peaks(pk_no) = curr_peak - ds_factor + temp; 112 | clear temp curr_peak tol_start tol_end 113 | 114 | end 115 | end 116 | 117 | % (iii) tidy up 118 | peaks = tidy_beats(peaks); 119 | 120 | %% calculate onset indices 121 | onsets = pulse_onsets_from_peaks(sig, peaks); 122 | 123 | %% check 124 | do_check = 0; 125 | if do_check 126 | plot(sig), hold on, 127 | plot(peaks, sig(peaks), '*r') 128 | plot(onsets, sig(onsets), '*k') 129 | close all 130 | end 131 | 132 | end 133 | 134 | function p = detect_peaks_using_ampd(x) 135 | N = length(x); % length of signal 136 | L = ceil(N/2)-1; % max window length 137 | alpha = 1; % constant factor 138 | 139 | % Step 1: calculate local maxima scalogram (LMS) 140 | 141 | % - detrend 142 | x = detrend(x); % this removes the best-fit straight line 143 | 144 | % - initialise LMS matrix 145 | m = alpha + rand(L,N); % rand is uniform across interval (0,1) 146 | 147 | % - populate LMS matrix 148 | for k = 1:L % local maxima scalogram scales 149 | for i = (k+2):(N-k+1) 150 | if x(i-1) > x(i-k-1) && x(i-1) > x(i+k-1) 151 | m(k,i) = 0; 152 | end 153 | end 154 | end 155 | 156 | % Step 2: find the scale with the most local maxima 157 | % - row-wise summation (i.e. sum each row) 158 | gamma = sum(m,2); % the ",2" option makes it row-wise 159 | % - find scale with the most local maxima (which is the lowest value of gamma as maxima are set to zero). 160 | [~, lambda] = min(gamma); 161 | 162 | % Step 3: Use lambda to remove all elements of m for which k>lambda 163 | m = m(1:lambda,:); 164 | 165 | % Step 4: Find peaks 166 | sigma = std(m); % column-wise standard deviation 167 | p = find(sigma==0); 168 | p = p(:); 169 | 170 | end 171 | -------------------------------------------------------------------------------- /source/coppg_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = coppg_beat_detector(sig, fs) 2 | % COPPG_BEAT_DETECTOR COPPG PPG beat detector. 3 | % COPPG_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 4 | % using the 'Percentile Peak Detector' beat detector 5 | % 6 | % # Inputs 7 | % 8 | % * sig : a vector of PPG values 9 | % * fs : the sampling frequency of the PPG in Hz 10 | % 11 | % # Outputs 12 | % * peaks : indices of detected pulse peaks 13 | % * onsets : indices of detected pulse onsets 14 | % 15 | % # Reference 16 | % C. Orphanidou et al., 'Signal-quality indices for the electrocardiogram and photoplethysmogram: derivation and applications to wireless monitoring,' IEEE Journal of Biomedical and Health Informatics, vol. 19, no. 3, pp. 832-8, 2015. 17 | % 18 | % # Author 19 | % Adapted by Peter Charlton from code supplied by Christina Orphanidou. With grateful thanks to Christina Orphanidou and Alexander Darrell. 20 | % 21 | % # Documentation 22 | % 23 | % 24 | % # Version 25 | % 1.0 26 | % 27 | % # Source 28 | % * Original source: , part of the RRest () repository which is covered by the GNU public licence. 29 | % * Modifications from original by PC: (i) Removed pre-processing; (ii) Hard-coded constants 30 | % 31 | % # License - GPL-3.0 32 | % This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 33 | % This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 34 | % You should have received a copy of the GNU General Public License along with this program. If not, see . 35 | 36 | %% Constants 37 | % Hard-coded by PC 38 | up.paramSet.CO_peak_det.upctl = 0.9; 39 | up.paramSet.CO_peak_det.lpctl = 0.1; 40 | 41 | %% Pre-processing 42 | % removed by PC (see original for details) 43 | ppgfilt.v = sig; 44 | ppgfilt.t = [0:length(ppgfilt.v)-1]./fs; 45 | 46 | %% Setup 47 | % Segmentation into 10s windows 48 | win_length = 10; 49 | overlap = 3; 50 | win_starts = ppgfilt.t(1):win_length:ppgfilt.t(end); 51 | win_ends = win_starts + win_length + overlap; 52 | win_ends(end) = win_ends(end) - overlap; 53 | % setup variables 54 | [overall_peaks, overall_onsets] = deal([]); 55 | 56 | %% Perform peak detection on each 10s window 57 | for win_no = 1 : length(win_starts) 58 | 59 | rel_data = struct; 60 | 61 | rel_data.i = find(ppgfilt.t >= win_starts(win_no) & ppgfilt.t <= win_ends(win_no)); 62 | rel_data.t = ppgfilt.t(rel_data.i); 63 | rel_data.v = ppgfilt.v(rel_data.i); 64 | 65 | %% Calculate thresholds 66 | thresh1=quantile(rel_data.v,up.paramSet.CO_peak_det.upctl); 67 | thresh2=quantile(rel_data.v,up.paramSet.CO_peak_det.lpctl); 68 | thresh3=thresh2+0.3*(thresh1-thresh2); 69 | thresh4=thresh2+0.7*(thresh1-thresh2); 70 | 71 | %% Find all peaks 72 | % identify peaks 73 | diffs_on_left_of_pt = diff(rel_data.v); diffs_on_left_of_pt = diffs_on_left_of_pt(1:(end-1)); diffs_on_left_of_pt = logical(diffs_on_left_of_pt>0); 74 | diffs_on_right_of_pt = diff(rel_data.v); diffs_on_right_of_pt = diffs_on_right_of_pt(2:end); level_on_right_of_pt = logical(diffs_on_right_of_pt==0); diffs_on_right_of_pt = logical(diffs_on_right_of_pt<0); 75 | diffs_on_second_right_of_pt = [1; diff(rel_data.v(2:end))]; diffs_on_second_right_of_pt = diffs_on_second_right_of_pt(2:end); diffs_on_second_right_of_pt = logical(diffs_on_second_right_of_pt<0); 76 | peaks.i = find((diffs_on_left_of_pt & diffs_on_right_of_pt) | ... 77 | (diffs_on_left_of_pt & level_on_right_of_pt & diffs_on_second_right_of_pt))+1; 78 | peaks.i = peaks.i'; 79 | % Take maximum value in window if no peaks were found 80 | if isempty(peaks.i) 81 | peaks.i = find(max(rel_data.v) == rel_data.v); 82 | end 83 | % Extract signal values at peaks 84 | peaks.t = rel_data.t(peaks.i); 85 | peaks.v = rel_data.v(peaks.i); 86 | 87 | %% Classify peaks 88 | % Identify relevant peaks according to amplitude 89 | upperdiff = abs(peaks.v-thresh1); 90 | middlehighdiff = abs(peaks.v-thresh4); 91 | middlelowdiff = abs(peaks.v-thresh3); 92 | lowerdiff = abs(peaks.v-thresh2); 93 | upper_pks = find(upperdiff=fs/3)+1; 99 | PPG_PKS.i = PPG_PKS.i(good_els); 100 | PPG_PKS.v = PPG_PKS.v(good_els); 101 | PPG_PKS.t = PPG_PKS.t(good_els); 102 | 103 | %% Find troughs 104 | 105 | PPG_TRS.i = nan(length(PPG_PKS.t)-1,1); 106 | for s = 1 : (length(PPG_PKS.t)-1) 107 | start_el = PPG_PKS.i(s); 108 | [~, additional_el] = min(rel_data.v(PPG_PKS.i(s):PPG_PKS.i(s+1))); 109 | PPG_TRS.i(s) = start_el - 1 + additional_el; 110 | end 111 | PPG_TRS.v = rel_data.v(PPG_TRS.i); 112 | PPG_TRS.t = rel_data.t(PPG_TRS.i); 113 | 114 | temp_pk_is = rel_data.i(PPG_PKS.i(:)); 115 | temp_on_is = rel_data.i(PPG_TRS.i(:)); 116 | 117 | overall_peaks = [overall_peaks; temp_pk_is(:)]; overall_peaks = unique(overall_peaks); 118 | overall_onsets = [overall_onsets; temp_on_is(:)]; overall_onsets = unique(overall_onsets); 119 | 120 | clear upper lower middlehigh middlelow temp_pk_is temp_on_is PPG_TRS PPG_PKS additional_el s good_els peaks lower_pks upper_pks lowerdiff middlelowdiff middlehighdiff upperdiff ind 121 | 122 | end 123 | 124 | % store overall peaks and onsets 125 | peaks = overall_peaks; 126 | onsets = overall_onsets; 127 | 128 | end 129 | -------------------------------------------------------------------------------- /source/figures/create_ppg_beat_detection_fig.m: -------------------------------------------------------------------------------- 1 | function create_ppg_beat_detection_fig(data) 2 | 3 | close all 4 | 5 | % load data 6 | filepath = '/Users/petercharlton/Documents/Data/mimiciii_ppg_neonate_adult_beat_detection/mimic_train_a_data.mat'; 7 | if nargin==0 8 | load(filepath); 9 | end 10 | 11 | % create assessment framework plot 12 | create_assessment_framework_plot(data); 13 | 14 | % select signal of interest 15 | subj_no = 3; 16 | durn = 10; 17 | buffer = 5; 18 | S_orig = data(subj_no).ppg; 19 | rel_els = 1:(1+durn*S_orig.fs+2*buffer*S_orig.fs); 20 | rel_els2 = rel_els + 1000; 21 | rel_els1 = rel_els + 49800; 22 | 23 | % make figure 24 | figure('Position', [20,20,500,400]) 25 | 26 | % cycle through each sample 27 | for sample_no = 1 :2 28 | 29 | eval(['curr_rel_els = rel_els' num2str(sample_no) ';']); 30 | S.v = S_orig.v(curr_rel_els); 31 | S.fs = S_orig.fs; 32 | 33 | %% - filter signal 34 | 35 | % - Fiducial Points: eliminating high freqs 36 | uParams.analysis.filtering.elim_high_freqs.Fpass = 10; % in HZ 37 | uParams.analysis.filtering.elim_high_freqs.Fstop = 6.93; % in HZ % 6.93 - 10 gives -3dB cutoff of 8.00 Hz 38 | uParams.analysis.filtering.elim_high_freqs.Dpass = 0.05; 39 | uParams.analysis.filtering.elim_high_freqs.Dstop = 0.01; 40 | 41 | % - Fiducial Points: eliminating low freqs 42 | uParams.analysis.filtering.elim_low_freqs.Fpass = 1.02; % in Hz 43 | uParams.analysis.filtering.elim_low_freqs.Fstop = 0.6; % in Hz % 0.6 - 1.02 gives -3dB cutoff of 40 bpm (i.e. 0.67 Hz) 44 | uParams.analysis.filtering.elim_low_freqs.Dpass = 0.05; 45 | uParams.analysis.filtering.elim_low_freqs.Dstop = 0.01; 46 | 47 | % - setup PulseAnalyse options for filtering 48 | options.elim_high_freqs = uParams.analysis.filtering.elim_high_freqs; 49 | options.elim_low_freqs = uParams.analysis.filtering.elim_low_freqs; 50 | options.calc_pw_inds = 0; 51 | options.do_plot = 0; 52 | options.do_beats = 0; 53 | options.do_quality = 0; 54 | options.close_figures = 0; 55 | 56 | % Downsample and filter PPG signal 57 | [~, ~, ~, sigs] = PulseAnalyse(S, options); 58 | S.v = sigs.filt; 59 | S.fs = sigs.fs; 60 | clear options 61 | 62 | % specify beat detectors 63 | beat_detectors = {'MSPTD', 'Pulses'}; 64 | 65 | % detect beats 66 | for beat_detector_no = 1 : length(beat_detectors) 67 | curr_detector = beat_detectors{beat_detector_no}; 68 | 69 | %% - beat detection 70 | 71 | % - setup PulseAnalyse options for beat detection 72 | options.calc_pw_inds = 0; 73 | options.do_plot = 0; 74 | options.do_beat_filter = 0; 75 | options.do_filter = 0; 76 | options.do_quality = 0; 77 | options.close_figures = 0; 78 | options.beat_detector = curr_detector; 79 | 80 | % - detect beats in this subject's PPG signals using this peak detector 81 | [~, ~, pulses, ~] = PulseAnalyse(S, options); 82 | peaks{beat_detector_no,1} = pulses.peaks; 83 | clear options pulses 84 | 85 | end 86 | 87 | %% create plot 88 | 89 | % settings 90 | ftsize = 16; 91 | markers = {'+', 'o'}; 92 | marker_sizes = [12,8]; 93 | colors = {'k', 'r'}; 94 | 95 | % make time vector 96 | t = [0:length(S.v)-1]/S.fs; t = t-5; 97 | 98 | % plot PPG 99 | if sample_no == 1 100 | subplot('Position', [0.08,0.56, 0.89, 0.43]) 101 | else 102 | subplot('Position', [0.08,0.13, 0.89, 0.35]) 103 | end 104 | plot(t, S.v, 'Color', 0.2*ones(1,3), 'LineWidth', 2) 105 | hold on 106 | 107 | % plot detected beats 108 | for detector_no = 1 : length(peaks) 109 | curr_peaks = peaks{detector_no}; 110 | curr_marker = markers{detector_no}; 111 | curr_marker_size = marker_sizes(detector_no); 112 | curr_color = colors{detector_no}; 113 | h(detector_no) = plot(t(curr_peaks), S.v(curr_peaks), curr_marker, 'LineWidth', 1.5, 'Color', curr_color, 'MarkerSize', curr_marker_size); 114 | end 115 | 116 | % tidy up 117 | if sample_no == 2 118 | xlabel('Time (s)', 'FontSize', ftsize) 119 | end 120 | ylabel('PPG (au)', 'FontSize', ftsize) 121 | set(gca, 'YTick', [], 'FontSize', ftsize, 'XGrid', 'on') 122 | box off 123 | xlim([0 10]) 124 | 125 | % legend 126 | if sample_no == 1 127 | legend(h, {'Beat detector 1', 'Beat detector 2'}, 'Location', 'northoutside', 'Orientation', 'horizontal') 128 | end 129 | 130 | end 131 | 132 | % annotate 133 | dim = [.0 .84 .1 .1]; 134 | str = '(a)'; 135 | annotation('textbox',dim,'String',str,'FitBoxToText','on', 'LineStyle', 'none', 'FontSize', ftsize); 136 | dim = [.0 .4 .1 .1]; 137 | str = '(b)'; 138 | annotation('textbox',dim,'String',str,'FitBoxToText','on', 'LineStyle', 'none', 'FontSize', ftsize); 139 | 140 | % save figure 141 | filepath = '/Users/petercharlton/Google Drive/Work/Images/PPG beat detection/beat_detection'; 142 | save_fig(filepath) 143 | 144 | 145 | end 146 | 147 | function save_fig(filepath) 148 | 149 | print(gcf, filepath, '-depsc') 150 | fid = fopen([filepath, '.txt'], 'w'); 151 | fprintf(fid, ['Created using ' mfilename, ', ', date]); 152 | fclose(fid); 153 | 154 | end 155 | 156 | function create_assessment_framework_plot(data) 157 | 158 | % make figure 159 | figure('Position', [20,20,500,400]) 160 | subplot('Position', [0.01,0.01,0.98,0.98]) 161 | lwidth = 2; 162 | 163 | % set up 164 | no_sigs = 10; 165 | no_els = data(1).ppg.fs*5; 166 | scale_vector = linspace(1, 0.2, no_els); 167 | offset_vector = linspace(0,2, no_els); 168 | subj_nos = [19,20,22,25,26, 29:32,34]; 169 | start_el = 125*5*60; 170 | rel_els = start_el:start_el+no_els-1; 171 | 172 | % extract data for each signal 173 | for s = 1 : no_sigs 174 | rel_data(s).v = data(subj_nos(s)).ppg.v(rel_els); 175 | rel_data(s).v = (rel_data(s).v-min(rel_data(s).v))./range(rel_data(s).v); 176 | rel_data(s).v = rel_data(s).v(:) .* scale_vector(:) + offset_vector(:); 177 | end 178 | 179 | % plot data 180 | for s = 1 : no_sigs 181 | plot(s+rel_data(s).v, 'b', 'LineWidth', lwidth); 182 | hold on 183 | end 184 | 185 | % tidy up 186 | box off 187 | xlim([0 no_els]) 188 | ylim([1 no_sigs+2.2]) 189 | set(gca, 'XTick', [], 'YTick', [], 'visible', 'off') 190 | 191 | % save figure 192 | filepath = '/Users/petercharlton/Google Drive/Work/Images/PPG beat detection/assessment_framework'; 193 | save_fig(filepath) 194 | close all 195 | 196 | end -------------------------------------------------------------------------------- /source/wepd_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = wepd_beat_detector(sig,fs) 2 | % WEPD_BEAT_DETECTOR WEPD PPG beat detector. 3 | % WEPD_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 4 | % using the 'waveform envelope peak detection' (WEPD) beat detector 5 | % 6 | % # Inputs 7 | % 8 | % * sig : a vector of PPG values 9 | % * fs : the sampling frequency of the PPG in Hz 10 | % 11 | % # Outputs 12 | % * peaks : indices of detected pulse peaks 13 | % * onsets : indices of detected pulse onsets 14 | % 15 | % # Reference 16 | % D. Han et al., 'A Real-Time PPG Peak Detection Method for Accurate Determination of Heart Rate during Sinus Rhythm and Cardiac Arrhythmia,' Biosensors, vol. 12, no. 2, p. 82, 2022. 17 | % 18 | % # Author 19 | % * Peter H. Charlton 20 | % 21 | % # Documentation 22 | % 23 | % 24 | % # Version 25 | % 1.0 26 | % 27 | % # MIT License 28 | % Copyright (c) 2022 Peter H. Charlton 29 | % Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 30 | % The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 31 | % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | 33 | beat_indices_all = detect_beats(sig, fs); 34 | 35 | % locate peaks because the timings of onsets aren't always exact 36 | onsets = nan(length(beat_indices_all)-1,1); 37 | for beat_no = 1 : length(beat_indices_all)-1 38 | curr_ind = beat_indices_all(beat_no); 39 | [~, temp] = min(sig(curr_ind:beat_indices_all(beat_no+1))); 40 | onsets(beat_no) = curr_ind+temp-1; 41 | end 42 | 43 | onsets = tidy_beats(onsets); 44 | 45 | peaks = pulse_peaks_from_onsets(sig, onsets); 46 | 47 | end 48 | 49 | function onsets = detect_beats(sig, fs) 50 | % this function detects beats in sig using the WEPD method, as described in Han et al., 2022. 51 | 52 | %% Setup 53 | plausible_hrs = [30, 200]; 54 | 55 | %% Bandpass filter 56 | % bandpass filter from 0.5 - 5 Hz using 5th order zero phase elliptic filter 57 | ftype = 'bandpass'; 58 | n = 3; 59 | Rp = 10; 60 | Rs = 50; 61 | Wp = [0.5, 5]/(fs/2); 62 | [b,a] = ellip(n,Rp,Rs,Wp,ftype); 63 | bpf = filtfilt(b,a,sig); 64 | %freqz(b,a) 65 | 66 | %% Moving average filtering and differentiation 67 | % Moving average filter three times 68 | 69 | % setup 70 | a = bpf; 71 | M = round(fs/10); 72 | % first moving average 73 | b = mov_avg(a, M); 74 | M = round(fs/9); 75 | % second moving average 76 | b = mov_avg(b, M); 77 | % calculate first-order difference of filtered signal 78 | c = nan(size(sig)); 79 | c(1:end-1) = b(2:end) - b(1:end-1); 80 | % third moving average 81 | b = mov_avg(b, M); 82 | 83 | %% Normalize signal 84 | non_nans = ~isnan(c); 85 | d = (c - mean(c(non_nans)))/std(c(non_nans)); 86 | 87 | %% Invert signal 88 | % find minima and maxima 89 | d_min = find_minima(d); 90 | d_max = find_minima(-1*d); 91 | % calculate HRs from each 92 | d_min_hr = find_hr(d_min, fs); 93 | d_max_hr = find_hr(d_max, fs); 94 | % decide whether to use minima or maxima 95 | [can_use_min, can_use_max] = deal(true); 96 | % - check plausibility of HRs 97 | if d_min_hr > plausible_hrs(2) || d_min_hr < plausible_hrs(1) 98 | can_use_min = false; 99 | end 100 | if d_max_hr > plausible_hrs(2) || d_max_hr < plausible_hrs(1) 101 | can_use_max = false; 102 | end 103 | % - see if one has significantly fewer beats 104 | diff_hr = d_min_hr - d_max_hr; 105 | if abs(diff_hr) > 10 106 | if d_min_hr < d_max_hr 107 | can_use_max = false; 108 | else 109 | can_use_min = false; 110 | end 111 | end 112 | % - choose the one with the sharper peaks 113 | if can_use_max && can_use_min 114 | min_sharpness = mean([d(d_min-1)-d(d_min); d(d_min+1)-d(d_min)]); 115 | max_sharpness = mean([d(d_max)-d(d_max-1); d(d_min)-d(d_min+1)]); 116 | if min_sharpness > max_sharpness 117 | can_use_max = false; 118 | else 119 | can_use_min = false; 120 | end 121 | end 122 | 123 | %% Local minima 124 | if can_use_max 125 | min_els = d_max; 126 | inv_log = true; 127 | else 128 | min_els = d_min; 129 | inv_log = false; 130 | end 131 | 132 | % invert if required 133 | if inv_log 134 | d = -1*d; 135 | end 136 | 137 | 138 | %% Use envelope to remove false positive beats 139 | 140 | % cubic spline interpolation to find envelope 141 | env1 = interp1(min_els, d(min_els), 1:length(d), 'spline'); 142 | env1 = env1(:); 143 | 144 | % hilbert filter to find envelope 145 | first_non_nan = find(~isnan(d),1); 146 | d(1:first_non_nan-1) = d(first_non_nan); 147 | last_non_nan = find(~isnan(d),1,'last'); 148 | d(last_non_nan+1:end) = d(last_non_nan); 149 | fl = round(1.5*fs); 150 | [~,env2] = envelope(d,fl,'analytic'); 151 | 152 | % find beats 153 | res = range(d)/50; % this step is done because env2 doesn't quite line-up on the PPG signal 154 | d_temp = d - rem(d,res); 155 | env1 = env1 - rem(env1,res); 156 | env2 = env2 - rem(env2,res); 157 | beats = find(env1==env2 & env1==d_temp); 158 | 159 | %% Eliminate overlapped beats 160 | tol = 0.3; % in secs 161 | tol_samps = round(tol*fs); 162 | rel_beats = true(size(beats)); 163 | finished = false; beat_no = 1; 164 | while ~finished 165 | overlapping_beats = abs(beats-beats(beat_no))= length(beats) 176 | finished = true; 177 | end 178 | end 179 | 180 | rel_beats = beats(rel_beats); 181 | 182 | %% Output beat locations 183 | if inv_log 184 | peaks = rel_beats; 185 | onsets = pulse_onsets_from_peaks(d, peaks); 186 | else 187 | onsets = rel_beats; 188 | end 189 | 190 | end 191 | 192 | function b = mov_avg(a, M) 193 | % calculate moving average 194 | 195 | % setup 196 | N = length(a); 197 | b = nan(size(a)); 198 | 199 | % calculate for each index 200 | for i = M : (N-M-1) 201 | % skip if this can't be calculated 202 | if i-M<1 || i+M>N 203 | continue 204 | end 205 | % calculate moving average for this index 206 | b(i-M+1) = (1/(2*M+1))*sum(a(i-M:i+M)); 207 | end 208 | end 209 | 210 | function min_els = find_minima(sig) 211 | % find indices of local minima in a signal 212 | 213 | min_els = 1+find( sig(2:end-1) 25 | % 26 | % # Author 27 | % Peter H. Charlton, University of Cambridge, February 2022. 28 | % 29 | % # Version 30 | % 1.0 31 | % 32 | % # License - GPL-3.0 33 | % Copyright (c) 2022 Peter H. Charlton 34 | % This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 35 | % This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 36 | % You should have received a copy of the GNU General Public License along with this program. If not, see . 37 | 38 | % Downsample 'no_signal' 39 | if options.do_downsample && sum(strcmp(fieldnames(S), 'no_signal')) 40 | if rem(S.fs, options.downsample_freq) == 0 41 | % Downsample 42 | ds_factor = S.fs/options.downsample_freq; 43 | S.no_signal = downsample(S.no_signal, ds_factor); 44 | else 45 | % Resample 46 | %S.no_signal = resample(double(S.no_signal), options.downsample_freq, S.fs); 47 | t = [0:length(S.no_signal)-1]./S.fs; 48 | S.no_signal = interp1(t, double(S.no_signal), 0:(1/options.downsample_freq):t(end), 'previous'); 49 | S.no_signal = logical(S.no_signal); 50 | end 51 | end 52 | 53 | % Downsample PPG signal 54 | if options.do_downsample 55 | S = do_downsample(S, options.downsample_freq); 56 | end 57 | 58 | % Filter PPG signal 59 | sigs = filter_signal(S, options); 60 | S.v = sigs.filt; 61 | S.fs = sigs.fs; 62 | clear options 63 | 64 | end 65 | 66 | function S = do_downsample(S, downsample_freq) 67 | 68 | %% Downsample 69 | 70 | % Work out whether to downsample or interpolate 71 | if rem(S.fs, downsample_freq) == 0 72 | % Downsample 73 | ds_factor = S.fs/downsample_freq; 74 | S.v = downsample(S.v, ds_factor); 75 | S.fs = downsample_freq; 76 | else 77 | use_resampling = 1; 78 | if use_resampling 79 | % Resample 80 | S.v(isnan(S.v)) = median(S.v(~isnan(S.v))); 81 | S.v = resample(S.v, downsample_freq, S.fs); 82 | else 83 | % Interpolate 84 | t = 1:length(S.v); 85 | t_new = 1:(S.fs/downsample_freq):t(end); 86 | S.v = interp1(t, S.v, t_new); 87 | end 88 | S.fs = downsample_freq; 89 | end 90 | 91 | end 92 | 93 | function sigs = filter_signal(S, options) 94 | 95 | %% Make output variable 96 | sigs.fs = S.fs; 97 | sigs.orig = S.v; 98 | 99 | %% Filter to remove high frequencies 100 | filt_characteristics = options.filtering.elim_high_freqs; 101 | s_filt = elim_vhfs3(S, filt_characteristics); 102 | 103 | %% Filter to remove low frequencies 104 | filt_characteristics = options.filtering.elim_low_freqs; 105 | s_filt = elim_vlfs(s_filt, filt_characteristics); 106 | 107 | %% Output 108 | sigs.filt = s_filt.v; 109 | sigs.curr = sigs.filt; 110 | 111 | end 112 | 113 | function s_filt = elim_vhfs3(s, filt_characteristics) 114 | %% Filter signal to remove VHFs 115 | % Adapted from RRest 116 | 117 | s_filt.fs = s.fs; 118 | 119 | %% Eliminate nans 120 | s.v(isnan(s.v)) = mean(s.v(~isnan(s.v))); 121 | 122 | %% Check to see if sampling freq is at least twice the freq of interest 123 | if (filt_characteristics.Fpass/(s.fs/2)) >= 1 124 | % then the fs is too low to perform this filtering 125 | s_filt.v = s.v; 126 | return 127 | end 128 | 129 | %% Create filter 130 | % parameters for the low-pass filter to be used 131 | AMfilter = create_lpf(filt_characteristics, s); 132 | 133 | %% Re-make filter if it requires too many samples for this signal 134 | % check to see if it requires too many samples 135 | req_sig_length = 3*(length(AMfilter.numerator)-1); 136 | no_attempts = 0; 137 | while no_attempts<4 && length(s.v)<=req_sig_length 138 | % change Fpass (i.e. the high frequency end of the filter band) 139 | filt_characteristics.Fpass = filt_characteristics.Fpass+0.75*(filt_characteristics.Fpass-filt_characteristics.Fstop); 140 | % re-make filter 141 | AMfilter = create_lpf(filt_characteristics, s); 142 | % update criterion 143 | req_sig_length = 3*(length(AMfilter.numerator)-1); 144 | % increment number of attempts 145 | no_attempts = no_attempts+1; 146 | end 147 | if length(s.v)<=req_sig_length 148 | fprintf('\n - Couldn''t perform high frequency filtering') 149 | end 150 | 151 | %% Check frequency response 152 | % Gives a -3 dB cutoff at cutoff_freq Hz, using: 153 | % freqz(AMfilter.Numerator) 154 | % norm_cutoff_freq = 0.0512; % insert freq here from plot 155 | % cutoff_freq = norm_cutoff_freq*(s.fs/2); 156 | 157 | %% Remove VHFs 158 | if length(s.v)>req_sig_length 159 | s_filt.v = filtfilt(AMfilter.numerator, 1, s.v); 160 | else 161 | s_filt.v = s.v; 162 | end 163 | 164 | end 165 | 166 | function AMfilter = create_lpf(filt_characteristics, s) 167 | 168 | [N,Wn,BETA,TYPE] = kaiserord([filt_characteristics.Fstop filt_characteristics.Fpass]/(s.fs/2), [1 0], [filt_characteristics.Dstop filt_characteristics.Dpass]); 169 | b = fir1(N, Wn, TYPE, kaiser(N+1, BETA), 'scale'); 170 | AMfilter = dfilt.dffir(b); 171 | 172 | end 173 | 174 | function s_filt = elim_vlfs(s, filt_characteristics) 175 | %% Filter pre-processed signal to remove frequencies below resp 176 | % Adapted from RRest 177 | 178 | %% Eliminate nans 179 | s.v(isnan(s.v)) = mean(s.v(~isnan(s.v))); 180 | 181 | %% Make filter 182 | flag = 'scale'; 183 | [N,Wn,BETA,TYPE] = kaiserord([filt_characteristics.Fstop filt_characteristics.Fpass]/(s.fs/2), [1 0], [filt_characteristics.Dstop filt_characteristics.Dpass]); 184 | b = fir1(N, Wn, TYPE, kaiser(N+1, BETA), flag); 185 | AMfilter = dfilt.dffir(b); 186 | 187 | %% Check frequency response 188 | % % Gives a -3 dB cutoff at ? Hz, using: 189 | % freqz(AMfilter.Numerator) 190 | % norm_cutoff_freq = 4.435e-3; % insert freq here from plot 191 | % cutoff_freq = norm_cutoff_freq*(s.fs/2); 192 | 193 | if length(s.v) > (length(AMfilter.numerator)-1)*3 194 | % - Tukey Window to avoid edge effects 195 | win_edge_durn = 0.5; % in secs 196 | prop_win = win_edge_durn*2/((length(s.v)-1)/s.fs); 197 | tw = tukeywin(length(s.v),prop_win); 198 | s_filt.v = filtfilt(AMfilter.numerator, 1, s.v.*tw); 199 | s_filt.v = s.v-s_filt.v; 200 | else 201 | s_filt.v = s.v; 202 | end 203 | s_filt.fs = s.fs; 204 | end -------------------------------------------------------------------------------- /source/figures/make_plot_of_ppg_beat_detection_challenges_msptdfastv2.m: -------------------------------------------------------------------------------- 1 | function make_plot_of_ppg_beat_detection_challenges_msptdfastv2 2 | 3 | % load data 4 | subj_no = 7; 5 | raw_data_path = '/Users/petercharlton/Documents/Data/WESAD/conv_data/wesad_all_activities_data.mat'; 6 | load(raw_data_path); 7 | 8 | % identify challenges 9 | lims = [-inf, 0, 10, 20, 30, inf]; 10 | challenges = {}; 11 | for chall_no = 1 : length(lims)-1 12 | challenges{chall_no} = ['SNR: ', num2str(lims(chall_no)), ' to ', num2str(lims(chall_no+1)) ' dB']; 13 | end 14 | 15 | % Obtain beats for this challenge 16 | load_beats = 0; 17 | if load_beats 18 | % Load PPG beats 19 | % - Identify path of detected PPG beats for this subject 20 | subj_no_txt = num2str(subj_no, '%04.f'); 21 | temp = strfind(raw_data_path, '/'); 22 | temp2 = strfind(raw_data_path, '_'); 23 | ppg_beats_path = [raw_data_path(1:temp(end)), 'proc_data_', raw_data_path(temp(end)+1:temp2(end)-1), filesep, subj_no_txt, '_ppg_beats']; 24 | load(ppg_beats_path); 25 | ecg_beats_aligned_path = [raw_data_path(1:temp(end)), 'proc_data_', raw_data_path(temp(end)+1:temp2(end)-1), filesep, subj_no_txt, '_ecg_beats_aligned']; 26 | load(ecg_beats_aligned_path); 27 | else 28 | % Identify beats 29 | [~, ~, ppg_beats_inds.MSPTD] = detect_ppg_beats(data(subj_no).ppg, 'MSPTDfastv2'); % detect beats in PPG 30 | %[ppg_beats_inds.qppgfast, ~, ~] = detect_ppg_beats(data(subj_no).ppg, 'qppgfast'); % detect beats in PPG 31 | [ecg_beats_inds, qual] = detect_ecg_beats(data(subj_no).ecg.v, data(subj_no).ecg.fs); % detect beats in ECG and assess quality of beat detection 32 | ecg_exc_log = ~qual; 33 | options = struct; 34 | [ecg_beats_a_inds.MSPTD, ecg_exc_a_log, lag_ecg_samps] = align_ppg_ecg_beats(ppg_beats_inds.MSPTD, ecg_beats_inds, data(subj_no).ppg.fs, data(subj_no).ecg.fs, options, ecg_exc_log); 35 | 36 | end 37 | 38 | % extract required ECG and PPG data for this challenge 39 | overall_rel_data.ecg = data(subj_no).ecg; 40 | overall_rel_data.ecg.t = [0:length(overall_rel_data.ecg.v)-1]./overall_rel_data.ecg.fs; 41 | %possible_els = find(ecg_exc_a_log==0); 42 | %overall_rel_data.start_time = possible_els(start_el)/data(subj_no).ecg.fs; 43 | overall_rel_data.ppg = data(subj_no).ppg; 44 | overall_rel_data.ppg.t = [0:length(overall_rel_data.ppg.v)-1]./overall_rel_data.ppg.fs; 45 | overall_rel_data.ppg_beats_MSPTD = ppg_beats_inds.MSPTD; 46 | %overall_rel_data.ppg_beats_qppg = ppg_beats_inds.qppgfast; 47 | overall_rel_data.ecg_beats = ecg_beats_a_inds.MSPTD; 48 | 49 | % make subplot for each challenge 50 | for challenge_no = 1 : length(challenges) 51 | 52 | % assess SNR and find start time 53 | options.quality_metrics = 'snr'; 54 | [qual, onsets, win_start_els, win_end_els] = assess_ppg_quality(overall_rel_data.ppg.v, overall_rel_data.ppg.fs, options); 55 | qual_int = interp1(onsets, qual.snr, 1:length(overall_rel_data.ppg.v)); 56 | ecg_exc_a_log_int = interp1(overall_rel_data.ecg.t, ecg_exc_a_log, overall_rel_data.ppg.t); 57 | possible_els = find(qual_int>lims(challenge_no) & qual_int= start_time(challenge_no) & overall_rel_data.ppg.t <= end_time); 84 | rel_ppg.fs = overall_rel_data.ppg.fs; 85 | rel_ppg.v = overall_rel_data.ppg.v(rel_els); 86 | rel_ppg.t = [0:length(rel_ppg.v)-1]./rel_ppg.fs; 87 | rel_ppg.MSPTD = overall_rel_data.ppg_beats_MSPTD(overall_rel_data.ppg_beats_MSPTD>= rel_els(1) & overall_rel_data.ppg_beats_MSPTD<= rel_els(end)) - rel_els(1)+1; 88 | %rel_ppg.qppg = overall_rel_data.ppg_beats_qppg(overall_rel_data.ppg_beats_qppg>= rel_els(1) & overall_rel_data.ppg_beats_qppg<= rel_els(end)) - rel_els(1)+1; 89 | rel_ecg = overall_rel_data.ecg; 90 | rel_els = find(rel_ecg.t >= start_time(challenge_no) & rel_ecg.t <= end_time); 91 | rel_ecg.beats = overall_rel_data.ecg_beats(overall_rel_data.ecg_beats>= rel_els(1) & overall_rel_data.ecg_beats<= rel_els(end)) - rel_els(1)+1; 92 | 93 | % - normalise PPG signal to lie between 0 and 1 94 | rel_ppg.v = (rel_ppg.v-min(rel_ppg.v))./range(rel_ppg.v); 95 | 96 | %% Make plot 97 | %subplot(length(challenges),1, challenge_no) 98 | if length(challenges) == 4 99 | subplot('Position', [0.08,0.06+0.24*(length(challenges)-challenge_no), 0.89, 0.18]) 100 | else 101 | curr_y_number = 3 - challenge_no; 102 | if curr_y_number < 0 103 | curr_y_number = curr_y_number+3; 104 | end 105 | if challenge_no < 4 106 | subplot('Position', [0.08,0.06+0.31*(curr_y_number), 0.40, 0.23]) 107 | else 108 | subplot('Position', [0.59,0.06+0.31*(curr_y_number), 0.40, 0.23]) 109 | end 110 | end 111 | 112 | % - plot ECG beats 113 | for beat_no = 1 : length(rel_ecg.beats) 114 | h = plot(ones(2,1)*rel_ecg.t(rel_ecg.beats(beat_no)), [-0.1,0.23], '--', 'Color', 0.2*ones(1,3), 'LineWidth', lwidth); 115 | hold on 116 | end 117 | 118 | % - plot PPG signal 119 | plot(rel_ppg.t, rel_ppg.v, 'b', 'LineWidth', lwidth), hold on 120 | 121 | % - plot PPG beats 122 | beat_detectors = {'MSPTD'}; %, 'qppg'}; 123 | mk_styles = {'or','+k'}; 124 | mk_sizes = [16,12]; 125 | for beat_detector_no = 1:length(beat_detectors) 126 | curr_beat_detector = beat_detectors{beat_detector_no}; 127 | eval(['rel_ppg_beats_inds = rel_ppg.' curr_beat_detector ';']); 128 | h(beat_detector_no+1) = plot(rel_ppg.t(rel_ppg_beats_inds), rel_ppg.v(rel_ppg_beats_inds), mk_styles{beat_detector_no}, 'LineWidth', lwidth, 'MarkerSize', mk_sizes(beat_detector_no)); 129 | end 130 | 131 | % - Label axes 132 | ylabel('PPG (au)', 'FontSize', ftsize) 133 | if curr_y_number ==0 || challenge_no == 5 134 | xlabel('Time (s)', 'FontSize', ftsize) 135 | end 136 | 137 | % - tidy up plot 138 | xlim([0 durn]) 139 | ylim([-0.1, 1.1]) 140 | xticks = 0:2:durn; 141 | set(gca, 'FontSize', ftsize, 'XTick', xticks, 'YTick', [], 'XGrid', 'on') 142 | box off 143 | 144 | % - add title 145 | if length(challenges) == 4 146 | x_val = .44; 147 | else 148 | if challenge_no < 4 149 | x_val = 0.2; 150 | elseif challenge_no >=4 151 | x_val = 0.75; 152 | end 153 | end 154 | dim = [x_val 0.22+0.31*(curr_y_number) .1 .1]; 155 | annotation('textbox',dim,'String',txt,'FitBoxToText','on', 'FontSize', ftsize, 'LineStyle', 'none'); 156 | 157 | % legend 158 | if challenge_no == 1 159 | leg_txt = {'ECG beats', 'MSPTDfastv2 beat detector'}; %, 'qppg beat detector'} 160 | legend(h, leg_txt, 'Position', [0.525,0.98,0.01,0.01], 'Orientation', 'horizontal', 'FontSize', ftsize) 161 | end 162 | 163 | end 164 | 165 | % save figure 166 | filepath = '/Users/petercharlton/Library/CloudStorage/GoogleDrive-peterhcharlton@gmail.com/My Drive/Work/Images/PPG beat detection/ppg_beat_detection_challenges_msptdfastv2'; 167 | save_fig(filepath) 168 | 169 | end 170 | 171 | function save_fig(filepath) 172 | 173 | print(gcf, filepath, '-depsc') 174 | fid = fopen([filepath, '.txt'], 'w'); 175 | fprintf(fid, ['Created using ' mfilename, ', ', date]); 176 | fclose(fid); 177 | 178 | end -------------------------------------------------------------------------------- /source/pda_beat_detector.m: -------------------------------------------------------------------------------- 1 | function [peaks, onsets] = pda_beat_detector(sig,fs) 2 | % PDA_BEAT_DETECTOR PDA PPG beat detector. 3 | % PDA_BEAT_DETECTOR detects beats in a photoplethysmogram (PPG) signal 4 | % using the 'Peak Detection Algorithm' beat detector 5 | % 6 | % # Inputs 7 | % 8 | % * sig : a vector of PPG values 9 | % * fs : the sampling frequency of the PPG in Hz 10 | % 11 | % # Outputs 12 | % * peaks : indices of detected pulse peaks 13 | % * onsets : indices of detected pulse onsets 14 | % 15 | % # Reference 16 | % E. J. Arguello Prada and R. D. Serna Maldonado, 'A novel and low-complexity peak detection algorithm for heart rate estimation from low-amplitude photoplethysmographic (PPG) signals,' Journal of Medical Engineering and Technology, vol. 42, no. 8, pp. 569-577, 2018. 17 | % 18 | % # Author 19 | % * Elisa Mejía Mejía - wrote the code: 'upslopes' 20 | % * Peter H. Charlton - did very little, just wrote this wrapper 21 | % 22 | % # Documentation 23 | % 24 | % 25 | % # Version 26 | % 1.0 27 | % 28 | % # License - MIT 29 | % Copyright (c) 2022 Elisa Mejía Mejía and Peter H. Charlton 30 | % Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 31 | % The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 32 | % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | 34 | v = 0; % set to 1 to enable visualiation 35 | peaks = upslopes(sig,v); 36 | 37 | peaks = tidy_beats(peaks); 38 | 39 | onsets = pulse_onsets_from_peaks(sig, peaks); 40 | 41 | end 42 | 43 | function ibis = upslopes(ppg,v) 44 | %% Detects inter-beat intervals using the upslopes 45 | % Citation: Argüello Prada EJ, Serna Maldonado RD. (2018 ) A novel and low-complexity peak detection algorithm for 46 | % heart rate estimation from low-amplitude photoplethysmographic (PPG) signals. J Med Eng Technol 42(8):569-577. 47 | % doi: 10.1080/03091902.2019.1572237. Epub 2019 Mar 28. PMID: 30920315. 48 | % 49 | % Inputs: PPG signal, ppg 50 | % visualization option, v (1: plot, 0: don't plot) 51 | % Outputs: position of the starting points of inter-beat intervals, in number of samples, ibis 52 | % 53 | % Developed by: Elisa Mejía Mejía 54 | % City, University of London 55 | % Version: 1.0 - May, 2021 56 | 57 | %% Detection of peaks 58 | if v == 1 59 | color = [ 0, 0.4470, 0.7410; ... 60 | 0.8500, 0.3250, 0.0980; ... 61 | 0.9290, 0.6940, 0.1250; ... 62 | 0.4940, 0.1840, 0.5560; ... 63 | 0.4660, 0.6740, 0.1880; ... 64 | 0.3010, 0.7450, 0.9330; ... 65 | 0.6350, 0.0780, 0.1840; ... 66 | 0.75, 0, 0.75]; 67 | 68 | figure('position',[240 140 1200 625],'name','Upslopes'); 69 | ax(1) = subplot(2,1,1); 70 | plot(ppg,'k','linewidth',1.2); 71 | hold on; 72 | xlim([1 length(ppg)]); 73 | title('Peak detection'); 74 | xlabel('Samples'); 75 | ylabel('PPG'); 76 | grid on; 77 | box off; 78 | 79 | ax(2) = subplot(2,1,2); 80 | plot(ppg,'k','linewidth',1.2); 81 | hold on; 82 | xlim([1 length(ppg)]); 83 | title('Upslopes'); 84 | xlabel('Samples'); 85 | ylabel('PPG'); 86 | grid on; 87 | box off; 88 | linkaxes(ax,'x'); 89 | end 90 | 91 | th = 6; % Initializes threshold 92 | pks = []; % Initializes variable 93 | pos_peak = []; % Initializes variable 94 | pos_peak_b = 0; % Initializes variable 95 | n_pos_peak = 0; % Initializes counter 96 | n_up = 0; % Initializes counter 97 | for i = 2:length(ppg) % Iterates through signal 98 | if ppg(i) > ppg(i - 1) % Verifies if it is the upslope 99 | n_up = n_up + 1; % Adds one to the coutner 100 | 101 | if v == 1 102 | ax(2) = subplot(2,1,2); 103 | plot(i,ppg(i),'.','color',color(5,:)); 104 | linkaxes(ax,'x'); 105 | end 106 | else 107 | if n_up >= th % Checks if the number of data in the upslope is higher than the 108 | % threshold 109 | pos_peak = [pos_peak; i]; % Adds the position to the possible peaks 110 | pos_peak_b = 1; % Sets the value as 1 for the detection of a new possible peak 111 | n_pos_peak = n_pos_peak + 1; % Refreshes counter 112 | n_up_pre = n_up; % Stores the previous value of n_up 113 | 114 | if v == 1 115 | ax(2) = subplot(2,1,2); 116 | plot(pos_peak(n_pos_peak),ppg(pos_peak(n_pos_peak)),'ok','MarkerFaceColor',color(1,:)); 117 | linkaxes(ax,'x'); 118 | end 119 | else 120 | if pos_peak_b == 1 % Verifies if a peak has been found 121 | if ppg(i - 1) > ppg(pos_peak(n_pos_peak)) 122 | % Verifies if the previous sample is higher than 123 | % the one selected as peak 124 | pos_peak(n_pos_peak) = i - 1; 125 | % Updates the value of the position of the possible 126 | % peak 127 | if v == 1 128 | ax(2) = subplot(2,1,2); 129 | plot(pos_peak(n_pos_peak),ppg(pos_peak(n_pos_peak)),'ok','MarkerFaceColor',color(2,:)); 130 | linkaxes(ax,'x'); 131 | end 132 | else 133 | pks = [pks; pos_peak(n_pos_peak)]; 134 | % Refreshes array of peaks 135 | if v == 1 136 | ax(2) = subplot(2,1,2); 137 | plot(pks(end),ppg(pks(end)),'ok','MarkerFaceColor',color(3,:)); 138 | linkaxes(ax,'x'); 139 | end 140 | end 141 | th = 0.6*n_up_pre; % Updates value of threshold 142 | pos_peak_b = 0; % Updates value of variable 143 | end 144 | end 145 | n_up = 0; % Resets counter 146 | end 147 | end 148 | 149 | if v == 1 150 | ax(1) = subplot(2,1,1); 151 | plot(pks,ppg(pks),'ok','MarkerFaceColor',color(1,:)); 152 | linkaxes(ax,'x'); 153 | end 154 | 155 | ibis = pks; % Sets the output 156 | 157 | end --------------------------------------------------------------------------------