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 ofTelemetryPointobjects plus metadata (lap time, validity flag, penalty count, lap time source).LapLengthMetersis taken from the last point's distance. -
SessionData— container holding all laps for a session plusTelemetrySessionMetadata(track, vehicle, weather, recorded timestamp).BestValidLapreturns the fastest valid, timing-eligible lap. -
AnalysisEngine— staticAnalyze(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 anAnalysisResultwith rankedAnalysisInsight[],TrackSegment[], and the fullResampledLapComparisonfor plotting. -
LapResampler— staticResample(refLap, currentLap, options). Interpolates both laps to N distance points (default 512), computes cumulative time along distance, produces point-wise Δ (current − reference). TheResampledLapComparisonfeeds all four ScottPlot curves (Speed, Throttle, Brake, Delta). -
TrackSegmentation— staticBuildSegments(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), buildsLapobjects 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. Containssession_main.csv, optionalsession_pilot_b.csv, andmanifest.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, buffersTelemetryPoint[]per lap, emitsLapCompletedevents. ExposesTrySample()for non-blocking live HUD reads. -
LmuSharedMemorySampler— thin wrapper aroundRF2MemoryReader(rF2SharedMemoryNet 1.0.4). Opens shared memory withenableDMA=truefor 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 anyIGameLiveSampler, accumulates samples intoLapobjects using the same lap-counter detection logic asLmuLiveRecorder. -
GameSamplerFactory— createsIGameLiveSamplerinstances byGameSourceId.
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 toNavigationService.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 (viaLmuLiveRecorder), AC Classic UDP (viaAssettoCorsaClassicLiveSampler), and all other games (viaGenericLiveRecorderper 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 inSessionCatalogStore. -
SyncService— pollsSessionCatalogStore.ListUnsynced()and uploads each unsynced session toPOST /v1/sessions. On success, callsMarkSynced()with the cloud session ID. -
AIService— thin client forPOST /v1/ai/insights(one-shot coaching) andPOST /v1/ai/chat(conversational). Injects JWT fromAuthService. -
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 fromSessionCatalogStore.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 achievementsGET /v1/achievements/catalog— all available achievementsGET /v1/tracks— distinct tracks with uploaded sessionsGET/PATCH /v1/profile— user profile (display name, avatar)GET /health— liveness probe
Background Services¶
-
OutboxDispatcher— pollsoutbox_messagesevery 5 seconds. Dispatchessession.uploadedto FeedService and AchievementService;leaderboard.submitto AchievementService. Retries up to 5 times with error logging. -
TrackProcessingService— pollstrack_processing_jobsevery 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 aTrackVersionwhen 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 payloadAiInsightRequestDto/AiChatRequestDto/AiChatResponseDto— AI coaching contractsLeaderboardSubmitDto— leaderboard submissionUserProfileDto— profileApiResponse<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.