Skip to content

Component Map

Module Reference

SimCopilot.Core

The analysis engine. Zero external dependencies — no UI, no disk, no network.

Key types:

  • TelemetryPoint — immutable data unit, keyed by track distance. Fields: Distance (m), Time (s), Speed (km/h), Throttle/Brake (0–1), Steering (°), Gear, X/Y world position, optional game-specific extras.

  • Lap — ordered sequence of TelemetryPoint objects plus metadata (lap time, validity flag, penalty count, lap time source). LapLengthMeters is taken from the last point's distance.

  • SessionData — container holding all laps for a session plus TelemetrySessionMetadata (track, vehicle, weather, recorded timestamp). BestValidLap returns the fastest valid, timing-eligible lap.

  • AnalysisEngine — static Analyze(session, refLap, currentLap, options). Compares two laps corner-by-corner after resampling and detects: brake too early, slow corner exit, late throttle application, sector delta loss/gain, lap time variance, throttle input stability. Returns an AnalysisResult with ranked AnalysisInsight[], TrackSegment[], and the full ResampledLapComparison for plotting.

  • LapResampler — static Resample(refLap, currentLap, options). Interpolates both laps to N distance points (default 512), computes cumulative time along distance, produces point-wise Δ (current − reference). The ResampledLapComparison feeds all four ScottPlot curves (Speed, Throttle, Brake, Delta).

  • TrackSegmentation — static BuildSegments(lap, options). Two-threshold hysteresis on smoothed steering angle to detect corners. Output: TrackSegment[] with kind (Corner/Straight), ordinal, and distance bounds. Used by AnalysisEngine and the Segments tab.

  • AnalysisOptions — tunable parameters: sample count, steering thresholds, brake timing tolerance, minimum speed for delta accumulation.


SimCopilot.Storage

Persistence. No UI dependencies.

  • SessionCatalogStore — SQLite at %LocalAppData%\ApexLab\sessions.db. Stores session metadata (track, vehicle, best lap time, file path, sync status, cloud session ID). Key queries: recent sessions, search by text/game/track/vehicle, personal best per venue, unsynced sessions for background upload.

  • SessionBlobStorage — path utilities for CSV session files under %LocalAppData%\ApexLab\Sessions\.

  • TrackProfileStore — SQLite at %LocalAppData%\ApexLab\track_profiles.db. Maps track slugs to geometry hashes for SVG cache invalidation.

  • TrackBoundaryCache — local cache of OSM track outlines (WGS84 GeoJSON) under %LocalAppData%\ApexLab\track-outlines\. Built-in fallbacks for Spa, Monza, Silverstone.

  • CoachMemoryStore — SQLite at %LocalAppData%\ApexLab\coach.db. Persists AI coaching conversation context across sessions.


SimCopilot.Telemetry

Telemetry file I/O.

  • CsvTelemetryReader — async reader for the internal CSV format (spec: telemetry-format.txt). Parses comment-header metadata (Track, Vehicle, GameSource, weather), builds Lap objects on lap-counter changes, derives lap times from OfficialLapTime column, time delta, or speed/distance estimation as fallback.

  • CsvTelemetryWriter — writes sessions back to CSV, including all optional columns.

  • SharePackExporter/Importer — ZIP archive format for sharing sessions with another driver. Contains session_main.csv, optional session_pilot_b.csv, and manifest.json (track, vehicle, lap count, notes). Not yet exposed in the V1 UI.


SimCopilot.Lmu

Le Mans Ultimate / rFactor 2 shared memory integration. Windows-only.

  • LmuLiveRecorder — polls rF2 shared memory at 40 Hz (25ms interval). Detects lap counter advances, buffers TelemetryPoint[] per lap, emits LapCompleted events. Exposes TrySample() for non-blocking live HUD reads.

  • LmuSharedMemorySampler — thin wrapper around RF2MemoryReader (rF2SharedMemoryNet 1.0.4). Opens shared memory with enableDMA=true for extended LMU data.

  • Supporting utilities: LmuAiwExtractor (AI setup extraction), LmuInstallLocator, LmuPluginDetector (SMMP plugin check), SimSessionPhaseMapper.


SimCopilot.Connectors

Multi-game abstraction layer.

  • ConnectorCatalog — static registry of 8 supported simulators: LMU/rF2 (Stable), AC Classic (Beta, UDP), ACC (Beta, SHM), iRacing (Beta), F1 Codemasters (Beta, UDP), Automobilista 2 (Beta, SHM), Forza (Beta, UDP), FileImport (Internal).

  • IGameLiveSampler — abstraction for a live connector: Connect(), Disconnect(), TryPeekLive(out GameLiveSample), IsConnected. Each game gets one concrete implementation.

  • GenericLiveRecorder — wraps any IGameLiveSampler, accumulates samples into Lap objects using the same lap-counter detection logic as LmuLiveRecorder.

  • GameSamplerFactory — creates IGameLiveSampler instances by GameSourceId.


SimCopilot.App

WPF desktop client. CommunityToolkit.Mvvm, MVVM pattern, feature-based ViewModels.

Shell & Navigation

  • ShellWindow — main window frame. Holds the nav rail and content area.
  • ShellViewModel — authentication state, navigation commands (Home, Sessions, Leaderboards, My Pilot, Settings). Delegates to NavigationService.
  • LoginWindow — OAuth PKCE login UI.

State Containers (singletons)

Container Holds
SessionState Currently loaded SessionData
SelectionState Reference lap, current lap, favorite session
AnalysisState Latest AnalysisResult (insights, segments, comparison)
AnalysisPipeline Reactive orchestrator — re-runs analysis on lap selection change

Services (singletons)

  • AuthService — OAuth PKCE flow against the API. Opens browser, starts local HTTP listener (random port), waits for callback with one-time code, exchanges code for JWT + refresh token. Stores tokens encrypted at %LocalAppData%\ApexLab\auth.dat. Supports Google, Discord, Steam, email/password. Logout() fire-and-forgets a server-side token revocation before clearing local state.

  • LiveCaptureService — owns all live recording state. Separate branches for LMU/rF2 (via LmuLiveRecorder), AC Classic UDP (via AssettoCorsaClassicLiveSampler), and all other games (via GenericLiveRecorder per game). 250ms HUD timer computes real-time delta vs. reference lap. Auto-save triggers on 5 laps or 10s idle. Writes session to CSV, then registers in SessionCatalogStore.

  • SyncService — polls SessionCatalogStore.ListUnsynced() and uploads each unsynced session to POST /v1/sessions. On success, calls MarkSynced() with the cloud session ID.

  • AIService — thin client for POST /v1/ai/insights (one-shot coaching) and POST /v1/ai/chat (conversational). Injects JWT from AuthService.

  • LeaderboardService — fetches and submits leaderboard data.

  • NavigationService — instantiates and displays ViewModels.

Feature ViewModels

  • HomeViewModel — dashboard with recent sessions and live capture status.
  • SessionsViewModel — session library: search, filter (game/track/vehicle), sort. Loads from SessionCatalogStore.
  • LeaderboardsViewModel — ranked lap times per track.
  • MyPilotViewModel — user achievements and stats.
  • SettingsViewModel — app preferences (API URL, language, auto-record thresholds, HUD toggle).

Session Detail (tab ViewModels)

Tab What it shows
Overview Session metadata, lap list, best time
Compare Reference vs. current lap selection; ranked insights from AnalysisEngine
Telemetry ScottPlot curves: Speed, Throttle, Brake, Delta over distance; playback scrubber; cursor sync with track map
Segments Corner-by-corner breakdown: speed/brake/throttle per segment vs. reference; problem area ranking
Coach Conversational AI coaching via Claude API

Other UI components

  • HudWindow — overlay showing live delta during recording.
  • GameSourcesWindow — configure active connectors.
  • OnboardingWindow — first-run setup.
  • I18n.cs — 1000+ French UI strings (all user-facing text is in French).

SimCopilot.Api

ASP.NET Core 8 REST API. Deployed on Railway. Neon Postgres + Redis.

Endpoints

Auth (/v1/auth)

Method Route Purpose
GET /login/{provider} Initiate OAuth PKCE (google, discord, steam)
GET /callback/{provider} OAuth redirect handler; issues one-time code
POST /exchange Exchange code for JWT + refresh token
POST /refresh Rotate refresh token
POST /logout Revoke refresh token
POST /register Email/password registration
POST /login/email Email/password login

Access tokens expire in 60 minutes. Refresh tokens last 30 days and rotate on use (replay attempts revoke the token family). Refresh tokens are stored as BCrypt hashes, never in plaintext.

Sessions (/v1/sessions, requires JWT)

Method Route Purpose
POST / Upload session summary (no raw telemetry)

Idempotent on (userId, clientSessionId). Creates Session + Lap rows, optionally enqueues a track processing job, and emits a session.uploaded outbox event.

Leaderboards (/v1/leaderboards)

Method Route Purpose
GET / Ranked lap times for a track (cached in Redis)
POST /submit Submit a lap for ranking

One entry per (Track, User) — new submission replaces if faster. Quality score ≥ 0.8 required. Emits leaderboard.submit outbox event for achievement checking.

AI Coach (/v1/ai, requires JWT)

Method Route Purpose
POST /insights One-shot coaching — hash-cached in Redis, quota-gated
POST /chat Multi-turn conversational coaching — quota-gated, never cached

Implemented via Ai/ subsystem: ILlmProvider (Claude or OpenAI, config-driven via Llm:Provider), InsightCache (Redis, 24h TTL), UsageTracker (Postgres user_ai_usage, daily cap via Llm:DailyRequestCap). Cache hit rate and token usage visible in SafeWatch dashboard via AiEvent table.

Other endpoints (all require JWT)

  • GET/POST /v1/feed — activity stream (session uploads, leaderboard submissions, achievements)
  • GET /v1/achievements — user achievements
  • GET /v1/achievements/catalog — all available achievements
  • GET /v1/tracks — distinct tracks with uploaded sessions
  • GET/PATCH /v1/profile — user profile (display name, avatar)
  • GET /health — liveness probe

Background Services

  • OutboxDispatcher — polls outbox_messages every 5 seconds. Dispatches session.uploaded to FeedService and AchievementService; leaderboard.submit to AchievementService. Retries up to 5 times with error logging.

  • TrackProcessingService — polls track_processing_jobs every 3 seconds. For each job: normalizes track points via PCA, aligns with prior versions via Procrustes 2D, resamples to 1000 waypoints, fuses with running weighted average, computes confidence, freezes a TrackVersion when drift < 0.5m over 10 submissions. Infrastructure for Phase 2 canonical track registry.

  • DataRetentionService — periodic cleanup of old sessions (configurable retention window).

Data Model

12 Postgres tables:

Table Purpose
users Accounts (OAuth + email/password)
sessions Uploaded session summaries
laps Lap rows with quality scores (coherence, stability, completeness)
tracks Canonical track records
track_versions Immutable centerline snapshots (GeoJSON)
leaderboard_entries One per (track, user) — best lap only
user_achievements Grant history
feed_entries Activity stream
refresh_tokens Hashed refresh tokens
outbox_messages Transactional event outbox
track_processing_jobs Durable queue for centerline processing
share_packs Reserved for V2

Key uniqueness constraints: (oauth_provider, oauth_subject) on users; (user_id, client_session_id) on sessions (idempotency); (game_id, track_name, track_layout) on tracks; (track_id, user_id) on leaderboard entries.


SimCopilot.Shared

DTOs shared between the App and API:

  • SessionUploadDto / LapSummaryDto / TrackPointDto — session upload payload
  • AiInsightRequestDto / AiChatRequestDto / AiChatResponseDto — AI coaching contracts
  • LeaderboardSubmitDto — leaderboard submission
  • UserProfileDto — profile
  • ApiResponse<T> — standard envelope

SimCopilot.Tests

30+ xUnit unit tests. Coverage areas: corner detection (TrackSegmentationTests), lap resampling and delta (LapTimeDistanceInterpolationTests), sector threshold logic (AnalysisSectorDeltaThresholdTests), CSV round-trip (CsvTelemetryReaderTests), SharePack (SharePackRoundTripTests), SQLite catalog (SessionCatalogStoreTests), track outline cache (TrackCacheServiceTests), SVG generation (TrackSvgGeneratorTests), quality scoring (TelemetryQualityScorerTests), geometry utilities (PolylineGeometry2DTests, SimilarityTransform2DTests).

UI testing is manual — see Test Checklist.