neraly complete Model of Driving done, but needs tweaking
This commit is contained in:
@@ -6,194 +6,204 @@ from __future__ import annotations
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from typing import Dict, Any
|
||||
from app.simulation.ui import UITab
|
||||
from app.simulation.ui import UITab
|
||||
|
||||
class BasicTab(UITab):
|
||||
NAME = "basic"
|
||||
TITLE = "Basisdaten"
|
||||
PRIO = 10
|
||||
"""Basis-Fahrzeug-Tab (Zündung & Elektrik)."""
|
||||
|
||||
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)
|
||||
|
||||
row = 0
|
||||
# Vehicle basics -----------------------------------------------------------
|
||||
ttk.Label(self.frame, text="Fahrzeugtyp").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.type_var = tk.StringVar(value=self.sim.v.config.get("vehicle", {}).get("type", "motorcycle"))
|
||||
ttk.Combobox(self.frame, textvariable=self.type_var, state="readonly",
|
||||
values=["motorcycle", "car", "truck"], width=16)\
|
||||
.grid(row=row-1, 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 == "check":
|
||||
ttk.Checkbutton(self.frame, variable=var).grid(row=rowL, column=1, sticky="w")
|
||||
elif kind == "radio":
|
||||
f = ttk.Frame(self.frame); f.grid(row=rowL, column=1, sticky="w")
|
||||
for i,(t,vv) in enumerate(values or []):
|
||||
ttk.Radiobutton(f, text=t, value=vv, variable=var, command=self._apply_ign)\
|
||||
.grid(row=0, column=i, padx=(0,6))
|
||||
rowL += 1
|
||||
|
||||
ttk.Label(self.frame, text="Gewicht [kg]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.mass_var = tk.DoubleVar(value=float(self.sim.v.config.get("vehicle", {}).get("mass_kg", 210.0)))
|
||||
ttk.Entry(self.frame, textvariable=self.mass_var, width=10).grid(row=row-1, column=1, sticky="w")
|
||||
# Vehicle
|
||||
self.type = tk.StringVar(); L("Fahrzeugtyp", self.type, kind="combo", values=["motorcycle","car","truck"])
|
||||
self.mass = tk.DoubleVar(); L("Gewicht [kg]", self.mass)
|
||||
self.abs = tk.BooleanVar(); L("ABS vorhanden", self.abs, kind="check")
|
||||
self.tcs = tk.BooleanVar(); L("ASR/Traktionskontrolle", self.tcs, kind="check")
|
||||
|
||||
self.abs_var = tk.BooleanVar(value=bool(self.sim.v.config.get("vehicle", {}).get("abs", True)))
|
||||
ttk.Checkbutton(self.frame, text="ABS vorhanden", variable=self.abs_var)\
|
||||
.grid(row=row, column=0, columnspan=2, sticky="w"); row+=1
|
||||
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
|
||||
|
||||
self.tcs_var = tk.BooleanVar(value=bool(self.sim.v.config.get("vehicle", {}).get("tcs", False)))
|
||||
ttk.Checkbutton(self.frame, text="ASR/Traktionskontrolle", variable=self.tcs_var)\
|
||||
.grid(row=row, column=0, columnspan=2, sticky="w"); row+=1
|
||||
# Environment / Ignition
|
||||
self.amb = tk.DoubleVar(); L("Umgebung [°C]", self.amb)
|
||||
self.ign = tk.StringVar(); L("Zündung", self.ign, kind="radio",
|
||||
values=[("OFF","OFF"),("ACC","ACC"),("ON","ON"),("START","START")])
|
||||
|
||||
ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(6,6)); row+=1
|
||||
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
|
||||
|
||||
# Ambient -----------------------------------------------------------------
|
||||
ttk.Label(self.frame, text="Umgebung [°C]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.ambient_var = tk.DoubleVar(value=float(self.sim.snapshot().get("ambient_c", 20.0)))
|
||||
ttk.Entry(self.frame, textvariable=self.ambient_var, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
# Live links (Labels)
|
||||
self.batt_v = tk.StringVar(); L("Batterie [V]", self.batt_v, kind="label")
|
||||
self.elx_v = tk.StringVar(); L("ELX/Bus [V]", self.elx_v, kind="label")
|
||||
self.soc = tk.StringVar(); L("SOC [0..1]", self.soc, kind="label")
|
||||
|
||||
# Ignition ----------------------------------------------------------------
|
||||
ttk.Label(self.frame, text="Zündung").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.ign_var = tk.StringVar(value=str(self.sim.snapshot().get("ignition", "ON")))
|
||||
ign_frame = ttk.Frame(self.frame); ign_frame.grid(row=row-1, column=1, sticky="w")
|
||||
for i, state in enumerate(["OFF", "ACC", "ON", "START"]):
|
||||
ttk.Radiobutton(ign_frame, text=state, value=state,
|
||||
variable=self.ign_var, command=self._apply_ign)\
|
||||
.grid(row=0, column=i, padx=(0,6))
|
||||
# ---------- 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
|
||||
|
||||
# Live Electrical ----------------------------------------------------------
|
||||
ttk.Label(self.frame, text="Batterie [V]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.batt_v_var = tk.StringVar(value=f"{self.sim.snapshot().get('battery_voltage', 12.6):.2f}")
|
||||
ttk.Label(self.frame, textvariable=self.batt_v_var).grid(row=row-1, column=1, sticky="w")
|
||||
# Live rechts (Labels)
|
||||
self.ibatt = tk.StringVar(); R("I Batterie [A] (+entlädt)", self.ibatt, kind="label")
|
||||
self.ialt = tk.StringVar(); R("I Lima [A]", self.ialt, kind="label")
|
||||
self.load_elx= tk.StringVar(); R("Last ELX [A]", self.load_elx, kind="label")
|
||||
self.load_bat= tk.StringVar(); R("Last Batterie [A]", self.load_bat, kind="label")
|
||||
self.load_tot= tk.StringVar(); R("Last gesamt [A]", self.load_tot, kind="label")
|
||||
|
||||
ttk.Label(self.frame, text="ELX/Bus [V]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.elx_v_var = tk.StringVar(value=f"{self.sim.snapshot().get('elx_voltage', 0.0):.2f}")
|
||||
ttk.Label(self.frame, textvariable=self.elx_v_var).grid(row=row-1, column=1, sticky="w")
|
||||
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
|
||||
|
||||
ttk.Label(self.frame, text="SOC [0..1]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.soc_var = tk.StringVar(value=f"{self.sim.snapshot().get('battery_soc', 0.8):.2f}")
|
||||
ttk.Label(self.frame, textvariable=self.soc_var).grid(row=row-1, column=1, sticky="w")
|
||||
# Electrical config
|
||||
self.bcap = tk.DoubleVar(); R("Batt Kap. [Ah]", self.bcap)
|
||||
self.brint = tk.DoubleVar(); R("Batt R_int [Ω]", self.brint)
|
||||
self.alt_v = tk.DoubleVar(); R("Reglerspannung [V]", self.alt_v)
|
||||
self.alt_a = tk.DoubleVar(); R("Lima Nennstrom [A]", self.alt_a)
|
||||
self.alt_ci = tk.IntVar(); R("Cut-In RPM", self.alt_ci)
|
||||
self.alt_fc = tk.IntVar(); R("Full-Cap RPM", self.alt_fc)
|
||||
self.alt_eta= tk.DoubleVar(); R("Lima η_mech [-]", self.alt_eta)
|
||||
self.alt_rat= tk.DoubleVar(); R("Lima Übersetzung [-]", self.alt_rat)
|
||||
self.alt_d0 = tk.DoubleVar(); R("Lima Drag Grund [Nm]", self.alt_d0)
|
||||
self.alt_d1 = tk.DoubleVar(); R("Lima Drag /krpm [Nm]", self.alt_d1)
|
||||
|
||||
ttk.Label(self.frame, text="I Batterie [A] (+entlädt)").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.ibatt_var = tk.StringVar(value=f"{self.sim.snapshot().get('battery_current_a', 0.0):.2f}")
|
||||
ttk.Label(self.frame, textvariable=self.ibatt_var).grid(row=row-1, column=1, sticky="w")
|
||||
# ---------- Buttons ----------
|
||||
rowBtns = max(rowL, rowR) + 1
|
||||
btnrow = ttk.Frame(self.frame); btnrow.grid(row=rowBtns, column=0, columnspan=4, sticky="w", pady=(8,0))
|
||||
ttk.Button(btnrow, text="Aktualisieren", command=self.refresh).pack(side="left")
|
||||
ttk.Button(btnrow, text="Anwenden", command=self.apply).pack(side="left", padx=(8,0))
|
||||
|
||||
ttk.Label(self.frame, text="I Lima [A]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.ialt_var = tk.StringVar(value=f"{self.sim.snapshot().get('alternator_current_a', 0.0):.2f}")
|
||||
ttk.Label(self.frame, textvariable=self.ialt_var).grid(row=row-1, column=1, sticky="w")
|
||||
self.refresh()
|
||||
|
||||
ttk.Label(self.frame, text="Last gesamt [A]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.load_var = tk.StringVar(value=f"{self.sim.snapshot().get('elec_load_total_a', 0.0):.2f}")
|
||||
ttk.Label(self.frame, textvariable=self.load_var).grid(row=row-1, column=1, sticky="w")
|
||||
|
||||
ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(6,6)); row+=1
|
||||
|
||||
# Electrical config --------------------------------------------------------
|
||||
econf = self.sim.v.config.get("electrical", {})
|
||||
ttk.Label(self.frame, text="Batt Kap. [Ah]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.bcap = tk.DoubleVar(value=float(econf.get("battery_capacity_ah", 8.0)))
|
||||
ttk.Entry(self.frame, textvariable=self.bcap, width=10).grid(row=row-1, column=1, sticky="w")
|
||||
|
||||
ttk.Label(self.frame, text="Batt R_int [Ω]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.brint = tk.DoubleVar(value=float(econf.get("battery_r_int_ohm", 0.020)))
|
||||
ttk.Entry(self.frame, textvariable=self.brint, width=10).grid(row=row-1, column=1, sticky="w")
|
||||
|
||||
ttk.Label(self.frame, text="Reglerspannung [V]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.alt_v = tk.DoubleVar(value=float(econf.get("alternator_reg_v", 14.2)))
|
||||
ttk.Entry(self.frame, textvariable=self.alt_v, width=10).grid(row=row-1, column=1, sticky="w")
|
||||
|
||||
ttk.Label(self.frame, text="Lima Nennstrom [A]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.alt_a = tk.DoubleVar(value=float(econf.get("alternator_rated_a", 20.0)))
|
||||
ttk.Entry(self.frame, textvariable=self.alt_a, width=10).grid(row=row-1, column=1, sticky="w")
|
||||
|
||||
ttk.Label(self.frame, text="Cut-In RPM").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.alt_cutin = tk.IntVar(value=int(econf.get("alt_cut_in_rpm", 1500)))
|
||||
ttk.Entry(self.frame, textvariable=self.alt_cutin, width=10).grid(row=row-1, column=1, sticky="w")
|
||||
|
||||
ttk.Label(self.frame, text="Full-Cap RPM").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.alt_full = tk.IntVar(value=int(econf.get("alt_full_rpm", 4000)))
|
||||
ttk.Entry(self.frame, textvariable=self.alt_full, width=10).grid(row=row-1, column=1, sticky="w")
|
||||
|
||||
# Apply --------------------------------------------------------------------
|
||||
ttk.Button(self.frame, text="Anwenden", command=self.apply)\
|
||||
.grid(row=row, column=0, pady=(8,0), sticky="w")
|
||||
|
||||
# periodic UI refresh
|
||||
self._tick()
|
||||
|
||||
def _tick(self):
|
||||
# ------------ Logic ------------
|
||||
def refresh(self):
|
||||
snap = self.sim.snapshot()
|
||||
# Live-Werte
|
||||
self.batt_v_var.set(f"{snap.get('battery_voltage', 0):.2f}")
|
||||
self.elx_v_var.set(f"{snap.get('elx_voltage', 0):.2f}")
|
||||
self.soc_var.set(f"{snap.get('battery_soc', 0.0):.2f}")
|
||||
self.ibatt_var.set(f"{snap.get('battery_current_a', 0.0):.2f}")
|
||||
self.ialt_var.set(f"{snap.get('alternator_current_a', 0.0):.2f}")
|
||||
self.load_var.set(f"{snap.get('elec_load_total_a', 0.0):.2f}")
|
||||
vcfg = dict(self.sim.v.config.get("vehicle", {}))
|
||||
ecfg = dict(self.sim.v.config.get("electrical", {}))
|
||||
|
||||
# START→ON aus dem Modul spiegeln
|
||||
curr_ign = snap.get("ignition")
|
||||
if curr_ign and curr_ign != self.ign_var.get():
|
||||
self.ign_var.set(curr_ign)
|
||||
# Vehicle
|
||||
self.type.set(vcfg.get("type", "motorcycle"))
|
||||
self.mass.set(float(vcfg.get("mass_kg", 210.0)))
|
||||
self.abs.set(bool(vcfg.get("abs", True)))
|
||||
self.tcs.set(bool(vcfg.get("tcs", False)))
|
||||
|
||||
try:
|
||||
self.frame.after(200, self._tick)
|
||||
except tk.TclError:
|
||||
pass
|
||||
# Env / Ign
|
||||
self.amb.set(float(snap.get("ambient_c", 20.0)))
|
||||
self.ign.set(str(snap.get("ignition", "ON")))
|
||||
|
||||
# Live left
|
||||
self.batt_v.set(f"{float(snap.get('battery_voltage', 12.6)):.2f}")
|
||||
self.elx_v.set(f"{float(snap.get('elx_voltage', 0.0)):.2f}")
|
||||
self.soc.set(f"{float(snap.get('battery_soc', 0.80)):.2f}")
|
||||
|
||||
# Live right
|
||||
self.ibatt.set(f"{float(snap.get('battery_current_a', 0.0)):.2f}")
|
||||
self.ialt.set(f"{float(snap.get('alternator_current_a', 0.0)):.2f}")
|
||||
self.load_elx.set(f"{float(snap.get('elec_load_elx_a', 0.0)):.2f}")
|
||||
self.load_bat.set(f"{float(snap.get('elec_load_batt_a', 0.0)):.2f}")
|
||||
self.load_tot.set(f"{float(snap.get('elec_load_total_a', 0.0)):.2f}")
|
||||
|
||||
# Electrical config
|
||||
self.bcap.set(float(ecfg.get("battery_capacity_ah", 8.0)))
|
||||
self.brint.set(float(ecfg.get("battery_r_int_ohm", 0.020)))
|
||||
self.alt_v.set(float(ecfg.get("alternator_reg_v", 14.2)))
|
||||
self.alt_a.set(float(ecfg.get("alternator_rated_a", 20.0)))
|
||||
self.alt_ci.set(int(ecfg.get("alt_cut_in_rpm", 1500)))
|
||||
self.alt_fc.set(int(ecfg.get("alt_full_rpm", 4000)))
|
||||
self.alt_eta.set(float(ecfg.get("alternator_mech_efficiency", 0.55)))
|
||||
self.alt_rat.set(float(ecfg.get("alternator_pulley_ratio", 1.0)))
|
||||
self.alt_d0.set(float(ecfg.get("alternator_drag_nm_idle", 0.15)))
|
||||
self.alt_d1.set(float(ecfg.get("alternator_drag_nm_per_krpm", 0.05)))
|
||||
|
||||
def _apply_ign(self):
|
||||
# Zündung live setzen
|
||||
self.sim.v.set("ignition", self.ign_var.get())
|
||||
self.sim.v.set("ignition", self.ign.get())
|
||||
|
||||
def apply(self):
|
||||
# Ambient in State (wirkt sofort auf Thermik, andere Module lesen das)
|
||||
try:
|
||||
self.sim.v.set("ambient_c", float(self.ambient_var.get()))
|
||||
except Exception:
|
||||
pass
|
||||
# Umgebung sofort in den State (wirkt auf Thermik)
|
||||
try: self.sim.v.set("ambient_c", float(self.amb.get()))
|
||||
except: pass
|
||||
|
||||
cfg = {
|
||||
"vehicle": {
|
||||
"type": self.type_var.get(),
|
||||
"mass_kg": float(self.mass_var.get()),
|
||||
"abs": bool(self.abs_var.get()),
|
||||
"tcs": bool(self.tcs_var.get()),
|
||||
"type": self.type.get(),
|
||||
"mass_kg": float(self.mass.get()),
|
||||
"abs": bool(self.abs.get()),
|
||||
"tcs": bool(self.tcs.get()),
|
||||
},
|
||||
"electrical": {
|
||||
"battery_capacity_ah": float(self.bcap.get()),
|
||||
"battery_r_int_ohm": float(self.brint.get()),
|
||||
"alternator_reg_v": float(self.alt_v.get()),
|
||||
"alternator_rated_a": float(self.alt_a.get()),
|
||||
"alt_cut_in_rpm": int(self.alt_cutin.get()),
|
||||
"alt_full_rpm": int(self.alt_full.get()),
|
||||
"alt_cut_in_rpm": int(self.alt_ci.get()),
|
||||
"alt_full_rpm": int(self.alt_fc.get()),
|
||||
"alternator_mech_efficiency": float(self.alt_eta.get()),
|
||||
"alternator_pulley_ratio": float(self.alt_rat.get()),
|
||||
"alternator_drag_nm_idle": float(self.alt_d0.get()),
|
||||
"alternator_drag_nm_per_krpm": float(self.alt_d1.get()),
|
||||
}
|
||||
}
|
||||
self.sim.load_config(cfg)
|
||||
|
||||
# Save/Load Hooks für Gesamt-Export
|
||||
def save_into_config(self, out: Dict[str, Any]) -> None:
|
||||
out.setdefault("vehicle", {})
|
||||
out["vehicle"].update({
|
||||
"type": self.type_var.get(),
|
||||
"mass_kg": float(self.mass_var.get()),
|
||||
"abs": bool(self.abs_var.get()),
|
||||
"tcs": bool(self.tcs_var.get()),
|
||||
out.setdefault("vehicle", {}).update({
|
||||
"type": self.type.get(),
|
||||
"mass_kg": float(self.mass.get()),
|
||||
"abs": bool(self.abs.get()),
|
||||
"tcs": bool(self.tcs.get()),
|
||||
})
|
||||
out.setdefault("electrical", {})
|
||||
out["electrical"].update({
|
||||
out.setdefault("electrical", {}).update({
|
||||
"battery_capacity_ah": float(self.bcap.get()),
|
||||
"battery_r_int_ohm": float(self.brint.get()),
|
||||
"alternator_reg_v": float(self.alt_v.get()),
|
||||
"alternator_rated_a": float(self.alt_a.get()),
|
||||
"alt_cut_in_rpm": int(self.alt_cutin.get()),
|
||||
"alt_full_rpm": int(self.alt_full.get()),
|
||||
"alt_cut_in_rpm": int(self.alt_ci.get()),
|
||||
"alt_full_rpm": int(self.alt_fc.get()),
|
||||
"alternator_mech_efficiency": float(self.alt_eta.get()),
|
||||
"alternator_pulley_ratio": float(self.alt_rat.get()),
|
||||
"alternator_drag_nm_idle": float(self.alt_d0.get()),
|
||||
"alternator_drag_nm_per_krpm": float(self.alt_d1.get()),
|
||||
})
|
||||
|
||||
def load_from_config(self, cfg: Dict[str, Any]) -> None:
|
||||
vcfg = cfg.get("vehicle", {})
|
||||
self.type_var.set(vcfg.get("type", self.type_var.get()))
|
||||
self.mass_var.set(vcfg.get("mass_kg", self.mass_var.get()))
|
||||
self.abs_var.set(vcfg.get("abs", self.abs_var.get()))
|
||||
self.tcs_var.set(vcfg.get("tcs", self.tcs_var.get()))
|
||||
ecfg = cfg.get("electrical", {})
|
||||
vcfg = cfg.get("vehicle", {}); ecfg = cfg.get("electrical", {})
|
||||
self.type.set(vcfg.get("type", self.type.get()))
|
||||
self.mass.set(vcfg.get("mass_kg", self.mass.get()))
|
||||
self.abs.set(vcfg.get("abs", self.abs.get()))
|
||||
self.tcs.set(vcfg.get("tcs", self.tcs.get()))
|
||||
self.bcap.set(ecfg.get("battery_capacity_ah", self.bcap.get()))
|
||||
self.brint.set(ecfg.get("battery_r_int_ohm", self.brint.get()))
|
||||
self.alt_v.set(ecfg.get("alternator_reg_v", self.alt_v.get()))
|
||||
self.alt_a.set(ecfg.get("alternator_rated_a", self.alt_a.get()))
|
||||
self.alt_cutin.set(ecfg.get("alt_cut_in_rpm", self.alt_cutin.get()))
|
||||
self.alt_full.set(ecfg.get("alt_full_rpm", self.alt_full.get()))
|
||||
# wichtig: NICHT self.sim.load_config(cfg) hier!
|
||||
self.alt_ci.set(ecfg.get("alt_cut_in_rpm", self.alt_ci.get()))
|
||||
self.alt_fc.set(ecfg.get("alt_full_rpm", self.alt_fc.get()))
|
||||
self.alt_eta.set(ecfg.get("alternator_mech_efficiency", self.alt_eta.get()))
|
||||
self.alt_rat.set(ecfg.get("alternator_pulley_ratio", self.alt_rat.get()))
|
||||
self.alt_d0.set(ecfg.get("alternator_drag_nm_idle", self.alt_d0.get()))
|
||||
self.alt_d1.set(ecfg.get("alternator_drag_nm_per_krpm", self.alt_d1.get()))
|
||||
# wichtig: hier KEIN sim.load_config()
|
||||
|
149
app/simulation/ui/cooling.py
Normal file
149
app/simulation/ui/cooling.py
Normal file
@@ -0,0 +1,149 @@
|
||||
# =============================
|
||||
# app/simulation/ui/cooling.py
|
||||
# =============================
|
||||
|
||||
from __future__ import annotations
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from app.simulation.modules.cooling import COOLING_DEFAULTS
|
||||
from app.simulation.ui import UITab
|
||||
|
||||
class CoolingTab(UITab):
|
||||
NAME = "cooling"
|
||||
TITLE = "Kühlung"
|
||||
PRIO = 11
|
||||
|
||||
def __init__(self, parent, sim):
|
||||
self.sim = sim
|
||||
self.frame = ttk.Frame(parent, padding=8)
|
||||
|
||||
for c in (0,1,2,3):
|
||||
self.frame.columnconfigure(c, weight=1)
|
||||
|
||||
# ---------- Linke Spalte ----------
|
||||
rowL = 0
|
||||
def L(lbl, var, 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 == "combo":
|
||||
cb = ttk.Combobox(self.frame, textvariable=var, state="readonly", values=values or [])
|
||||
cb.grid(row=rowL, column=1, sticky="w")
|
||||
elif kind == "check":
|
||||
ttk.Checkbutton(self.frame, variable=var).grid(row=rowL, column=1, sticky="w")
|
||||
rowL += 1
|
||||
|
||||
self.t_open = tk.DoubleVar(); L("Thermostat öffnet ab [°C]", self.t_open)
|
||||
self.t_full = tk.DoubleVar(); L("Thermostat voll offen [°C]", self.t_full)
|
||||
self.rad_base = tk.DoubleVar(); L("Radiator-Basis [W/K]", self.rad_base)
|
||||
self.ram_gain = tk.DoubleVar(); L("Fahrtwind-Zuwachs [W/K pro km/h]", self.ram_gain)
|
||||
self.amb_c = tk.DoubleVar(); L("Umgebung [°C]", self.amb_c)
|
||||
self.Cc = tk.DoubleVar(); L("Wärmekapazität Kühlmittel [J/K]", self.Cc)
|
||||
self.Coil = tk.DoubleVar(); L("Wärmekapazität Öl [J/K]", self.Coil)
|
||||
|
||||
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
|
||||
|
||||
# Versorgung & Nachlauf (links)
|
||||
self.feed = tk.StringVar()
|
||||
self.afteren = tk.BooleanVar()
|
||||
self.afterth = tk.DoubleVar()
|
||||
self.aftermax= tk.DoubleVar()
|
||||
|
||||
L("Lüfter-Versorgung", self.feed, kind="combo", values=["elx", "battery"])
|
||||
L("Nachlauf aktiv", self.afteren, kind="check")
|
||||
L("Nachlauf-Schwelle [°C]", self.afterth)
|
||||
L("Nachlauf max. Zeit [s]", self.aftermax)
|
||||
|
||||
# ---------- Rechte Spalte ----------
|
||||
rowR = 0
|
||||
def R(lbl, var, w=12):
|
||||
nonlocal rowR
|
||||
ttk.Label(self.frame, text=lbl).grid(row=rowR, column=2, sticky="w")
|
||||
ttk.Entry(self.frame, textvariable=var, width=w).grid(row=rowR, column=3, sticky="w")
|
||||
rowR += 1
|
||||
|
||||
self.f1_on = tk.DoubleVar(); R("Lüfter 1 EIN [°C]", self.f1_on)
|
||||
self.f1_off = tk.DoubleVar(); R("Lüfter 1 AUS [°C]", self.f1_off)
|
||||
self.f2_on = tk.DoubleVar(); R("Lüfter 2 EIN [°C]", self.f2_on)
|
||||
self.f2_off = tk.DoubleVar(); R("Lüfter 2 AUS [°C]", self.f2_off)
|
||||
|
||||
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
|
||||
|
||||
self.f1_w = tk.DoubleVar(); R("Lüfter 1 Leistung [W]", self.f1_w)
|
||||
self.f2_w = tk.DoubleVar(); R("Lüfter 2 Leistung [W]", self.f2_w)
|
||||
self.f1_air = tk.DoubleVar(); R("Lüfter 1 Luftstrom [W/K]", self.f1_air)
|
||||
self.f2_air = tk.DoubleVar(); R("Lüfter 2 Luftstrom [W/K]", self.f2_air)
|
||||
|
||||
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
|
||||
|
||||
self.Uoc = tk.DoubleVar(); R("Öl↔Kühlmittel Kopplung [W/K]", self.Uoc)
|
||||
self.Uoil = tk.DoubleVar(); R("Öl→Umgebung [W/K]", self.Uoil)
|
||||
self.frac = tk.DoubleVar(); R("Motorwärme→Kühlmittel [%]", self.frac)
|
||||
|
||||
# ---------- Buttons ----------
|
||||
rowBtns = max(rowL, rowR) + 1
|
||||
btnrow = ttk.Frame(self.frame)
|
||||
btnrow.grid(row=rowBtns, column=0, columnspan=4, sticky="w", pady=(8,0))
|
||||
ttk.Button(btnrow, text="Aktualisieren", command=self.refresh).pack(side="left")
|
||||
ttk.Button(btnrow, text="Anwenden", command=self.apply).pack(side="left", padx=(8,0))
|
||||
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
c = dict(COOLING_DEFAULTS)
|
||||
c.update(self.sim.v.config.get("cooling", {}))
|
||||
|
||||
# links
|
||||
self.t_open.set(c["thermostat_open_c"])
|
||||
self.t_full.set(c["thermostat_full_c"])
|
||||
self.rad_base.set(c["rad_base_u_w_per_k"])
|
||||
self.ram_gain.set(c["ram_air_gain_per_kmh"])
|
||||
self.amb_c.set(self.sim.v.get("ambient_c", 20.0))
|
||||
self.Cc.set(c["coolant_thermal_cap_j_per_k"])
|
||||
self.Coil.set(c["oil_thermal_cap_j_per_k"])
|
||||
|
||||
# Versorgung & Nachlauf
|
||||
self.feed.set(c.get("fan_power_feed", "elx"))
|
||||
self.afteren.set(bool(c.get("fan_afterrun_enable", False)))
|
||||
self.afterth.set(float(c.get("fan_afterrun_threshold_c", 105.0)))
|
||||
self.aftermax.set(float(c.get("fan_afterrun_max_s", 300.0)))
|
||||
|
||||
# rechts
|
||||
self.f1_on.set(c["fan1_on_c"]); self.f1_off.set(c["fan1_off_c"])
|
||||
self.f2_on.set(c["fan2_on_c"]); self.f2_off.set(c["fan2_off_c"])
|
||||
self.f1_w.set(c["fan1_power_w"]); self.f2_w.set(c["fan2_power_w"])
|
||||
self.f1_air.set(c["fan1_airflow_gain"]); self.f2_air.set(c["fan2_airflow_gain"])
|
||||
|
||||
self.Uoc.set(c["oil_coolant_u_w_per_k"])
|
||||
self.Uoil.set(c["oil_to_amb_u_w_per_k"])
|
||||
self.frac.set(c["engine_heat_frac_to_coolant"]*100.0)
|
||||
|
||||
def apply(self):
|
||||
cfg = {"cooling": {
|
||||
# links
|
||||
"thermostat_open_c": float(self.t_open.get()),
|
||||
"thermostat_full_c": float(self.t_full.get()),
|
||||
"rad_base_u_w_per_k": float(self.rad_base.get()),
|
||||
"ram_air_gain_per_kmh": float(self.ram_gain.get()),
|
||||
"coolant_thermal_cap_j_per_k": float(self.Cc.get()),
|
||||
"oil_thermal_cap_j_per_k": float(self.Coil.get()),
|
||||
# Versorgung & Nachlauf
|
||||
"fan_power_feed": self.feed.get(),
|
||||
"fan_afterrun_enable": bool(self.afteren.get()),
|
||||
"fan_afterrun_threshold_c": float(self.afterth.get()),
|
||||
"fan_afterrun_max_s": float(self.aftermax.get()),
|
||||
# rechts
|
||||
"fan1_on_c": float(self.f1_on.get()),
|
||||
"fan1_off_c": float(self.f1_off.get()),
|
||||
"fan2_on_c": float(self.f2_on.get()),
|
||||
"fan2_off_c": float(self.f2_off.get()),
|
||||
"fan1_power_w": float(self.f1_w.get()),
|
||||
"fan2_power_w": float(self.f2_w.get()),
|
||||
"fan1_airflow_gain": float(self.f1_air.get()),
|
||||
"fan2_airflow_gain": float(self.f2_air.get()),
|
||||
"oil_coolant_u_w_per_k": float(self.Uoc.get()),
|
||||
"oil_to_amb_u_w_per_k": float(self.Uoil.get()),
|
||||
"engine_heat_frac_to_coolant": float(self.frac.get())/100.0,
|
||||
}}
|
||||
self.sim.load_config(cfg)
|
@@ -5,180 +5,182 @@
|
||||
from __future__ import annotations
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from typing import Dict, Any
|
||||
# Wichtig: Defaults aus dem Modul importieren
|
||||
from app.simulation.modules.engine import ENGINE_DEFAULTS
|
||||
from app.simulation.ui import UITab
|
||||
|
||||
from app.simulation.ui import UITab
|
||||
|
||||
class EngineTab(UITab):
|
||||
NAME = "engine"
|
||||
TITLE = "Motor"
|
||||
PRIO = 10
|
||||
|
||||
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)
|
||||
|
||||
# ------------- Widgets anlegen (OHNE Defaultwerte eintragen) --------------
|
||||
row = 0
|
||||
ttk.Label(self.frame, text="Leerlauf [RPM]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.idle_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.idle_var, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
# ---------- Linke Spalte ----------
|
||||
rowL = 0
|
||||
def L(lbl, var, 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 == "combo":
|
||||
ttk.Combobox(self.frame, textvariable=var, state="readonly",
|
||||
values=values or [], width=w).grid(row=rowL, column=1, sticky="w")
|
||||
rowL += 1
|
||||
|
||||
ttk.Label(self.frame, text="Max RPM").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.maxrpm_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.maxrpm_var, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
self.idle = tk.IntVar(); L("Leerlauf [RPM]", self.idle)
|
||||
self.maxrpm = tk.IntVar(); L("Max RPM", self.maxrpm)
|
||||
self.rise = tk.IntVar(); L("Anstieg [RPM/s]", self.rise)
|
||||
self.fall = tk.IntVar(); L("Abfall [RPM/s]", self.fall)
|
||||
self.curve = tk.StringVar(); L("Gaspedal-Kennlinie", self.curve, kind="combo",
|
||||
values=["linear","progressive","aggressive"])
|
||||
|
||||
ttk.Label(self.frame, text="Anstieg [RPM/s]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.rise_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.rise_var, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
|
||||
|
||||
ttk.Label(self.frame, text="Abfall [RPM/s]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.fall_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.fall_var, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
self.power = tk.DoubleVar(); L("Motorleistung [kW]", self.power)
|
||||
self.tqpeak = tk.DoubleVar(); L("Drehmoment-Peak [RPM]", self.tqpeak)
|
||||
|
||||
ttk.Label(self.frame, text="Gaspedal-Kennlinie").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.thr_curve = tk.StringVar()
|
||||
ttk.Combobox(self.frame, textvariable=self.thr_curve, state="readonly",
|
||||
values=["linear","progressive","aggressive"])\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
|
||||
|
||||
ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1
|
||||
self.st_nom = tk.DoubleVar(); L("Starter Nenn-RPM", self.st_nom)
|
||||
self.st_vmin= tk.DoubleVar(); L("Starter min. Spannung [V]", self.st_vmin)
|
||||
self.st_thr = tk.DoubleVar(); L("Start-Schwelle [RPM]", self.st_thr)
|
||||
self.stall = tk.DoubleVar(); L("Stall-Grenze [RPM]", self.stall)
|
||||
|
||||
# Leistung
|
||||
ttk.Label(self.frame, text="Motorleistung [kW]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.power_kw = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.power_kw, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
|
||||
|
||||
ttk.Label(self.frame, text="Drehmoment-Peak [RPM]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.peak_rpm = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.peak_rpm, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
self.o_idle = tk.DoubleVar(); L("Öldruck Leerlauf [bar]", self.o_idle)
|
||||
self.o_slope= tk.DoubleVar(); L("Öldruck Steigung [bar/krpm]", self.o_slope)
|
||||
self.o_floor= tk.DoubleVar(); L("Öldruck Boden [bar]", self.o_floor)
|
||||
|
||||
ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1
|
||||
# ---------- Rechte Spalte ----------
|
||||
rowR = 0
|
||||
def R(lbl, var, 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")
|
||||
elif kind == "scale":
|
||||
s = ttk.Scale(self.frame, from_=0.0, to=100.0, variable=var,
|
||||
command=lambda _=None: self._on_pedal_change())
|
||||
s.grid(row=rowR, column=3, sticky="ew")
|
||||
rowR += 1
|
||||
|
||||
# Starter
|
||||
ttk.Label(self.frame, text="Starter Nenn-RPM").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.starter_nom = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.starter_nom, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
self.dk_idle = tk.DoubleVar(); R("DK min Leerlauf [%]", self.dk_idle)
|
||||
self.dk_over = tk.DoubleVar(); R("DK Schub [%]", self.dk_over)
|
||||
self.dk_tau = tk.DoubleVar(); R("DK Zeitkonstante [s]", self.dk_tau)
|
||||
self.tq_kp = tk.DoubleVar(); R("Torque-Kp", self.tq_kp)
|
||||
self.tq_ki = tk.DoubleVar(); R("Torque-Ki", self.tq_ki)
|
||||
|
||||
ttk.Label(self.frame, text="Starter min. Spannung [V]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.starter_vmin = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.starter_vmin, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
|
||||
|
||||
ttk.Label(self.frame, text="Start-Schwelle [RPM]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.start_th = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.start_th, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
self.jit_idle= tk.DoubleVar(); R("Jitter Leerlauf [±RPM]", self.jit_idle)
|
||||
self.jit_high= tk.DoubleVar(); R("Jitter hoch [±RPM]", self.jit_high)
|
||||
self.jit_tau = tk.DoubleVar(); R("Jitter-Zeitkonstante [s]", self.jit_tau)
|
||||
self.jit_off = tk.DoubleVar(); R("Jitter aus unter [RPM]", self.jit_off)
|
||||
|
||||
ttk.Label(self.frame, text="Stall-Grenze [RPM]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.stall_rpm = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.stall_rpm, width=10)\
|
||||
.grid(row=row-1, column=1, sticky="w")
|
||||
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
|
||||
|
||||
ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1
|
||||
self.amb_c = tk.DoubleVar(); R("Umgebung [°C]", self.amb_c)
|
||||
self.cold_k = tk.DoubleVar(); R("Kalt-Leerlauf +/°C [RPM/°C]", self.cold_k)
|
||||
self.cold_max=tk.DoubleVar(); R("Kalt-Leerlauf max [RPM]", self.cold_max)
|
||||
|
||||
# Thermik (analog – Variablen ohne Defaults anlegen) ...
|
||||
self.amb_c = tk.DoubleVar(); self.c_warm = tk.DoubleVar(); self.c_cool = tk.DoubleVar()
|
||||
self.o_warm = tk.DoubleVar(); self.o_cool = tk.DoubleVar()
|
||||
self.cold_gain = tk.DoubleVar(); self.cold_gain_max = tk.DoubleVar()
|
||||
# (Labels/Entries spar ich hier ab – wie gehabt weiterführen)
|
||||
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
|
||||
|
||||
# Öl, DBW, Jitter, Pedal
|
||||
self.o_idle = tk.DoubleVar(); self.o_slope = tk.DoubleVar(); self.o_floor = tk.DoubleVar()
|
||||
self.plate_idle_min = tk.DoubleVar(); self.plate_overrun = tk.DoubleVar(); self.plate_tau = tk.DoubleVar()
|
||||
self.torque_kp = tk.DoubleVar(); self.torque_ki = tk.DoubleVar()
|
||||
self.jitter_idle = tk.DoubleVar(); self.jitter_high = tk.DoubleVar()
|
||||
self.jitter_tau = tk.DoubleVar(); self.jitter_off = tk.DoubleVar()
|
||||
self.pedal = tk.DoubleVar(); R("Gaspedal [%]", self.pedal, kind="scale")
|
||||
|
||||
ttk.Label(self.frame, text="Gaspedal [%]").grid(row=row, column=0, sticky="w"); row+=1
|
||||
self.pedal_var = tk.DoubleVar()
|
||||
self.pedal_scale = ttk.Scale(self.frame, from_=0.0, to=100.0, variable=self.pedal_var)
|
||||
self.pedal_scale.grid(row=row-1, column=1, sticky="ew")
|
||||
# ---------- 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))
|
||||
|
||||
# Buttons
|
||||
row += 1
|
||||
btnrow = ttk.Frame(self.frame); btnrow.grid(row=row, column=0, columnspan=2, sticky="w", pady=(8,0))
|
||||
ttk.Button(btnrow, text="Aktualisieren", command=self.refresh).pack(side="left")
|
||||
ttk.Button(btnrow, text="Anwenden", command=self.apply).pack(side="left", padx=(8,0))
|
||||
|
||||
# Zum Start einmal „live“ laden:
|
||||
self.refresh()
|
||||
|
||||
# liest IMMER effektiv: config.get(key, ENGINE_DEFAULTS[key])
|
||||
def _on_pedal_change(self):
|
||||
try: self.sim.v.set("throttle_pedal_pct", float(self.pedal.get()))
|
||||
except: pass
|
||||
|
||||
def refresh(self):
|
||||
e = dict(ENGINE_DEFAULTS)
|
||||
e.update(self.sim.v.config.get("engine", {})) # Config über default mergen
|
||||
e = dict(ENGINE_DEFAULTS); e.update(self.sim.v.config.get("engine", {}))
|
||||
|
||||
self.idle_var.set(e["idle_rpm"])
|
||||
self.maxrpm_var.set(e["max_rpm"])
|
||||
self.rise_var.set(e["rpm_rise_per_s"])
|
||||
self.fall_var.set(e["rpm_fall_per_s"])
|
||||
self.thr_curve.set(e["throttle_curve"])
|
||||
self.power_kw.set(e["engine_power_kw"])
|
||||
self.peak_rpm.set(e["torque_peak_rpm"])
|
||||
# links
|
||||
self.idle.set(e["idle_rpm"])
|
||||
self.maxrpm.set(e["max_rpm"])
|
||||
self.rise.set(e["rpm_rise_per_s"])
|
||||
self.fall.set(e["rpm_fall_per_s"])
|
||||
self.curve.set(e["throttle_curve"])
|
||||
|
||||
self.starter_nom.set(e["starter_rpm_nominal"])
|
||||
self.starter_vmin.set(e["starter_voltage_min"])
|
||||
self.start_th.set(e["start_rpm_threshold"])
|
||||
self.stall_rpm.set(e["stall_rpm"])
|
||||
self.power.set(e["engine_power_kw"])
|
||||
self.tqpeak.set(e["torque_peak_rpm"])
|
||||
|
||||
self.amb_c.set(e["coolant_ambient_c"])
|
||||
self.c_warm.set(e["coolant_warm_rate_c_per_s"])
|
||||
self.c_cool.set(e["coolant_cool_rate_c_per_s"])
|
||||
self.o_warm.set(e["oil_warm_rate_c_per_s"])
|
||||
self.o_cool.set(e["oil_cool_rate_c_per_s"])
|
||||
self.cold_gain.set(e["idle_cold_gain_per_deg"])
|
||||
self.cold_gain_max.set(e["idle_cold_gain_max"])
|
||||
self.st_nom.set(e["starter_rpm_nominal"])
|
||||
self.st_vmin.set(e["starter_voltage_min"])
|
||||
self.st_thr.set(e["start_rpm_threshold"])
|
||||
self.stall.set(e["stall_rpm"])
|
||||
|
||||
self.o_idle.set(e["oil_pressure_idle_bar"])
|
||||
self.o_slope.set(e["oil_pressure_slope_bar_per_krpm"])
|
||||
self.o_floor.set(e["oil_pressure_off_floor_bar"])
|
||||
|
||||
self.plate_idle_min.set(e["throttle_plate_idle_min_pct"])
|
||||
self.plate_overrun.set(e["throttle_plate_overrun_pct"])
|
||||
self.plate_tau.set(e["throttle_plate_tau_s"])
|
||||
self.torque_kp.set(e["torque_ctrl_kp"])
|
||||
self.torque_ki.set(e["torque_ctrl_ki"])
|
||||
# rechts
|
||||
self.dk_idle.set(e["throttle_plate_idle_min_pct"])
|
||||
self.dk_over.set(e["throttle_plate_overrun_pct"])
|
||||
self.dk_tau.set(e["throttle_plate_tau_s"])
|
||||
self.tq_kp.set(e["torque_ctrl_kp"])
|
||||
self.tq_ki.set(e["torque_ctrl_ki"])
|
||||
|
||||
self.jitter_idle.set(e["rpm_jitter_idle_amp_rpm"])
|
||||
self.jitter_high.set(e["rpm_jitter_high_amp_rpm"])
|
||||
self.jitter_tau.set(e["rpm_jitter_tau_s"])
|
||||
self.jitter_off.set(e["rpm_jitter_off_threshold_rpm"])
|
||||
self.jit_idle.set(e["rpm_jitter_idle_amp_rpm"])
|
||||
self.jit_high.set(e["rpm_jitter_high_amp_rpm"])
|
||||
self.jit_tau.set(e["rpm_jitter_tau_s"])
|
||||
self.jit_off.set(e["rpm_jitter_off_threshold_rpm"])
|
||||
|
||||
self.pedal_var.set(e["throttle_pedal_pct"])
|
||||
self.amb_c.set(e["coolant_ambient_c"])
|
||||
self.cold_k.set(e["idle_cold_gain_per_deg"])
|
||||
self.cold_max.set(e["idle_cold_gain_max"])
|
||||
|
||||
self.pedal.set(e["throttle_pedal_pct"])
|
||||
self._on_pedal_change()
|
||||
|
||||
def apply(self):
|
||||
# Nur hier wird geschrieben
|
||||
cfg = {"engine": {
|
||||
"idle_rpm": int(self.idle_var.get()),
|
||||
"max_rpm": int(self.maxrpm_var.get()),
|
||||
"rpm_rise_per_s": int(self.rise_var.get()),
|
||||
"rpm_fall_per_s": int(self.fall_var.get()),
|
||||
"throttle_curve": self.thr_curve.get(),
|
||||
"engine_power_kw": float(self.power_kw.get()),
|
||||
"torque_peak_rpm": float(self.peak_rpm.get()),
|
||||
"starter_rpm_nominal": float(self.starter_nom.get()),
|
||||
"starter_voltage_min": float(self.starter_vmin.get()),
|
||||
"start_rpm_threshold": float(self.start_th.get()),
|
||||
"stall_rpm": float(self.stall_rpm.get()),
|
||||
"coolant_ambient_c": float(self.amb_c.get()),
|
||||
"coolant_warm_rate_c_per_s": float(self.c_warm.get()),
|
||||
"coolant_cool_rate_c_per_s": float(self.c_cool.get()),
|
||||
"oil_warm_rate_c_per_s": float(self.o_warm.get()),
|
||||
"oil_cool_rate_c_per_s": float(self.o_cool.get()),
|
||||
"idle_cold_gain_per_deg": float(self.cold_gain.get()),
|
||||
"idle_cold_gain_max": float(self.cold_gain_max.get()),
|
||||
"idle_rpm": int(self.idle.get()),
|
||||
"max_rpm": int(self.maxrpm.get()),
|
||||
"rpm_rise_per_s": int(self.rise.get()),
|
||||
"rpm_fall_per_s": int(self.fall.get()),
|
||||
"throttle_curve": self.curve.get(),
|
||||
|
||||
"engine_power_kw": float(self.power.get()),
|
||||
"torque_peak_rpm": float(self.tqpeak.get()),
|
||||
|
||||
"starter_rpm_nominal": float(self.st_nom.get()),
|
||||
"starter_voltage_min": float(self.st_vmin.get()),
|
||||
"start_rpm_threshold": float(self.st_thr.get()),
|
||||
"stall_rpm": float(self.stall.get()),
|
||||
|
||||
"oil_pressure_idle_bar": float(self.o_idle.get()),
|
||||
"oil_pressure_slope_bar_per_krpm": float(self.o_slope.get()),
|
||||
"oil_pressure_off_floor_bar": float(self.o_floor.get()),
|
||||
"throttle_plate_idle_min_pct": float(self.plate_idle_min.get()),
|
||||
"throttle_plate_overrun_pct": float(self.plate_overrun.get()),
|
||||
"throttle_plate_tau_s": float(self.plate_tau.get()),
|
||||
"torque_ctrl_kp": float(self.torque_kp.get()),
|
||||
"torque_ctrl_ki": float(self.torque_ki.get()),
|
||||
"rpm_jitter_idle_amp_rpm": float(self.jitter_idle.get()),
|
||||
"rpm_jitter_high_amp_rpm": float(self.jitter_high.get()),
|
||||
"rpm_jitter_tau_s": float(self.jitter_tau.get()),
|
||||
"rpm_jitter_off_threshold_rpm": float(self.jitter_off.get()),
|
||||
"throttle_pedal_pct": float(self.pedal_var.get()),
|
||||
|
||||
"throttle_plate_idle_min_pct": float(self.dk_idle.get()),
|
||||
"throttle_plate_overrun_pct": float(self.dk_over.get()),
|
||||
"throttle_plate_tau_s": float(self.dk_tau.get()),
|
||||
"torque_ctrl_kp": float(self.tq_kp.get()),
|
||||
"torque_ctrl_ki": float(self.tq_ki.get()),
|
||||
|
||||
"rpm_jitter_idle_amp_rpm": float(self.jit_idle.get()),
|
||||
"rpm_jitter_high_amp_rpm": float(self.jit_high.get()),
|
||||
"rpm_jitter_tau_s": float(self.jit_tau.get()),
|
||||
"rpm_jitter_off_threshold_rpm": float(self.jit_off.get()),
|
||||
|
||||
"coolant_ambient_c": float(self.amb_c.get()),
|
||||
"idle_cold_gain_per_deg": float(self.cold_k.get()),
|
||||
"idle_cold_gain_max": float(self.cold_max.get()),
|
||||
|
||||
"throttle_pedal_pct": float(self.pedal.get()),
|
||||
}}
|
||||
self.sim.load_config(cfg)
|
||||
|
@@ -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