---
title: "ECG Morphology: PQRST Segmentation and Interval Analysis"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{ECG Morphology: PQRST Segmentation and Interval Analysis}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)
```

## Introduction

Beyond heart rate and HRV analysis, the morphology of individual ECG
waveforms carries important clinical and research information. PhysioECG
provides functions for PQRST waveform delineation and computation of
standard clinical ECG intervals (PR, QT, QTc, QRS duration).

This vignette demonstrates:

1. Generating synthetic ECG with known PQRST morphology
2. R-peak detection as the entry point for morphology analysis
3. PQRST waveform delineation (P-wave, QRS complex, T-wave)
4. Computing clinical ECG intervals
5. QT interval analysis and Bazett correction

## Setup

```{r setup}
library(PhysioECG)
library(PhysioCore)
```

## Simulating ECG with PQRST Morphology

The `make_ecg_pqrst()` generator creates synthetic ECG signals with
physiologically realistic P, Q, R, S, and T wave components. It also
returns a data.frame of known fiducial points for validation.

```{r simulate}
result <- make_ecg_pqrst(
  n_time = 10000,   # 20 seconds at 500 Hz
  n_channels = 1,
  sr = 500,
  heart_rate = 72,
  noise_sd = 0.02
)

pe <- result$pe
fiducials <- result$fiducials
head(fiducials)
```

The `fiducials` data.frame includes columns for each known wave location
(`r_peak`, `p_peak`, `q_point`, `s_point`, `t_peak`) and QRS boundaries
(`qrs_onset`, `qrs_offset`), all in sample indices.

## R-Peak Detection

Morphology analysis begins with R-peak detection. The Pan-Tompkins
algorithm reliably identifies R-peaks even in the presence of moderate
noise.

```{r rpeaks}
peaks <- ecgDetectRpeaks(pe)
head(peaks)
```

Compare detected peaks against known fiducial points:

```{r validate-peaks}
# Check detection accuracy
n_detected <- nrow(peaks)
n_expected <- nrow(fiducials)
cat(sprintf("Detected %d peaks, expected %d\n", n_detected, n_expected))
```

## PQRST Waveform Delineation

The `ecgDelineate()` function identifies the full PQRST complex for each
detected R-peak:

- **QRS onset** and **QRS offset** (J-point): detected using gradient-based
  search from the R-peak
- **P-wave peak**: local maximum in the 300--80 ms window before the R-peak
- **T-wave peak**: local maximum in the 80--500 ms window after the R-peak
- **T-wave end**: estimated using the tangent-intercept method from the
  T-wave downslope

```{r delineate}
delin <- ecgDelineate(pe, peaks)
head(delin)
```

The result contains one row per beat with columns: `channel`, `beat`,
`r_peak`, `qrs_onset`, `qrs_offset`, `qrs_duration_ms`, `p_peak`,
`t_peak`, and `t_end`.

### Understanding Delineation Output

```{r delineation-summary}
# QRS duration distribution
summary(delin$qrs_duration_ms)

# P-wave detection rate
p_detected <- sum(!is.na(delin$p_peak))
cat(sprintf("P-wave detected in %d/%d beats (%.0f%%)\n",
            p_detected, nrow(delin), 100 * p_detected / nrow(delin)))

# T-wave detection rate
t_detected <- sum(!is.na(delin$t_peak))
cat(sprintf("T-wave detected in %d/%d beats (%.0f%%)\n",
            t_detected, nrow(delin), 100 * t_detected / nrow(delin)))
```

### Validating Against Known Fiducials

When using simulated data with known fiducial points, you can assess
delineation accuracy:

```{r validate-delineation}
# Match detected beats to known fiducials by closest R-peak
matched <- merge(
  delin,
  fiducials,
  by.x = "beat",
  by.y = "beat",
  suffixes = c("_detected", "_true")
)

# QRS onset accuracy (in samples)
onset_error <- matched$qrs_onset - matched$qrs_onset
cat(sprintf("QRS onset error: mean = %.1f samples, SD = %.1f samples\n",
            mean(onset_error, na.rm = TRUE),
            sd(onset_error, na.rm = TRUE)))
```

## Computing Clinical ECG Intervals

The `ecgIntervals()` function calculates standard clinical ECG intervals
from the delineation output:

- **PR interval**: P-wave peak to QRS onset (ms)
- **QT interval**: QRS onset to T-wave end (ms)
- **QTc**: Corrected QT using Bazett's formula (QT / sqrt(RR in seconds))
- **QRS duration**: QRS onset to QRS offset (ms)
- **RR interval**: R-peak to next R-peak (ms)

```{r intervals}
intervals <- ecgIntervals(delin, sr = samplingRate(pe))
head(intervals)
```

### QRS Duration Analysis

Normal QRS duration is 80--120 ms. Prolonged QRS may indicate bundle
branch block or ventricular conduction abnormalities.

```{r qrs-analysis}
# Summary statistics
summary(intervals$qrs_ms)

# Identify beats with prolonged QRS (> 120 ms)
prolonged <- intervals[intervals$qrs_ms > 120, ]
cat(sprintf("Beats with prolonged QRS: %d/%d\n",
            nrow(prolonged), nrow(intervals)))
```

### PR Interval Analysis

Normal PR interval is 120--200 ms. Shortened PR may indicate
pre-excitation (WPW syndrome); prolonged PR suggests first-degree AV block.

```{r pr-analysis}
# Filter beats with detected P-waves
pr_valid <- intervals[!is.na(intervals$pr_ms), ]
summary(pr_valid$pr_ms)

# Check for PR prolongation
pr_prolonged <- pr_valid[pr_valid$pr_ms > 200, ]
cat(sprintf("Beats with prolonged PR: %d/%d\n",
            nrow(pr_prolonged), nrow(pr_valid)))
```

### QT Interval and Bazett Correction

The QT interval represents ventricular depolarization and repolarization.
Because QT varies with heart rate, the corrected QT (QTc) using Bazett's
formula is preferred for clinical assessment. Normal QTc is below 440 ms
for males and 460 ms for females.

```{r qt-analysis}
# Filter beats with valid QT measurements
qt_valid <- intervals[!is.na(intervals$qt_ms) & !is.na(intervals$qtc_ms), ]
summary(qt_valid$qt_ms)
summary(qt_valid$qtc_ms)

# Check for QTc prolongation (> 460 ms)
qtc_prolonged <- qt_valid[qt_valid$qtc_ms > 460, ]
cat(sprintf("Beats with prolonged QTc: %d/%d\n",
            nrow(qtc_prolonged), nrow(qt_valid)))
```

### Beat-to-Beat Variability

Examine how intervals change across beats:

```{r variability}
# QT variability index
if (nrow(qt_valid) > 2) {
  qt_sd <- sd(qt_valid$qt_ms, na.rm = TRUE)
  rr_sd <- sd(qt_valid$rr_ms, na.rm = TRUE)
  cat(sprintf("QT SD: %.1f ms, RR SD: %.1f ms\n", qt_sd, rr_sd))
}
```

## Complete Morphology Analysis Pipeline

Here is a full pipeline from raw ECG to morphology analysis:

```{r pipeline}
# 1. Simulate or load ECG data
result <- make_ecg_pqrst(
  n_time = 60000,  # 2 minutes at 500 Hz
  sr = 500,
  heart_rate = 70,
  noise_sd = 0.02
)
pe <- result$pe

# 2. Optional: correct baseline wander
pe <- ecgBaselineCorrect(pe, method = "highpass", cutoff = 0.5,
                         output_assay = "clean")

# 3. Detect R-peaks
peaks <- ecgDetectRpeaks(pe, assay_name = "clean")

# 4. Delineate PQRST waveforms
delin <- ecgDelineate(pe, peaks, assay_name = "clean")

# 5. Compute clinical intervals
intervals <- ecgIntervals(delin, sr = samplingRate(pe))

# 6. Summary report
cat("=== ECG Morphology Summary ===\n")
cat(sprintf("Total beats analyzed: %d\n", nrow(intervals)))
cat(sprintf("Mean QRS duration: %.1f ms (SD: %.1f)\n",
            mean(intervals$qrs_ms, na.rm = TRUE),
            sd(intervals$qrs_ms, na.rm = TRUE)))
cat(sprintf("Mean PR interval: %.1f ms (SD: %.1f)\n",
            mean(intervals$pr_ms, na.rm = TRUE),
            sd(intervals$pr_ms, na.rm = TRUE)))
cat(sprintf("Mean QTc: %.1f ms (SD: %.1f)\n",
            mean(intervals$qtc_ms, na.rm = TRUE),
            sd(intervals$qtc_ms, na.rm = TRUE)))
cat(sprintf("Mean RR interval: %.1f ms (SD: %.1f)\n",
            mean(intervals$rr_ms, na.rm = TRUE),
            sd(intervals$rr_ms, na.rm = TRUE)))
```

## Handling Multi-Channel ECG

Both delineation and interval functions handle multi-channel ECG data.
Results are computed independently per channel.

```{r multi-channel}
# Create multi-channel ECG
result_mc <- make_ecg_pqrst(n_time = 10000, n_channels = 3, sr = 500)
pe_mc <- result_mc$pe

peaks_mc <- ecgDetectRpeaks(pe_mc)
delin_mc <- ecgDelineate(pe_mc, peaks_mc)
intervals_mc <- ecgIntervals(delin_mc, sr = samplingRate(pe_mc))

# Per-channel summary
for (ch in unique(intervals_mc$channel)) {
  ch_data <- intervals_mc[intervals_mc$channel == ch, ]
  cat(sprintf("Channel %d: %d beats, mean QTc = %.1f ms\n",
              ch, nrow(ch_data),
              mean(ch_data$qtc_ms, na.rm = TRUE)))
}
```

## References

- Goldberger, A.L., et al. (2000). "PhysioBank, PhysioToolkit, and
  PhysioNet: Components of a new research resource for complex physiologic
  signals." *Circulation*, 101(23), e215--e220.
- Pan, J. & Tompkins, W.J. (1985). "A real-time QRS detection algorithm."
  *IEEE Transactions on Biomedical Engineering*, 32(3), 230--236.
- Clifford, G.D., Azuaje, F. & McSharry, P.E. (2006). *Advanced Methods
  and Tools for ECG Data Analysis*. Artech House.
- Bazett, H.C. (1920). "An analysis of the time-relations of
  electrocardiograms." *Heart*, 7, 353--370.

## Session Info

```{r session-info}
sessionInfo()
```
