This case study is entirely synthetic. Its statistics are software demonstrations and are not empirical evidence about Gazepoint hardware, participants, or psychological processes.
The raw simulator is vendor-neutral. The next object creates a small Gazepoint-like view solely to exercise the verified field bridge.
gp_like <- data.frame(
TIME = sim$data$event_time[1:20],
LPD = 15 + sim$data$pupil_mm[1:20],
LPV = as.integer(!is.na(sim$data$pupil_mm[1:20])),
BPOGX = sim$data$gaze_x[1:20],
BPOGY = sim$data$gaze_y[1:20],
BPOGV = 1
)
gazepoint_pupil_mapping_table(inspect_gazepoint_pupil_schema(gp_like))
#> field role unit eye
#> 1 TIME time seconds none
#> 2 TIME_TICK time_tick ticks none
#> 3 LPOGX left_gaze_x normalized_screen left
#> 4 LPOGY left_gaze_y normalized_screen left
#> 5 LPOGV left_gaze_valid normalized_screen left
#> 6 RPOGX right_gaze_x normalized_screen right
#> 7 RPOGY right_gaze_y normalized_screen right
#> 8 RPOGV right_gaze_valid normalized_screen right
#> 9 BPOGX best_gaze_x normalized_screen combined
#> 10 BPOGY best_gaze_y normalized_screen combined
#> 11 BPOGV best_gaze_valid normalized_screen combined
#> 12 LPCX left_pupil_camera_x camera_pixels left
#> 13 LPCY left_pupil_camera_y camera_pixels left
#> 14 LPD left_pupil_diameter_pixels pixels left
#> 15 LPS left_pupil_scale scale left
#> 16 LPV left_pupil_valid indicator left
#> 17 RPCX right_pupil_camera_x camera_pixels right
#> 18 RPCY right_pupil_camera_y camera_pixels right
#> 19 RPD right_pupil_diameter_pixels pixels right
#> 20 RPS right_pupil_scale scale right
#> 21 RPV right_pupil_valid indicator right
#> 22 LEYEX left_eye_x metres left
#> 23 LEYEY left_eye_y metres left
#> 24 LEYEZ left_eye_z metres left
#> 25 LPUPILD left_pupil_diameter_metres metres left
#> 26 LPUPILV left_pupil_3d_valid indicator left
#> 27 REYEX right_eye_x metres right
#> 28 REYEY right_eye_y metres right
#> 29 REYEZ right_eye_z metres right
#> 30 RPUPILD right_pupil_diameter_metres metres right
#> 31 RPUPILV right_pupil_3d_valid indicator right
#> source_specification present
#> 1 Gazepoint Open Gaze API v2-era field specification TRUE
#> 2 Gazepoint Open Gaze API v2-era field specification FALSE
#> 3 Gazepoint Open Gaze API v2-era field specification FALSE
#> 4 Gazepoint Open Gaze API v2-era field specification FALSE
#> 5 Gazepoint Open Gaze API v2-era field specification FALSE
#> 6 Gazepoint Open Gaze API v2-era field specification FALSE
#> 7 Gazepoint Open Gaze API v2-era field specification FALSE
#> 8 Gazepoint Open Gaze API v2-era field specification FALSE
#> 9 Gazepoint Open Gaze API v2-era field specification TRUE
#> 10 Gazepoint Open Gaze API v2-era field specification TRUE
#> 11 Gazepoint Open Gaze API v2-era field specification TRUE
#> 12 Gazepoint Open Gaze API v2-era field specification FALSE
#> 13 Gazepoint Open Gaze API v2-era field specification FALSE
#> 14 Gazepoint Open Gaze API v2-era field specification TRUE
#> 15 Gazepoint Open Gaze API v2-era field specification FALSE
#> 16 Gazepoint Open Gaze API v2-era field specification TRUE
#> 17 Gazepoint Open Gaze API v2-era field specification FALSE
#> 18 Gazepoint Open Gaze API v2-era field specification FALSE
#> 19 Gazepoint Open Gaze API v2-era field specification FALSE
#> 20 Gazepoint Open Gaze API v2-era field specification FALSE
#> 21 Gazepoint Open Gaze API v2-era field specification FALSE
#> 22 Gazepoint Open Gaze API v2-era field specification FALSE
#> 23 Gazepoint Open Gaze API v2-era field specification FALSE
#> 24 Gazepoint Open Gaze API v2-era field specification FALSE
#> 25 Gazepoint Open Gaze API v2-era field specification FALSE
#> 26 Gazepoint Open Gaze API v2-era field specification FALSE
#> 27 Gazepoint Open Gaze API v2-era field specification FALSE
#> 28 Gazepoint Open Gaze API v2-era field specification FALSE
#> 29 Gazepoint Open Gaze API v2-era field specification FALSE
#> 30 Gazepoint Open Gaze API v2-era field specification FALSE
#> 31 Gazepoint Open Gaze API v2-era field specification FALSELPD is labelled as pixels by the bridge. The example
does not convert those synthetic values into millimetres.
contract <- create_pupil_contract(
outcome_col = "pupil_mm",
participant_col = "participant_id",
trial_col = "trial_id",
item_col = "item_id",
condition_col = "condition",
time_col = "event_time",
pupil_unit = "millimetres",
sampling_frequency = 20,
eye = "combined",
blink_col = "blink",
interpolation_col = "interpolated",
gaze_x_col = "gaze_x",
gaze_y_col = "gaze_y",
luminance_col = "luminance",
baseline_window = c(-0.5, 0),
preprocessing_provenance = "gp3bayes deterministic simulator"
)
prepared <- prepare_pupil_timecourse(sim$data, contract)
readiness <- audit_pupil_readiness(prepared)
measurement <- audit_pupil_measurement_context(prepared)
spec <- specify_pupil_timecourse_model(
prepared,
smooth_basis_dimension = 6,
autocorrelation = "ar1"
)
pupil_readiness_table(readiness)
#> metric value status
#> 1 rows 1968 pass
#> 2 participants 8 pass
#> 3 trials 48 pass
#> 4 items 12 pass
#> 5 conditions 2 pass
#> 6 estimated_sampling_hz 20 pass
#> 7 median_sampling_interval 0.05 pass
#> 8 sampling_interval_cv 1.38814e-15 pass
#> 9 missing_pupil_proportion 0 pass
#> 10 blink_proportion 0 pass
#> 11 interpolated_proportion 0 pass
#> 12 invalid_proportion <NA> review
#> 13 baseline_coverage 1 pass
#> 14 trials_lacking_baseline 0 pass
#> 15 pupil_min 3.18649 pass
#> 16 pupil_max 5.30599 pass
#> 17 minimum_trial_time_span 2 review
#> 18 maximum_trial_time_span 2 review
#> 19 gaze_available TRUE pass
#> 20 gaze_condition_imbalance 0.00178244 review
#> 21 left_right_pupil_disagreement <NA> review
#> 22 luminance_available TRUE pass
#> 23 luminance_condition_imbalance 3.24082e-05 review
#> 24 contrast_available FALSE review
#> 25 pfe_corrected_upstream FALSE review
#> 26 preprocessing_provenance_completeness 0.333 review
#> detail
#> 1
#> 2 One-participant data cannot support population participant heterogeneity.
#> 3
#> 4 Item hierarchy is optional.
#> 5 A condition contrast requires at least two levels.
#> 6 Declared 20 Hz.
#> 7
#> 8 AR(1) sample-order dependence requires regular-enough sampling.
#> 9 Missingness is reported, not automatically repaired or excluded.
#> 10
#> 11
#> 12
#> 13
#> 14
#> 15
#> 16
#> 17 Smallest observed within-trial event-time coverage.
#> 18 Largest observed within-trial event-time coverage.
#> 19
#> 20 Descriptive between-condition gaze-position difference.
#> 21 Median absolute paired-channel difference.
#> 22
#> 23 Descriptive between-condition mean luminance difference.
#> 24
#> 25 PFE status is contextual evidence, not an automatic correction.
#> 26
pupil_measurement_audit_table(measurement)
#> domain available observed
#> 1 blink TRUE 0
#> 2 interpolation TRUE 0
#> 3 baseline TRUE -0.5 to 0
#> 4 sampling TRUE 1.3881e-15
#> 5 gaze_position TRUE 0.0031145
#> 6 pfe TRUE FALSE
#> 7 luminance TRUE 3.2408e-05
#> 8 contrast FALSE <NA>
#> 9 time_on_task FALSE <NA>
#> interpretation
#> 1 Reported data-loss context only.
#> 2 Reported upstream interpolation context only.
#> 3 Baseline declaration; no automatic choice.
#> 4 Sampling irregularity context for temporal modelling.
#> 5 Between-condition mean gaze-position difference; descriptive only.
#> 6 Upstream PFE-correction declaration; no automatic PFE correction.
#> 7 Between-condition mean luminance difference; descriptive only.
#> 8 Contrast availability/range; no automatic adjustment.
#> 9 Recording-time support when available.
pupil_specification_table(spec)
#> field
#> 1 family
#> 2 likelihood
#> 3 link
#> 4 formula
#> 5 temporal_structure
#> 6 smooth_basis_dimension
#> 7 condition_trajectory
#> 8 autocorrelation
#> 9 participant_effects
#> 10 participant_trajectory
#> 11 item_effects
#> 12 covariates
#> 13 outcome_unit
#> 14 baseline_operation
#> 15 unrestricted_formula
#> value
#> 1 pupil
#> 2 Gaussian
#> 3 identity
#> 4 .pupil_model ~ .condition + s(.event_time, by = .condition, k = 6) + (1 | .participant) + (1 | .item) + ar(time = .sample_index, gr = .series_id, p = 1)
#> 5 smooth
#> 6 6
#> 7 TRUE
#> 8 ar1
#> 9 random_intercept
#> 10 none
#> 11 TRUE
#> 12
#> 13 millimetres
#> 14 none
#> 15 FALSEfit <- fit_pupil_model_backend(spec, backend = "cmdstanr", cores = 2)
trajectory <- estimate_pupil_trajectory(
predict_pupil_trajectory(fit, ndraws = 200)
)
window <- estimate_pupil_window(
predict_pupil_trajectory(fit, ndraws = 200),
window = c(0.3, 1.0)
)
auc <- estimate_pupil_auc(
predict_pupil_trajectory(fit, ndraws = 200),
window = c(0.3, 1.0)
)
ppc <- check_pupil_posterior_predictive(fit, ndraws = 200)
diag <- diagnose_pupil_fit(fit)
plan <- create_pupil_validation_plan(
prepared,
target = "new_trial_known_participant",
K = 4
)
validation <- validate_pupil_model(fit, plan, execute = TRUE)spec_for_sensitivity <- specify_pupil_timecourse_model(
prepared,
autocorrelation = "none",
smooth_basis_dimension = 5
)
suite <- create_pupil_sensitivity_suite(
spec_for_sensitivity,
baseline_windows = list(c(-0.5, -0.1), c(-0.4, -0.1)),
baseline_window_operation = "subtract",
interpolation_policy = c("retain", "exclude_flagged"),
gaze_adjustment = c("none", "declared_covariates"),
luminance_adjustment = c("none", "declared_covariate"),
analysis_windows = list(c(0.3, 1.0))
)
head(pupil_sensitivity_table(suite))
#> scenario_id axis value
#> 1 S001 baseline_window -0.5,-0.1
#> 2 S002 baseline_window -0.4,-0.1
#> 3 S003 interpolation_policy retain
#> 4 S004 interpolation_policy exclude_flagged
#> 5 S005 gaze_adjustment none
#> 6 S006 gaze_adjustment declared_covariatesAll windows and sensitivity dimensions are declared. No simulated estimate is presented as an empirical effect, and no scenario is automatically selected.