Zum Inhalt springen
EcoSimLab
DeutschEnglish
Docs-Navigation
Tool-Docs

SAGAT-Freeze

Das jsPsych-Plugin für SAGAT-Freezes installieren, einen Freeze mit Items konfigurieren und die Antwortdaten auslesen.

Gehört zu: SAGAT-freeze

Diese Seite ist die Anleitung zum Plugin-Paket @ecosimlab/jspsych-plugin-videosimlab-sagat: Installation, Trial-Konfiguration, Item-Formate, Datenausgabe. Ein Trial dieses Typs entspricht einem SAGAT-Freeze — die Items werden nacheinander abgefragt, danach endet der Trial und die Timeline läuft weiter.

Alles hier ist aus dem Repo videosimlab-sagat belegt, Stand Tag 1.0.1 (Commit e64c9a1, zugleich main), Paketversion 1.0.1. Die mitgelieferte Repo-Doku (docs/plugin-videosimlab-sagat.md) ist stellenweise noch das unausgefüllte jsPsych-Template und weicht an mehreren Stellen vom Code ab — wo das so ist, steht unten der Code-Stand, und die Abweichung ist markiert.

Voraussetzungen

  • jsPsych ≥ 8.0.0. Im Repo als reguläre dependency deklariert, nicht als peerDependency — in einer bestehenden Studie also darauf achten, dass nicht zwei jsPsych-Instanzen im Bundle landen.
  • Node/npm für Installation und Build. Der Unter-Build src/sagat-items ist ein Vite-/Solid-Projekt; sein README nennt pnpm, der Build-Script ruft npm install auf.
  • Browser-Kontext. Das Plugin rendert in das jsPsych-display_element und injiziert sein eigenes CSS. Kein Videoplayer und keine Datenquelle nötig — das Ausblenden von Video und Display ist Sache der umgebenden Timeline.

Installation

Das Paket heißt @ecosimlab/jspsych-plugin-videosimlab-sagat. Es ist nicht in der npm-Registry veröffentlicht — der Kommentar in examples/index.html („Once this plugin package is published, you can load it from a CDN instead“) bestätigt das. Die Installation läuft über das öffentliche GitLab-Repo, festgelegt auf einen Tag:

npm install https://gitlab.com/ecosimlab/videosimlab-sagat#1.0.1

Der README nennt an dieser Stelle noch #1.0.0. Der neueste Tag ist 1.0.1, und die package.json steht ebenfalls auf 1.0.1. Es gilt der Tag 1.0.1.

Das Paket liefert seinen gebauten dist/-Ordner mit. Die Installation baut also nichts nach.

Import. README und package.json nennen denselben Paketnamen:

import jsPsychVideosimlabSagat from "@ecosimlab/jspsych-plugin-videosimlab-sagat";

Der README schreibt die Variable als jsPsychVideoSimLabSagat. Der Name der Variablen ist frei wählbar; maßgeblich für das Browser-Global ist jsPsychVideosimlabSagat aus rollup.config.mjs.

Im Browser ohne Bundler. Das README lässt diesen Abschnitt leer, der Build liefert die Datei aber: dist/index.browser.min.js (auch als unpkg-Feld eingetragen) setzt das globale Symbol jsPsychVideosimlabSagat — so macht es auch examples/index.html.

Einbindung / Quickstart

examples/index.html bindet jsPsych und das gebaute Browser-Bundle direkt ein:

<script src="https://unpkg.com/jspsych"></script>
<script src="../dist/index.browser.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/jspsych/css/jspsych.css" />

Das Beispiel-Trial im Repo ist allerdings nicht lauffähig — es definiert nur type: jsPsychVideosimlabSagat ohne items, und die Item-Komponente greift beim Rendern sofort auf items[0] zu. Minimal lauffähig ist erst ein Trial mit mindestens einem Item:

const jsPsych = initJsPsych();

const trial = {
  type: jsPsychVideosimlabSagat,
  sectorId: "sector-01",
  replayType: "efficient",
  items: [
    {
      id: "level1-1",
      itemType: "range",
      stimulus: "Wie schnell sind Sie gerade gefahren?",
      options: [
        { text: "Geschwindigkeit", start: 0, min: 0, max: 130, unit: "km/h" },
      ],
    },
  ],
};

jsPsych.run([trial]);

Der Ablauf ist fest verdrahtet: Überschrift „Fragen zur Fahrt“, Fortschrittsanzeige n / gesamt, ein Item pro Seite, Button „Weiter“. Ohne Eingabe erscheint statt des Weitersprungs der Hinweis „Eingabe erforderlich“; nach dem letzten Item endet der Trial. Alle Beschriftungen des Plugins sind deutsch.

Freezes und Fragen konfigurieren

Ein Freeze = ein Trial = ein items-Array. Jedes Item hat vier Felder:

Feld Typ Bedeutung
id String Item-Kennung, Konvention laut Code-Kommentar: level-number-typelevel1/level2/level3 (SA-Ebene), fortlaufende Nummer, optional p für Power bzw. c für Consumption
itemType String Item-Template, siehe unten
stimulus String Die angezeigte Frage
options Objekt[] Antwortkonfiguration; die Felder hängen vom itemType ab

Achtung itemType-Namen. Plugin-info, README und Repo-Doku nennen die vier Typen radio, select, range, bar. Der tatsächlich ausgewertete Satz (utils.ts, App.tsx) ist:

radio · selection · range · bar · pedal · dragdrop

select trifft im Code keinen Zweig — Mehrfachauswahl heißt selection. pedal und dragdrop sind implementiert, aber nirgends dokumentiert.

radio und selection — Auswahl

Optionen: { text, suboptions }, suboptions ist ein String-Array. text ist die Gruppenüberschrift, jede Suboption wird als Zeile mit Radio-Button (radio) bzw. Checkbox (selection) gerendert. Zwei Sonderfälle sind im Code fest verdrahtet:

  • Ist text einer von pickup/Pickup, pkw/Pkw/PKW, bus/Bus, wird statt der Überschrift ein Fahrzeugbild gezeigt.
  • Suboptionen N, D, R, P bekommen eine Fahrmodus-Darstellung.

range — horizontaler Slider

Optionen: { text, start, min, max, unit }. Mehrere Slider pro Item sind möglich. Je nach Inhalt wird zusätzlich eine Anzeige über dem Slider eingeblendet — die Auswahl erfolgt per String-Vergleich:

Bedingung Zusatzanzeige
unit === "km/h" Tacho
text enthält "SOC" Batterie-Symbol in Prozent
text enthält "Reichweite" Reichweite in km
text enthält "Durchschnittlicher Verbrauch" Verbrauch in kWh/100km

Einschränkung: min und max werden für alle Slider eines Items aus options[0] gelesen — abweichende Grenzen pro Slider wirken nicht.

bar — vertikaler Slider (Power / Rekuperation)

Optionen: { text, textTop, textBottom, start, unit }. textTop/textBottom beschriften die beiden Enden der Säule; text erscheint nur als Überschrift, wenn das Item mehrere Bars enthält. Der rohe Sliderbereich ist fest -149 … 300 (negativ = Rekuperation). Der daraus berechnete Wert wird über unit skaliert: bei unit === "kW" auf 39 (negativ) bzw. 92 (positiv), sonst auf 1 bzw. 2 — für Verbrauchsangaben also effektiv ein Bereich von −1 bis 2.

pedal — Energie- und Bremspedal

Optionen: { text, start }. Rendert zwei vertikale Slider („Energiepedal“, „Bremspedal“), jeweils roh 0 … 300, angezeigt und gespeichert als Prozentwert 0–100.

dragdrop — Fahrzeug-Relativposition

Optionen: { text } je Fahrzeug. Gerendert wird eine Fahrspur mit vier Ablagefeldern pos1pos4 (von unten nach oben, vor dem Ego-Fahrzeug) und darunter eine Startreihe mit den Fahrzeugen aus options. Das Ego-Fahrzeug (zoe) steht fest und ist nicht ziehbar. text bestimmt zugleich das Fahrzeugbild (pickup, pkw, bus, sonst zoe).

Nicht deklarierte Felder: Die options-Struktur in der Plugin-info kennt nur text, textTop, textBottom und start. suboptions, min, max und unit werden von den Komponenten gelesen, sind in der info aber nicht deklariert — sie erscheinen deshalb nicht in der generierten Parameter-Doku und bekommen keine jsPsych-Defaults.

API-Referenz

Default-Export: die Plugin-Klasse VideosimlabSagatPlugin (info.name: plugin-videosimlab-sagat). Browser-Global und Rollup-Name: jsPsychVideosimlabSagat.

Trial-Parameter

Parameter Typ Default Beschreibung
sectorId STRING undefined Kennung des Sektors
replayType STRING undefined Effizienz-Typ des Replays; im Code-Kommentar: efficient / inefficient
items COMPLEX undefined Array der Items dieses Freezes (siehe oben)

Dazu kommen die Standardparameter aller jsPsych-Plugins.

TypeScript-Typen (src/utils.ts)

Item, ItemType, Answer, DataItem, SagatItem sowie die optionsspezifischen Typen SelectionItemOptions, RangeItemOptions, BarItemOptions, PedalItemOptions, DragDropItemOptions. Die gebauten Deklarationen liegen in dist/index.d.ts (typings-Feld).

Events und Styling

Es gibt keine eigenen Events oder Callbacks — der einzige Ausgang ist jsPsych.finishTrial() am Ende des letzten Items; für Reaktionen auf den Freeze also on_finish am Trial nutzen.

Die kompilierten Tailwind-Styles injiziert das Plugin beim ersten Trial einmalig als <style id="jspsych-videosimlab-sagat-styles">. Alle Utilities tragen den Präfix tw:, der Tailwind-Preflight ist bewusst weggelassen — die Host-Seite wird nicht überschrieben.

Trial-Daten

Der Trial schreibt ein Objekt mit drei Feldern in die jsPsych-Daten. Die Feldnamen im Code weichen von der Repo-Doku ab — maßgeblich ist der Code (src/index.ts):

Feld im Code laut Repo-Doku Inhalt
sectroId sectorId durchgereichtes sectorId — der Tippfehler steckt im Quellcode
replayType replayType durchgereichtes replayType
answers sagat Array der beantworteten Items

Jeder Eintrag in answers ist ein DataItem und spiegelt die Item-Konfiguration zurück, ergänzt um die Antworten:

{
  "id": "level1-1",
  "itemType": "range",
  "stimulus": "Wie schnell sind Sie gerade gefahren?",
  "options": [{ "text": "Geschwindigkeit", "start": 0, "min": 0, "max": 130, "unit": "km/h" }],
  "answers": [{ "text": "Geschwindigkeit", "answer": 62 }]
}

Was answers[].text und answers[].answer bedeuten, hängt am Item-Typ:

Typ text answer
radio / selection "<Gruppe>-<Suboption>" "on" bei Auswahl, sonst "" — es werden alle Suboptionen ausgegeben, nicht nur die gewählten
range options[i].text Zahlwert des Sliders
bar options[i].textTop skalierter Zahlwert (siehe bar oben)
pedal options[i].text String "energyPedal:<0–100>-brakePedal:<0–100>"
dragdrop options[i].text ID des Ablagefeldes (pos1pos4) bzw. null, solange nicht abgelegt

Es gibt keine Zeitstempel pro Item — nur die von jsPsych selbst erfassten Trial-Zeiten (rt, time_elapsed) — und keine Ground Truth im Plugin: Der Code-Kommentar „the correct one is marked“ trifft nicht zu, es wird nichts als richtig markiert. Der Abgleich mit den Fahrdaten passiert nachgelagert in der Auswertung.

Entwicklung & Tests

npm install
npm run build        # baut erst src/sagat-items, dann rollup --config
npm run build:watch
npm run tsc
npm test             # jest
npm run test:watch
  • Zweistufiger Build. build:sagat-items wechselt nach src/sagat-items, installiert dort und baut das Solid-/Vite-Teilprojekt zu src/sagat-items/dist/sagat-items.js. Erst danach bündelt Rollup (makeRollupConfig("jsPsychVideosimlabSagat") aus @jspsych/config) das Hauptpaket nach dist/ (ESM, CJS, Browser, minifiziert).
  • CSS-Einbettung. Das Vite-Plugin scripts/export-css.ts hängt die kompilierte CSS als export const __css__ = "…" an den Entry an und löscht die separate .css-Datei — deshalb braucht das fertige Paket keine extra Stylesheet-Einbindung.
  • Item-Templates isoliert entwickeln. In src/sagat-items startet npm run dev einen Vite-Dev-Server auf Port 3001 mit den Item-Komponenten ohne jsPsych drumherum.
  • Tests. jest.config.cjs nutzt @jspsych/config/jest. src/index.spec.ts ist allerdings noch der unveränderte jsPsych-Template-Test: er übergibt erfundene Parameter (parameter_name, parameter_name2) und prüft nur, dass der Trial endet. Es gibt faktisch keine inhaltliche Testabdeckung.

Lizenz & Zitation

MIT-Lizenz, Copyright 2026 Leonardt Wagner (LICENSE.txt, bestätigt durch package.json).

Die CITATION.cff nennt: Leonardt Wagner (ORCID 0009-0009-1759-9674), Titel jsPsychVideosimlabSagat, Version 1.0.0, Release-Datum 2026-02-07, kein DOI. Beim Build ersetzt @jspsych/config den Platzhalter citations: '__CITATIONS__' in der Plugin-info durch die daraus generierten Angaben — abrufbar über die jsPsych-Zitationsfunktion.

Zwei Fehler in der CITATION.cff nicht ungeprüft übernehmen: url zeigt auf videosimlab-cockpit-replay statt auf dieses Repo (Copy-Paste), und Version 1.0.0 widerspricht der Paketversion 1.0.1.

Troubleshooting

  • Cannot find module "@jspsych-plugin-videosimlab-sagat" — der Paketname trägt den Scope @ecosimlab/. Aus @ecosimlab/jspsych-plugin-videosimlab-sagat importieren (siehe Installation).
  • Trial bricht sofort ab / weiße Seiteitems fehlt oder ist leer. Das Plugin greift ungeprüft auf das erste Item zu; mindestens ein Item ist Pflicht.
  • Item wird gar nicht gerendertitemType prüfen. select ist kein gültiger Wert, Mehrfachauswahl heißt selection. Bei unbekanntem Typ bleibt der Bereich schlicht leer, ohne Fehlermeldung.
  • Auswertung findet sagat nicht — das Datenfeld heißt im Code answers, die Sektorkennung landet unter sectroId.
  • Layout ohne Styles — der Unter-Build fehlt. Das Hauptpaket importiert src/sagat-items/dist/sagat-items.js (liefert Markup und __css__); ohne npm run build:sagat-items schlägt der Rollup-Build fehl.
  • Fahrzeugbilder fehlen (selection, dragdrop) — die Bilder werden über relative Pfade wie ./cars/zoe.png geladen und nicht ins Bundle eingebettet. Die Dateien aus src/sagat-items/public/cars/ müssen von der Host-Seite unter diesem Pfad ausgeliefert werden.
  • range-Slider ignoriert min/max — beide werden für alle Slider eines Items aus options[0] gelesen. Slider mit abweichenden Grenzen auf eigene Items aufteilen.

Offene Punkte

  • Versionsangaben im Repo. README (#1.0.0) und CITATION.cff (1.0.0) hängen hinter Tag und Paketversion (1.0.1) zurück. Die url der CITATION.cff zeigt auf das falsche Repo.
  • Feldnamen der Datenausgabe. sectroId/answers (Code) vs. sectorId/sagat (Doku) — ob der Code korrigiert oder die Doku angepasst wird, ist eine offene Entscheidung des Teams. Die Anleitung hier wird danach angepasst.
  • select vs. selection — dieselbe Frage für die Typbezeichnung.
  • Beispiel-Konfigurationen. Im Repo liegt kein vollständiges, echtes Item-Set (z. B. das der VideoSimLab-Studie). Die Beispiele hier sind aus den Komponenten rekonstruiert, nicht aus einer produktiv genutzten Konfiguration.
  • Ungenutzte Assets. Unter src/sagat-items/public/road-signs/ liegen 14 Verkehrszeichen, die von keiner Komponente referenziert werden — vermutlich für ein geplantes Level-1-Item; ungeklärt.