neraly complete Model of Driving done, but needs tweaking
This commit is contained in:
@@ -1,72 +1,241 @@
|
||||
# =============================
|
||||
# app/simulation/ui/gearbox.py
|
||||
# =============================
|
||||
|
||||
from __future__ import annotations
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from typing import Dict, Any, List
|
||||
from app.simulation.ui import UITab
|
||||
|
||||
from typing import Dict, Any
|
||||
from app.simulation.ui import UITab
|
||||
from app.simulation.modules.gearbox import GEARBOX_DEFAULTS
|
||||
|
||||
class GearboxTab(UITab):
|
||||
NAME = "gearbox"
|
||||
TITLE = "Getriebe"
|
||||
PRIO = 10
|
||||
TITLE = "Getriebe & Antrieb"
|
||||
PRIO = 12
|
||||
|
||||
def __init__(self, parent, sim):
|
||||
self.sim = sim
|
||||
self.frame = ttk.Frame(parent, padding=8)
|
||||
self.frame.columnconfigure(1, weight=1)
|
||||
for c in (0,1,2,3): self.frame.columnconfigure(c, weight=1)
|
||||
|
||||
ttk.Label(self.frame, text="Gänge (inkl. Leerlauf als 0)").grid(row=0, column=0, sticky="w")
|
||||
self.gears_var = tk.IntVar(value=6)
|
||||
ttk.Spinbox(self.frame, from_=1, to=10, textvariable=self.gears_var, width=6, command=self._rebuild_ratios).grid(row=0, column=1, sticky="w")
|
||||
# ---------- Linke Spalte ----------
|
||||
rowL = 0
|
||||
def L(lbl, var=None, w=12, kind="entry", values=None):
|
||||
nonlocal rowL
|
||||
ttk.Label(self.frame, text=lbl).grid(row=rowL, column=0, sticky="w")
|
||||
if kind == "entry":
|
||||
ttk.Entry(self.frame, textvariable=var, width=w).grid(row=rowL, column=1, sticky="w")
|
||||
elif kind == "label":
|
||||
ttk.Label(self.frame, textvariable=var).grid(row=rowL, column=1, sticky="w")
|
||||
elif kind == "combo":
|
||||
ttk.Combobox(self.frame, textvariable=var, state="readonly",
|
||||
values=values or [], width=w).grid(row=rowL, column=1, sticky="w")
|
||||
elif kind == "buttons":
|
||||
f = ttk.Frame(self.frame); f.grid(row=rowL, column=1, sticky="w")
|
||||
ttk.Button(f, text="▼", width=3, command=self.shift_down).pack(side="left", padx=(0,4))
|
||||
ttk.Button(f, text="N", width=3, command=self.set_neutral).pack(side="left", padx=(0,4))
|
||||
ttk.Button(f, text="▲", width=3, command=self.shift_up).pack(side="left")
|
||||
rowL += 1
|
||||
|
||||
self.reverse_var = tk.BooleanVar(value=False)
|
||||
ttk.Checkbutton(self.frame, text="Rückwärtsgang vorhanden", variable=self.reverse_var).grid(row=1, column=0, columnspan=2, sticky="w")
|
||||
# Live/Controls (Labels → werden im _tick() live aktualisiert)
|
||||
self.gear_var = tk.StringVar(); L("Gang", self.gear_var, kind="label")
|
||||
L("Schalten", kind="buttons")
|
||||
self.speed_var = tk.StringVar(); L("Geschwindigkeit [km/h]", self.speed_var, kind="label")
|
||||
self.clutch_v = tk.StringVar(); L("Kupplung [%]", self.clutch_v, kind="label")
|
||||
self.slip_v = tk.StringVar(); L("Reifenschlupf [%]", self.slip_v, kind="label")
|
||||
|
||||
ttk.Label(self.frame, text="km/h pro 1000 RPM je Gang").grid(row=2, column=0, sticky="w", pady=(6,0))
|
||||
self.ratio_frame = ttk.Frame(self.frame); self.ratio_frame.grid(row=3, column=0, columnspan=2, sticky="ew")
|
||||
self.ratio_vars: List[tk.DoubleVar] = []
|
||||
self._rebuild_ratios()
|
||||
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
|
||||
|
||||
ttk.Button(self.frame, text="Anwenden", command=self.apply).grid(row=4, column=0, pady=(8,0), sticky="w")
|
||||
# Kupplung/Automation
|
||||
self.cl_Tmax = tk.DoubleVar(); L("Kupplung Tmax [Nm]", self.cl_Tmax)
|
||||
self.cl_agr = tk.DoubleVar(); L("Aggressivität [0..1]", self.cl_agr)
|
||||
self.cl_curve= tk.StringVar(); L("Kupplungs-Kurve", self.cl_curve, kind="combo",
|
||||
values=["linear","progressive","soft"])
|
||||
self.cl_drag = tk.DoubleVar(); L("Kupplungs-Schlepp [Nm]", self.cl_drag)
|
||||
self.sh_time = tk.DoubleVar(); L("Schaltzeit [s]", self.sh_time)
|
||||
self.sync_rb = tk.DoubleVar(); L("Sync-Band [RPM]", self.sync_rb)
|
||||
|
||||
def _rebuild_ratios(self):
|
||||
for w in self.ratio_frame.winfo_children(): w.destroy()
|
||||
self.ratio_vars.clear()
|
||||
n = int(self.gears_var.get())
|
||||
for i in range(1, n+1):
|
||||
ttk.Label(self.ratio_frame, text=f"Gang {i}").grid(row=i-1, column=0, sticky="w")
|
||||
v = tk.DoubleVar(value= [12.0,19.0,25.0,32.0,38.0,45.0][i-1] if i-1 < 6 else 45.0)
|
||||
ttk.Entry(self.ratio_frame, textvariable=v, width=8).grid(row=i-1, column=1, sticky="w", padx=(6,12))
|
||||
self.ratio_vars.append(v)
|
||||
# ---------- Rechte Spalte ----------
|
||||
rowR = 0
|
||||
def R(lbl, var=None, w=12, kind="entry"):
|
||||
nonlocal rowR
|
||||
ttk.Label(self.frame, text=lbl).grid(row=rowR, column=2, sticky="w")
|
||||
if kind == "entry":
|
||||
ttk.Entry(self.frame, textvariable=var, width=w).grid(row=rowR, column=3, sticky="w")
|
||||
elif kind == "label":
|
||||
ttk.Label(self.frame, textvariable=var).grid(row=rowR, column=3, sticky="w")
|
||||
rowR += 1
|
||||
|
||||
# Übersetzungen / Rad
|
||||
self.primary = tk.DoubleVar(); R("Primärübersetzung [-]", self.primary)
|
||||
self.zf = tk.IntVar(); R("Ritzel vorn [Z]", self.zf)
|
||||
self.zr = tk.IntVar(); R("Ritzel hinten [Z]", self.zr)
|
||||
self.rwheel = tk.DoubleVar(); R("Radradius [m]", self.rwheel)
|
||||
self.eta = tk.DoubleVar(); R("Wirkungsgrad [-]", self.eta)
|
||||
self.couple = tk.DoubleVar(); R("RPM-Kopplung [0..1]", self.couple)
|
||||
|
||||
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
|
||||
|
||||
# Gangübersetzungen 1..6
|
||||
self.g1 = tk.DoubleVar(); R("Gang 1 Ratio", self.g1)
|
||||
self.g2 = tk.DoubleVar(); R("Gang 2 Ratio", self.g2)
|
||||
self.g3 = tk.DoubleVar(); R("Gang 3 Ratio", self.g3)
|
||||
self.g4 = tk.DoubleVar(); R("Gang 4 Ratio", self.g4)
|
||||
self.g5 = tk.DoubleVar(); R("Gang 5 Ratio", self.g5)
|
||||
self.g6 = tk.DoubleVar(); R("Gang 6 Ratio", self.g6)
|
||||
|
||||
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
|
||||
|
||||
# Widerstände / Reifen
|
||||
self.c_rr = tk.DoubleVar(); R("Rollkoeff. c_rr", self.c_rr)
|
||||
self.rho = tk.DoubleVar(); R("Luftdichte [kg/m³]", self.rho)
|
||||
self.cd = tk.DoubleVar(); R("c_d [-]", self.cd)
|
||||
self.A = tk.DoubleVar(); R("Stirnfläche [m²]", self.A)
|
||||
self.mu_p = tk.DoubleVar(); R("Reifen μ_peak", self.mu_p)
|
||||
self.mu_s = tk.DoubleVar(); R("Reifen μ_slide", self.mu_s)
|
||||
self.w_rear = tk.DoubleVar(); R("Gewichtsanteil hinten [-]", self.w_rear)
|
||||
|
||||
# ---------- Buttons ----------
|
||||
rowBtns = max(rowL, rowR) + 1
|
||||
btn = ttk.Frame(self.frame); btn.grid(row=rowBtns, column=0, columnspan=4, sticky="w", pady=(8,0))
|
||||
ttk.Button(btn, text="Aktualisieren", command=self.refresh).pack(side="left")
|
||||
ttk.Button(btn, text="Anwenden", command=self.apply).pack(side="left", padx=(8,0))
|
||||
|
||||
self.refresh()
|
||||
self._tick()
|
||||
|
||||
# --- Live-Update nur für Labels ---
|
||||
def _tick(self):
|
||||
snap = self.sim.snapshot()
|
||||
gear = int(snap.get("gear", 0))
|
||||
self.gear_var.set("N" if gear == 0 else str(gear))
|
||||
self.speed_var.set(f"{float(snap.get('speed_kmh', 0.0)):.1f}")
|
||||
self.clutch_v.set(f"{float(snap.get('clutch_pct', 0.0)):.0f}")
|
||||
self.slip_v.set(f"{float(snap.get('wheel_slip_pct', 0.0)):.0f}")
|
||||
try:
|
||||
self.frame.after(200, self._tick)
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
# --- Actions (Buttons) ---
|
||||
def shift_up(self): self.sim.v.set("gear_shift_up", True)
|
||||
def shift_down(self): self.sim.v.set("gear_shift_down", True)
|
||||
def set_neutral(self): self.sim.v.set("gear_set_neutral", True)
|
||||
|
||||
# --- Data flow ---
|
||||
def refresh(self):
|
||||
# Live-Felder werden vom _tick() versorgt; hier nur Config mergen
|
||||
g = dict(GEARBOX_DEFAULTS)
|
||||
g.update(self.sim.v.config.get("gearbox", {}))
|
||||
|
||||
self.cl_Tmax.set(g["clutch_max_torque_nm"])
|
||||
self.cl_agr.set(g["clutch_aggressiveness"])
|
||||
self.cl_curve.set(g.get("clutch_curve", "linear"))
|
||||
self.cl_drag.set(g["clutch_drag_nm"])
|
||||
self.sh_time.set(g["shift_time_s"])
|
||||
self.sync_rb.set(g["sync_rpm_band"])
|
||||
|
||||
self.primary.set(g["primary_ratio"])
|
||||
self.zf.set(g["front_sprocket_teeth"])
|
||||
self.zr.set(g["rear_sprocket_teeth"])
|
||||
self.rwheel.set(g["wheel_radius_m"])
|
||||
self.eta.set(g["drivetrain_efficiency"])
|
||||
self.couple.set(g["rpm_couple_gain"])
|
||||
|
||||
ratios = list(g["gear_ratios"]) + [0.0]*7
|
||||
self.g1.set(ratios[1]); self.g2.set(ratios[2]); self.g3.set(ratios[3])
|
||||
self.g4.set(ratios[4]); self.g5.set(ratios[5]); self.g6.set(ratios[6])
|
||||
|
||||
self.c_rr.set(g["rolling_c"])
|
||||
self.rho.set(g["air_density"])
|
||||
self.cd.set(g["aero_cd"])
|
||||
self.A.set(g["frontal_area_m2"])
|
||||
self.mu_p.set(g["tire_mu_peak"])
|
||||
self.mu_s.set(g["tire_mu_slide"])
|
||||
self.w_rear.set(g["rear_static_weight_frac"])
|
||||
|
||||
def apply(self):
|
||||
ratios = [float(v.get()) for v in self.ratio_vars]
|
||||
cfg = {"gearbox": {
|
||||
"num_gears": int(self.gears_var.get()),
|
||||
"reverse": bool(self.reverse_var.get()),
|
||||
"kmh_per_krpm": [0.0] + ratios # index 0 reserved for neutral
|
||||
"clutch_max_torque_nm": float(self.cl_Tmax.get()),
|
||||
"clutch_aggressiveness": float(self.cl_agr.get()),
|
||||
"clutch_curve": self.cl_curve.get(),
|
||||
"clutch_drag_nm": float(self.cl_drag.get()),
|
||||
"shift_time_s": float(self.sh_time.get()),
|
||||
"sync_rpm_band": float(self.sync_rb.get()),
|
||||
|
||||
"primary_ratio": float(self.primary.get()),
|
||||
"front_sprocket_teeth": int(self.zf.get()),
|
||||
"rear_sprocket_teeth": int(self.zr.get()),
|
||||
"wheel_radius_m": float(self.rwheel.get()),
|
||||
"drivetrain_efficiency": float(self.eta.get()),
|
||||
"rpm_couple_gain": float(self.couple.get()),
|
||||
|
||||
"gear_ratios": [
|
||||
0.0,
|
||||
float(self.g1.get()),
|
||||
float(self.g2.get()),
|
||||
float(self.g3.get()),
|
||||
float(self.g4.get()),
|
||||
float(self.g5.get()),
|
||||
float(self.g6.get())
|
||||
],
|
||||
|
||||
"rolling_c": float(self.c_rr.get()),
|
||||
"air_density": float(self.rho.get()),
|
||||
"aero_cd": float(self.cd.get()),
|
||||
"frontal_area_m2": float(self.A.get()),
|
||||
"tire_mu_peak": float(self.mu_p.get()),
|
||||
"tire_mu_slide": float(self.mu_s.get()),
|
||||
"rear_static_weight_frac": float(self.w_rear.get()),
|
||||
}}
|
||||
self.sim.load_config(cfg)
|
||||
|
||||
def save_into_config(self, out: Dict[str, Any]) -> None:
|
||||
out.setdefault("gearbox", {})
|
||||
out["gearbox"].update({
|
||||
"num_gears": int(self.gears_var.get()),
|
||||
"reverse": bool(self.reverse_var.get()),
|
||||
"kmh_per_krpm": [0.0] + [float(v.get()) for v in self.ratio_vars]
|
||||
out.setdefault("gearbox", {}).update({
|
||||
"clutch_max_torque_nm": float(self.cl_Tmax.get()),
|
||||
"clutch_aggressiveness": float(self.cl_agr.get()),
|
||||
"clutch_curve": self.cl_curve.get(),
|
||||
"clutch_drag_nm": float(self.cl_drag.get()),
|
||||
"shift_time_s": float(self.sh_time.get()),
|
||||
"sync_rpm_band": float(self.sync_rb.get()),
|
||||
"primary_ratio": float(self.primary.get()),
|
||||
"front_sprocket_teeth": int(self.zf.get()),
|
||||
"rear_sprocket_teeth": int(self.zr.get()),
|
||||
"wheel_radius_m": float(self.rwheel.get()),
|
||||
"drivetrain_efficiency": float(self.eta.get()),
|
||||
"rpm_couple_gain": float(self.couple.get()),
|
||||
"gear_ratios": [0.0, float(self.g1.get()), float(self.g2.get()), float(self.g3.get()),
|
||||
float(self.g4.get()), float(self.g5.get()), float(self.g6.get())],
|
||||
"rolling_c": float(self.c_rr.get()),
|
||||
"air_density": float(self.rho.get()),
|
||||
"aero_cd": float(self.cd.get()),
|
||||
"frontal_area_m2": float(self.A.get()),
|
||||
"tire_mu_peak": float(self.mu_p.get()),
|
||||
"tire_mu_slide": float(self.mu_s.get()),
|
||||
"rear_static_weight_frac": float(self.w_rear.get()),
|
||||
})
|
||||
|
||||
def load_from_config(self, cfg: Dict[str, Any]) -> None:
|
||||
g = cfg.get("gearbox", {})
|
||||
n = int(g.get("num_gears", self.gears_var.get()))
|
||||
self.gears_var.set(n); self.reverse_var.set(g.get("reverse", self.reverse_var.get()))
|
||||
self._rebuild_ratios()
|
||||
ratios = g.get("kmh_per_krpm") or ([0.0] + [v.get() for v in self.ratio_vars])
|
||||
for i, v in enumerate(self.ratio_vars, start=1):
|
||||
try: v.set(float(ratios[i]))
|
||||
except Exception: pass
|
||||
self.sim.load_config(cfg)
|
||||
g = dict(GEARBOX_DEFAULTS); g.update(cfg.get("gearbox", {}))
|
||||
self.cl_Tmax.set(g["clutch_max_torque_nm"])
|
||||
self.cl_agr.set(g["clutch_aggressiveness"])
|
||||
self.cl_curve.set(g.get("clutch_curve","linear"))
|
||||
self.cl_drag.set(g["clutch_drag_nm"])
|
||||
self.sh_time.set(g["shift_time_s"])
|
||||
self.sync_rb.set(g["sync_rpm_band"])
|
||||
self.primary.set(g["primary_ratio"])
|
||||
self.zf.set(g["front_sprocket_teeth"])
|
||||
self.zr.set(g["rear_sprocket_teeth"])
|
||||
self.rwheel.set(g["wheel_radius_m"])
|
||||
self.eta.set(g["drivetrain_efficiency"])
|
||||
self.couple.set(g["rpm_couple_gain"])
|
||||
ratios = list(g["gear_ratios"]) + [0.0]*7
|
||||
self.g1.set(ratios[1]); self.g2.set(ratios[2]); self.g3.set(ratios[3])
|
||||
self.g4.set(ratios[4]); self.g5.set(ratios[5]); self.g6.set(ratios[6])
|
||||
self.c_rr.set(g["rolling_c"])
|
||||
self.rho.set(g["air_density"])
|
||||
self.cd.set(g["aero_cd"])
|
||||
self.A.set(g["frontal_area_m2"])
|
||||
self.mu_p.set(g["tire_mu_peak"])
|
||||
self.mu_s.set(g["tire_mu_slide"])
|
||||
self.w_rear.set(g["rear_static_weight_frac"])
|
||||
|
Reference in New Issue
Block a user