---
title: "Reported sampling rates and observed timestamp intervals"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Reported sampling rates and observed timestamp intervals}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

## Measurement question

Device-reported rate, source-export timestamp intervals, frame-delivery cadence, and downstream resampling frequency are distinct quantities. A declared sampling frequency should never be substituted for empirical timestamp checks; neither observed intervals nor a device specification by themselves establish synchronization accuracy, calibration quality, or the temporal accuracy of a fixation or pupil event.

This deliberately synthetic example begins with a *nominal* 120 Hz grid, introduces jitter, and inserts a few delayed intervals to show why mean realized rate and interval distribution should be reported separately.

```{r timestamp-synthetic-data}
set.seed(20261022)
n <- 501L
nominal_ms <- 1000 / 120
interval_ms <- pmax(0.1, stats::rnorm(n - 1L, nominal_ms, 0.55))
delays <- seq(95L, 395L, by = 60L)
interval_ms[delays] <- interval_ms[delays] * 2.6
timestamp_ms <- c(0, cumsum(interval_ms))
realized_rate_hz <- (length(timestamp_ms) - 1L) * 1000 /
  (tail(timestamp_ms, 1L) - timestamp_ms[1L])
stopifnot(length(timestamp_ms) == n,
          all(diff(timestamp_ms) > 0),
          realized_rate_hz < 120)
data.frame(declared_hz = 120, observed_mean_hz = realized_rate_hz,
           interval_q05_ms = unname(stats::quantile(interval_ms, 0.05)),
           interval_median_ms = unname(stats::median(interval_ms)),
           interval_q95_ms = unname(stats::quantile(interval_ms, 0.95)))
```

## Figure 1. The actual interval sequence

```{r timestamp-interval-figure, fig.width=9, fig.height=4, fig.cap="Synthetic timestamp differences. Dashed line shows the nominal device interval, not a measured timing-reference accuracy bound."}
graphics::plot(seq_along(interval_ms), interval_ms, type = "h",
               col = "#24577B", xlab = "Successive observed interval",
               ylab = "Elapsed time (milliseconds)",
               main = "Nominal sampling is not realized delivery")
graphics::abline(h = nominal_ms, lty = 2, col = "#C05B43", lwd = 2)
```

## Figure 2. Interval distribution and delayed updates

```{r timestamp-distribution-figure, fig.width=8, fig.height=4, fig.cap="Original R histogram of intentionally jittered synthetic intervals with occasional delayed updates; no real device measurement or clock-ground-truth comparison."}
graphics::hist(interval_ms, breaks = 28,
               col = "#CEE0ED", border = "#597087",
               xlab = "Observed timestamp interval (milliseconds)",
               main = "Interval distribution reveals long-tail delays")
graphics::abline(v = nominal_ms, col = "#C05B43", lty = 2, lwd = 2)
```

## Reporting constraints

This demonstration supports an elementary acquisition audit: retain native export timestamps and source rate declarations; calculate realized intervals before interpolation, smoothing or trial aggregation; record clock changes and delayed intervals; and distinguish observed missing records from true absence of the underlying behavioral event. It does **not** establish whether delayed samples reflect buffering, device loss, timestamp origin, USB delivery, a host computer, or another cause. That inference needs independent reference timing and vendor-specific documentation.

The separate [draft acquisition-integrity research PR #49](https://github.com/stefanosbalaskas/eyeprocess/pull/49) includes experimental certificate and plotting implementations that remain subject to empirical qualification and CI. This article does not merge those research APIs, change published stable 0.12.0 or authorize a newer CRAN release.
