# simulator.py — Driveline & ECU-State from __future__ import annotations import threading import time from dataclasses import dataclass @dataclass class DrivelineModel: idle_rpm: int = 1400 max_rpm: int = 9500 kmh_per_krpm: tuple = (0.0, 12.0, 19.0, 25.0, 32.0, 38.0, 45.0) rpm_rise_per_s: int = 5000 rpm_fall_per_s: int = 3500 def target_rpm_from_throttle(self, throttle_pct: int) -> int: t = max(0, min(100, throttle_pct)) / 100.0 return int(self.idle_rpm + t * (self.max_rpm - self.idle_rpm)) def speed_from_rpm_gear(self, rpm: int, gear: int) -> float: if gear <= 0: return 0.0 k = self.kmh_per_krpm[min(gear, len(self.kmh_per_krpm) - 1)] return (rpm / 1000.0) * k class EcuState: """Thread-sichere Zustandsmaschine (Gang, Gas, RPM, Speed).""" def __init__(self, model: DrivelineModel | None = None) -> None: self.model = model or DrivelineModel() self._lock = threading.Lock() self._gear = 0 self._throttle = 0 self._rpm = self.model.idle_rpm self._speed = 0.0 self._last = time.monotonic() def set_gear(self, gear: int) -> None: with self._lock: self._gear = max(0, min(6, int(gear))) def set_throttle(self, thr: int) -> None: with self._lock: self._throttle = max(0, min(100, int(thr))) def snapshot(self) -> tuple[int, int, int, float]: with self._lock: return self._gear, self._throttle, self._rpm, self._speed def update(self) -> None: now = time.monotonic() dt = max(0.0, min(0.1, now - self._last)) self._last = now with self._lock: target = self.model.target_rpm_from_throttle(self._throttle) if self._rpm < target: self._rpm = min(self._rpm + int(self.model.rpm_rise_per_s * dt), target) else: self._rpm = max(self._rpm - int(self.model.rpm_fall_per_s * dt), target) min_idle = 800 if self._gear == 0 and self._throttle == 0 else self.model.idle_rpm self._rpm = max(min_idle, min(self._rpm, self.model.max_rpm)) target_speed = self.model.speed_from_rpm_gear(self._rpm, self._gear) alpha = min(1.0, 4.0 * dt) if self._gear == 0: target_speed = 0.0 self._speed = (1 - alpha) * self._speed + alpha * target_speed self._speed = max(0.0, min(self._speed, 299.0))