67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
# 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))
|