21 KiB
CODEX.md — Forza Telemetry Tuning Analyzer
Projektziel
Baue eine webbasierte Anwendung zur Analyse von Forza-Telemetriedaten über UDP Data Out.
Die Anwendung soll lokal testbar sein, später aber so strukturiert werden, dass sie auf einem Server betrieben und mehreren Nutzern zur Verfügung gestellt werden kann.
Primäres Ziel ist nicht nur ein Live-Dashboard, sondern ein Tool, das Fahrverhalten und Fahrzeugsetup analysiert und daraus konkrete Setup-Hinweise ableitet.
Kurzbeschreibung
Forza sendet Telemetriedaten per UDP an eine Ziel-IP und einen Ziel-Port. Die Anwendung soll diese Daten empfangen, parsen, speichern, visualisieren und analysieren.
Die Weboberfläche soll Nutzern erlauben:
- Live-Telemetrie anzuzeigen
- Sessions aufzuzeichnen
- Runden und Stints auszuwerten
- Fahrzeug-Setup manuell zu erfassen
- wiederkehrende Fahrverhaltensprobleme zu erkennen
- Setup-Tipps auf Basis von Telemetrie + Setup zu erhalten
- Sessions zu vergleichen
Zielplattform
MVP
Lokaler Betrieb auf einem Gaming-PC oder im lokalen Netzwerk.
Beispiel:
Forza → UDP → Local Backend → Web UI im Browser
Spätere Server-Variante
Serverbetrieb für mehrere Nutzer.
Da Forza UDP-Daten normalerweise aus dem lokalen Netzwerk sendet, soll die Architektur später einen lokalen Agenten unterstützen:
Forza → UDP → Local Agent → WebSocket/HTTPS → Server → Web UI
Der Server soll Sessions speichern, Nutzer verwalten und Analysen bereitstellen.
Technologievorschlag
Backend
Nutze bevorzugt:
- Python 3.12+
- FastAPI
- WebSockets
- asyncio UDP Receiver
- SQLAlchemy oder SQLModel
- SQLite für MVP
- PostgreSQL für Serverbetrieb
- Pydantic für Datenmodelle
- Alembic für Migrationen
Frontend
Nutze bevorzugt:
- React
- TypeScript
- Vite
- Tailwind CSS
- Recharts oder uPlot für Charts
- WebSocket Client für Live-Daten
Deployment
Für den MVP:
- Docker Compose
- Backend Container
- Frontend Container oder statische Auslieferung über Backend
- SQLite Volume
Für spätere Server-Version:
- PostgreSQL
- Reverse Proxy
- TLS via nginx / Caddy / Traefik
- optional Redis für Live-State und Queues
Projektstruktur
Empfohlene Struktur:
forza-telemetry-analyzer/
├─ CODEX.md
├─ README.md
├─ docker-compose.yml
├─ .env.example
├─ backend/
│ ├─ pyproject.toml
│ ├─ app/
│ │ ├─ main.py
│ │ ├─ config.py
│ │ ├─ telemetry/
│ │ │ ├─ udp_receiver.py
│ │ │ ├─ parser.py
│ │ │ ├─ models.py
│ │ │ ├─ formats.py
│ │ │ └─ normalizer.py
│ │ ├─ analysis/
│ │ │ ├─ rules.py
│ │ │ ├─ handling.py
│ │ │ ├─ tires.py
│ │ │ ├─ suspension.py
│ │ │ ├─ gearing.py
│ │ │ ├─ braking.py
│ │ │ └─ advisor.py
│ │ ├─ sessions/
│ │ │ ├─ router.py
│ │ │ ├─ service.py
│ │ │ └─ repository.py
│ │ ├─ setups/
│ │ │ ├─ router.py
│ │ │ ├─ service.py
│ │ │ └─ schemas.py
│ │ ├─ db/
│ │ │ ├─ database.py
│ │ │ └─ models.py
│ │ └─ websocket/
│ │ └─ manager.py
│ └─ tests/
├─ frontend/
│ ├─ package.json
│ ├─ index.html
│ └─ src/
│ ├─ main.tsx
│ ├─ App.tsx
│ ├─ api/
│ ├─ components/
│ ├─ pages/
│ ├─ telemetry/
│ ├─ sessions/
│ ├─ setup/
│ └─ analysis/
└─ docs/
├─ telemetry-format.md
├─ analysis-rules.md
└─ server-architecture.md
Funktionsumfang MVP
Baue zuerst einen funktionalen MVP. Keine unnötige Komplexität.
Muss-Funktionen
- UDP-Telemetrie empfangen
- Telemetriepakete parsen
- Live-Daten per WebSocket an das Frontend senden
- Live-Dashboard anzeigen
- Recording starten/stoppen
- Session speichern
- Session-Liste anzeigen
- Session öffnen und Charts anzeigen
- Manuelle Fahrzeug-/Setup-Eingabe
- Erste Analyse-Regeln ausführen
- Setup-Hinweise anzeigen
Kein Muss im MVP
- Nutzerverwaltung
- Cloud-Sync
- Public Sharing
- KI-Chat
- automatische Streckenerkennung
- perfekte Runden-Segmentierung
- mobile App
- Multiplayer/Team-Funktionen
Backend-Anforderungen
FastAPI App
Die Backend-App soll folgende Bereiche haben:
- Health Check
- UDP Receiver Lifecycle
- WebSocket Live Feed
- Session API
- Setup API
- Analysis API
Beispiel-Endpunkte:
GET /api/health
POST /api/telemetry/start
POST /api/telemetry/stop
GET /api/telemetry/status
WS /ws/live
POST /api/sessions
GET /api/sessions
GET /api/sessions/{session_id}
DELETE /api/sessions/{session_id}
POST /api/sessions/{session_id}/setup
GET /api/sessions/{session_id}/setup
POST /api/sessions/{session_id}/analyze
GET /api/sessions/{session_id}/analysis
UDP Receiver
Implementiere einen asynchronen UDP Receiver.
Konfigurierbare Werte:
FORZA_UDP_HOST=0.0.0.0
FORZA_UDP_PORT=5685
FORZA_PACKET_FORMAT=dash
Der Receiver soll:
- UDP-Pakete empfangen
- Paketlänge prüfen
- Paketformat erkennen oder anhand Config parsen
- ungültige Pakete ignorieren, aber zählen
- aktuelle Telemetrie normalisieren
- Live-Daten an WebSocket Clients senden
- bei aktivem Recording Daten speichern
Parser
Implementiere den Parser modular.
Wichtig:
- keine Parser-Logik direkt im UDP Receiver
- Paketformat in separaten Dateien definieren
- Binary Parsing sauber kapseln
- Little Endian beachten
- Tests mit Beispielpaketen ermöglichen
Parser-Dateien:
backend/app/telemetry/parser.py
backend/app/telemetry/formats.py
backend/app/telemetry/models.py
Der Parser soll zunächst mindestens die bekannten Forza-Dash-Felder abbilden, soweit verfügbar.
Normalisiertes Telemetrie-Modell
Erzeuge aus den Rohdaten ein normalisiertes Modell, das das Frontend und die Analyse nutzen.
Beispiel:
class TelemetryFrame(BaseModel):
timestamp_ms: int
is_race_on: bool
engine_max_rpm: float | None
engine_idle_rpm: float | None
current_engine_rpm: float | None
speed_mps: float | None
speed_kmh: float | None
acceleration_x: float | None
acceleration_y: float | None
acceleration_z: float | None
velocity_x: float | None
velocity_y: float | None
velocity_z: float | None
angular_velocity_x: float | None
angular_velocity_y: float | None
angular_velocity_z: float | None
yaw: float | None
pitch: float | None
roll: float | None
normalized_suspension_travel_front_left: float | None
normalized_suspension_travel_front_right: float | None
normalized_suspension_travel_rear_left: float | None
normalized_suspension_travel_rear_right: float | None
tire_slip_ratio_front_left: float | None
tire_slip_ratio_front_right: float | None
tire_slip_ratio_rear_left: float | None
tire_slip_ratio_rear_right: float | None
tire_slip_angle_front_left: float | None
tire_slip_angle_front_right: float | None
tire_slip_angle_rear_left: float | None
tire_slip_angle_rear_right: float | None
tire_combined_slip_front_left: float | None
tire_combined_slip_front_right: float | None
tire_combined_slip_rear_left: float | None
tire_combined_slip_rear_right: float | None
tire_temp_front_left: float | None
tire_temp_front_right: float | None
tire_temp_rear_left: float | None
tire_temp_rear_right: float | None
wheel_rotation_speed_front_left: float | None
wheel_rotation_speed_front_right: float | None
wheel_rotation_speed_rear_left: float | None
wheel_rotation_speed_rear_right: float | None
throttle: float | None
brake: float | None
clutch: float | None
handbrake: float | None
gear: int | None
steer: float | None
power: float | None
torque: float | None
boost: float | None
lap_number: int | None
current_lap_time_ms: int | None
last_lap_time_ms: int | None
best_lap_time_ms: int | None
race_position: int | None
car_id: int | None
car_class: int | None
car_performance_index: int | None
drivetrain_type: str | None
num_cylinders: int | None
Datenbankmodell
Für den MVP reicht SQLite.
Tabellen:
sessions
id
name
game
track_name nullable
car_name nullable
car_id nullable
car_class nullable
performance_index nullable
drivetrain_type nullable
created_at
started_at
ended_at
notes nullable
telemetry_frames
id
session_id
timestamp_ms
frame_index
raw_json
normalized_json
speed_kmh
rpm
gear
throttle
brake
steer
lap_number
current_lap_time_ms
created_at
Wichtig: Speichere nicht nur einzelne extrahierte Felder, sondern auch das normalisierte Frame als JSON. Das erleichtert spätere Analyseänderungen ohne Datenverlust.
vehicle_setups
id
session_id
car_name
drivetrain_type
tire_pressure_front
tire_pressure_rear
gear_final_drive
gear_1
gear_2
gear_3
gear_4
gear_5
gear_6
gear_7
gear_8
gear_9
gear_10
camber_front
camber_rear
toe_front
toe_rear
caster
antiroll_front
antiroll_rear
springs_front
springs_rear
ride_height_front
ride_height_rear
rebound_front
rebound_rear
bump_front
bump_rear
aero_front
aero_rear
brake_balance
brake_pressure
front_diff_accel
front_diff_decel
rear_diff_accel
rear_diff_decel
center_diff_balance
created_at
updated_at
analysis_results
id
session_id
created_at
summary_json
findings_json
recommendations_json
Frontend-Anforderungen
Seiten
Baue diese Seiten:
/
Dashboard
/sessions
Session-Liste
/sessions/:id
Session-Detail mit Charts und Analyse
/sessions/:id/setup
Setup-Eingabe
/sessions/:id/analysis
Analysebericht
/settings
UDP-Port, Paketformat, Anzeigeoptionen
Dashboard
Das Dashboard soll anzeigen:
- Verbindungsstatus
- Recording-Status
- aktuelle Geschwindigkeit
- RPM
- Gang
- Gas
- Bremse
- Lenkung
- G-Kräfte
- Reifentemperaturen
- Reifen-Slip
- Federweg
- aktuelle Runde
- letzte Runde
- beste Runde
Session-Ansicht
Charts für:
- Geschwindigkeit über Zeit
- RPM über Zeit
- Gang über Zeit
- Gas/Bremse/Lenkung über Zeit
- Reifen-Combined-Slip je Rad
- Reifen-Temperatur je Rad
- Federweg je Rad
- Querbeschleunigung
- Längsbeschleunigung
Keine überladenen Monster-Charts. Lieber mehrere klare Diagramme.
Analysebericht
Der Analysebericht soll gegliedert sein:
- Executive Summary
- Handling
- Reifen
- Fahrwerk
- Getriebe
- Bremsen
- Priorisierte Setup-Vorschläge
- Hinweise zur Aussagekraft
Jeder Befund soll enthalten:
Titel
Kategorie
Schweregrad: info | low | medium | high
Confidence: 0–100 %
Beobachtung
Telemetrie-Begründung
Empfohlene Änderung
Alternative Ursache
Analyse-Engine
Die Analyse soll regelbasiert starten. Keine Blackbox.
Grundprinzip
Jede Regel bekommt:
- Liste von TelemetryFrames
- optional VehicleSetup
- abgeleitete Metriken
- Kontext wie Drivetrain Type
Jede Regel gibt Findings zurück.
Beispiel:
class Finding(BaseModel):
category: str
title: str
severity: str
confidence: float
observation: str
evidence: list[str]
recommendation: str
alternative_causes: list[str]
Abgeleitete Metriken
Berechne vor den Regeln zusätzliche Werte:
front_combined_slip_avg
rear_combined_slip_avg
front_slip_angle_avg
rear_slip_angle_avg
front_tire_temp_avg
rear_tire_temp_avg
left_tire_temp_avg
right_tire_temp_avg
suspension_front_avg
suspension_rear_avg
steer_abs
throttle_normalized
brake_normalized
rpm_ratio
speed_delta
yaw_rate
lateral_g
longitudinal_g
Kurvenphasen
Die Analyse soll versuchen, Fahrsituationen zu klassifizieren:
straight
braking
turn_in
mid_corner
corner_exit
acceleration
unknown
Heuristik:
braking:
brake > 0.25 and speed_kmh > 40
turn_in:
steer_abs > 0.25 and brake <= 0.40 and throttle < 0.30
mid_corner:
steer_abs > 0.35 and throttle between 0.05 and 0.55 and brake < 0.25
corner_exit:
steer_abs > 0.20 and throttle > 0.45
straight:
steer_abs < 0.10 and brake < 0.10
Beispiel-Regeln
Entry Understeer
Bedingung:
phase in [turn_in]
steer_abs > 0.55
front_combined_slip_avg > rear_combined_slip_avg + 0.10
yaw_response_low == true
speed_kmh > 50
Empfehlungen:
- Front-Stabi 1–2 Klicks weicher testen
- alternativ Heck-Stabi 1 Klick härter
- bei starkem Bremsanteil: Bremsbalance 1–2 % nach hinten testen
- bei FWD vorsichtig mit zu viel Gas am Kurveneingang
Mid-Corner Understeer
Bedingung:
phase == mid_corner
steer_abs > 0.50
front_combined_slip_avg > rear_combined_slip_avg + 0.12
throttle between 0.10 and 0.50
Empfehlungen:
- Front-Reifendruck leicht senken, falls Fronttemperatur niedrig/uneinheitlich
- Front-Stabi weicher oder Heck-Stabi härter
- Front-Camber prüfen
- Aero vorn erhöhen, falls verfügbar und bei schnellen Kurven relevant
Power Oversteer
Bedingung:
phase == corner_exit
throttle > 0.65
rear_combined_slip_avg > front_combined_slip_avg + 0.18
rear_slip_angle_avg deutlich erhöht
Empfehlungen:
- Rear Diff Accel reduzieren
- längere Übersetzung für kritischen Gang testen
- Rear-Reifendruck prüfen
- Heck-Stabi weicher oder Front-Stabi härter
- Gasprogression weicher fahren
Exit Understeer AWD/FWD
Bedingung:
phase == corner_exit
throttle > 0.60
front_combined_slip_avg > rear_combined_slip_avg + 0.15
steer_abs > 0.45
Empfehlungen:
- bei AWD: Center Diff mehr nach hinten
- Front Diff Accel reduzieren
- Front-Stabi weicher oder Heck-Stabi härter
- später/vorsichtiger Vollgas geben
Bottoming Out
Bedingung:
normalized_suspension_travel_* repeatedly near 0.0 or 1.0
speed_kmh > 80
Empfehlungen:
- Ride Height erhöhen
- Federrate erhöhen
- Bump-Dämpfung prüfen
- bei Aero-Fahrzeugen: Aero-Balance prüfen
Zu kurze Übersetzung
Bedingung:
rpm_ratio > 0.97
throttle > 0.75
speed_delta low
same_gear_duration > 0.5s
Empfehlungen:
- betroffenen Gang verlängern
- Final Drive länger stellen, falls mehrere Gänge betroffen sind
- Ziel: am Ende langer Geraden knapp unter Max RPM liegen
Zu lange Übersetzung
Bedingung:
throttle > 0.80
rpm_ratio < 0.55
acceleration low
gear high
Empfehlungen:
- Gang kürzer
- Final Drive kürzer
- Ganganschluss prüfen
Bremsinstabilität
Bedingung:
phase == braking
brake > 0.60
rear_combined_slip_avg > front_combined_slip_avg + 0.12
yaw_rate rising
Empfehlungen:
- Bremsbalance nach vorne
- Bremsdruck reduzieren
- Rear Decel Diff prüfen
- Heck-Stabi weicher testen
Trailbrake Understeer
Bedingung:
brake between 0.15 and 0.50
steer_abs > 0.45
front_combined_slip_avg high
yaw_response_low == true
Empfehlungen:
- Bremsbalance leicht nach hinten testen
- Bremse früher lösen
- Front-Stabi weicher
- Front-Reifendruck prüfen
Setup Advisor Logik
Der Advisor darf keine absoluten Garantien formulieren.
Gute Formulierungen:
Teste Front-Stabi 1–2 Klicks weicher.
Wenn das Auto dadurch am Kurvenausgang instabil wird, Änderung zurücknehmen und stattdessen Heck-Stabi 1 Klick härter testen.
Schlechte Formulierungen:
Stelle Front-Stabi auf exakt 23.4.
Jede Empfehlung braucht:
- Symptom
- Begründung
- Setup-Hebel
- mögliche Nebenwirkung
- Testvorschlag
Nutzerführung
Die Anwendung soll Nutzer nicht mit Daten erschlagen.
Priorisiere:
- Was ist das größte Problem?
- Wo tritt es auf?
- Woran erkennt das Tool das?
- Was soll ich zuerst ändern?
- Wie teste ich, ob die Änderung geholfen hat?
Sprache
UI und Texte zunächst auf Deutsch.
Code, Variablen und interne Namen auf Englisch.
Design-Richtung
Modernes, technisches Webinterface.
Stil:
- dunkles Dashboard bevorzugt
- klare Karten
- kompakte Zahlenanzeigen
- gute Chart-Lesbarkeit
- nicht verspielt
- kein Gaming-Kitsch
- keine überladenen Neon-Effekte
- responsive, aber Desktop-first
Wichtige UX-Details
Recording Flow
Der Nutzer soll klar sehen:
UDP verbunden / nicht verbunden
Pakete pro Sekunde
letztes Paket vor X ms
Recording aktiv / inaktiv
aktuelle Session
Buttons:
Start Recording
Stop Recording
Discard
Save Session
Analyze Session
Setup-Eingabe
Setup-Eingabe als strukturierte Tabs:
Reifen
Übersetzung
Ausrichtung
Stabis
Federn
Dämpfer
Aero
Bremsen
Differenzial
Erlaube leere Werte. Nicht jedes Fahrzeug hat alle Tuningoptionen.
Analysebericht
Der Bericht soll priorisiert sein.
Beispiel:
Höchste Priorität:
1. Power-Oversteer am Kurvenausgang
2. 4. Gang zu kurz
3. Frontreifen überlastet in langen Kurven
Geringere Priorität:
4. leichter Temperaturunterschied links/rechts
Serverfähigkeit
Schon im MVP so bauen, dass spätere Serverfähigkeit nicht blockiert wird.
Für später vorbereiten
- klare Trennung zwischen UDP Receiver und Web-App
- Session gehört perspektivisch zu User
- Local Agent als eigenes Modul denkbar
- API nicht hart an localhost koppeln
- Auth später ergänzbar
- Datenbankzugriff abstrahieren
Spätere Architektur
Local Agent:
- empfängt UDP von Forza
- normalisiert Telemetrie
- sendet Frames per WebSocket/HTTPS an Server
- optional lokaler Cache
Server:
- Nutzerverwaltung
- Session Storage
- Analyse
- Web UI
- Team-/Sharing-Funktion
Für den MVP darf der Backend-Server UDP direkt empfangen.
Sicherheit
Für lokalen MVP simpel halten.
Für Serverbetrieb später beachten:
- Authentifizierung
- Nutzertrennung
- Rate Limits
- keine offenen UDP-Ports ins Internet als Pflicht
- Local Agent bevorzugen
- HTTPS
- CORS sauber konfigurieren
- keine beliebigen File Uploads im MVP
Performance
Telemetrie kann viele Frames pro Sekunde erzeugen.
Daher:
- nicht jedes Frame einzeln synchron in die Datenbank schreiben
- Buffer verwenden
- Batch Inserts
- Live-Frontend throttlen, z. B. 10–20 FPS
- Rohdaten optional speichern, aber nicht UI mit 60 FPS erzwingen
- Session-Charts downsamplen
Testing
Erstelle Tests für:
- Parser mit Beispiel-Binary-Payloads
- Normalizer
- Analyse-Regeln
- API-Endpunkte
- WebSocket-Verbindung
- Session-Speicherung
Erstelle zusätzlich einen Fake-Telemetry-Generator:
backend/app/dev/fake_telemetry_sender.py
Dieser soll UDP-Pakete oder normalisierte Frames simulieren, damit Frontend und Backend ohne laufendes Forza getestet werden können.
Akzeptanzkriterien MVP
Der MVP gilt als fertig, wenn:
- Backend startet per Docker Compose
- Frontend ist im Browser erreichbar
- UDP Receiver kann gestartet werden
- Live-Dashboard zeigt eingehende Frames
- Recording kann gestartet und beendet werden
- Session wird gespeichert
- Session kann erneut geöffnet werden
- Charts zeigen Telemetriedaten
- Setup kann zur Session gespeichert werden
- Analyse erzeugt mindestens 5 Typen von Findings:
- Understeer
- Oversteer
- Gearing Issue
- Braking Instability
- Suspension Bottoming
- Empfehlungen werden verständlich auf Deutsch angezeigt
Entwicklungsreihenfolge
Arbeite in dieser Reihenfolge:
Schritt 1
Projektstruktur erstellen.
Schritt 2
FastAPI Backend mit Health Check und Config.
Schritt 3
UDP Receiver mit Logging.
Schritt 4
Parser-Schnittstelle und Fake-Parser, falls echtes Format noch nicht vollständig implementiert ist.
Schritt 5
WebSocket Live Feed.
Schritt 6
React Frontend mit Dashboard.
Schritt 7
Recording und Session Storage.
Schritt 8
Session-Liste und Session-Detailseite.
Schritt 9
Setup-Formular.
Schritt 10
Analyse-Engine mit ersten Regeln.
Schritt 11
Analysebericht UI.
Schritt 12
Docker Compose und README.
Wichtige Implementierungsregeln
- Schreibe klaren, wartbaren Code.
- Keine riesigen Dateien.
- Business-Logik nicht in UI-Komponenten verstecken.
- Analyse-Regeln modular halten.
- Datenmodelle typisieren.
- Frontend-Komponenten wiederverwendbar bauen.
- Fehler im UI sichtbar machen.
- Backend-Logs verständlich halten.
- Konfiguration über
.env. - Keine Secrets committen.
- Schreibe README mit Setup-Anleitung.
Beispiel README-Anleitung
Die README soll mindestens erklären:
1. Voraussetzungen
2. Start mit Docker Compose
3. UDP-Port konfigurieren
4. Forza Data Out konfigurieren
5. Live-Dashboard öffnen
6. Session aufnehmen
7. Analyse ausführen
8. Bekannte Limitierungen
Bekannte Limitierungen
Diese Punkte in README und UI transparent machen:
- Das Tool kennt das Fahrzeugsetup nur, wenn der Nutzer es eingibt.
- Setup-Tipps sind heuristisch, keine perfekte Simulation.
- Reifentemperaturen je Rad reichen nicht für perfekte Camber-Diagnose.
- Manche Probleme können durch Fahrstil statt Setup entstehen.
- Game-Version und Paketformat können sich ändern.
- FH6-Unterstützung muss anhand realer Pakete validiert werden.
Codex-Arbeitsweise
Wenn Informationen fehlen:
- eine sinnvolle Annahme treffen
- diese Annahme im Code oder README dokumentieren
- den MVP nicht blockieren
- Parser und Formate so bauen, dass Anpassungen leicht möglich sind
Wenn echte Telemetriedaten noch nicht vorliegen:
- mit Fake-Telemetrie weiterarbeiten
- Parser-Schnittstelle vorbereiten
- Sample-Payloads später ergänzbar machen
Ergebnis
Erzeuge am Ende ein lauffähiges Projekt mit:
- Backend
- Frontend
- Docker Compose
- README
- Beispiel
.env - Fake Telemetry Sender
- ersten Analyse-Regeln
- deutscher UI
- klarer Struktur für spätere Server-/Multi-User-Version