update of trace_signal_fitter.py and some doc
All checks were successful
CI-Build/Kettenoeler/pipeline/head This commit looks good
All checks were successful
CI-Build/Kettenoeler/pipeline/head This commit looks good
This commit is contained in:
@@ -699,95 +699,197 @@ class TabTraceBatch(ttk.Frame):
|
||||
|
||||
def _append(self, s): self.txt.insert(tk.END, s); self.txt.see(tk.END)
|
||||
|
||||
|
||||
# ---------------- Tab 4: Range-Fit (supervised + unsupervised) ----------------
|
||||
# ---------------- Tab 4: Range-Fit (supervised + unsupervised, mit Physik-Constraints) ----------------
|
||||
class TabRangeFit(ttk.Frame):
|
||||
def __init__(self, master, state: AppState, header: Header):
|
||||
super().__init__(master, padding=10)
|
||||
self.state = state
|
||||
self.header = header
|
||||
self._last_outdir = None
|
||||
self._build_ui()
|
||||
|
||||
def _build_ui(self):
|
||||
self.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(2, weight=1)
|
||||
self.rowconfigure(3, weight=1)
|
||||
|
||||
# unified trace panel (single-select)
|
||||
self.trace_panel = TracePanel(self, self.state, title="Trace wählen (Single)", single_select=True, height=8)
|
||||
self.trace_panel = TracePanel(self, self.state, title="Trace wählen (Single)", single_select=True, height=10)
|
||||
self.trace_panel.grid(row=0, column=0, sticky="nsew", padx=5, pady=(5,10))
|
||||
|
||||
# Parameter
|
||||
frm_params = ttk.LabelFrame(self, text="Range-/Unsupervised-Fit Parameter")
|
||||
# Parameter Frames
|
||||
frm_params = ttk.Frame(self)
|
||||
frm_params.grid(row=1, column=0, sticky="nsew", padx=5, pady=5)
|
||||
for c in (1,3,5):
|
||||
for c in range(6):
|
||||
frm_params.columnconfigure(c, weight=1)
|
||||
|
||||
# Range (leer lassen => unsupervised)
|
||||
ttk.Label(frm_params, text="Range-Min (leer = unsupervised)").grid(row=0, column=0, sticky="e")
|
||||
# --- Supervised (Range & Physik) ---
|
||||
box_sup = ttk.LabelFrame(frm_params, text="Supervised (Range-Fit) – lasse leer für Unsupervised")
|
||||
box_sup.grid(row=0, column=0, columnspan=6, sticky="nsew", padx=5, pady=5)
|
||||
for c in range(6):
|
||||
box_sup.columnconfigure(c, weight=1)
|
||||
|
||||
ttk.Label(box_sup, text="Range-Min").grid(row=0, column=0, sticky="e")
|
||||
self.rmin = tk.StringVar(value="")
|
||||
ttk.Entry(frm_params, textvariable=self.rmin, width=12).grid(row=0, column=1, sticky="w", padx=5)
|
||||
ttk.Entry(box_sup, textvariable=self.rmin, width=12).grid(row=0, column=1, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(frm_params, text="Range-Max (leer = unsupervised)").grid(row=0, column=2, sticky="e")
|
||||
ttk.Label(box_sup, text="Range-Max").grid(row=0, column=2, sticky="e")
|
||||
self.rmax = tk.StringVar(value="")
|
||||
ttk.Entry(frm_params, textvariable=self.rmax, width=12).grid(row=0, column=3, sticky="w", padx=5)
|
||||
ttk.Entry(box_sup, textvariable=self.rmax, width=12).grid(row=0, column=3, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(frm_params, text="Min. Hit-Ratio (Range-Fit)").grid(row=0, column=4, sticky="e")
|
||||
ttk.Label(box_sup, text="Min. Hit-Ratio (0..1)").grid(row=0, column=4, sticky="e")
|
||||
self.min_hit = tk.DoubleVar(value=0.5)
|
||||
ttk.Entry(frm_params, textvariable=self.min_hit, width=10).grid(row=0, column=5, sticky="w", padx=5)
|
||||
ttk.Entry(box_sup, textvariable=self.min_hit, width=10).grid(row=0, column=5, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(frm_params, text="Min. Smoothness (Unsupervised)").grid(row=1, column=0, sticky="e")
|
||||
self.allow_neg = tk.BooleanVar(value=False)
|
||||
ttk.Checkbutton(box_sup, text="negative Scale erlauben", variable=self.allow_neg).grid(row=1, column=0, columnspan=2, sticky="w")
|
||||
|
||||
ttk.Label(box_sup, text="Rate-Min (Hz)").grid(row=1, column=2, sticky="e")
|
||||
self.rate_min = tk.StringVar(value="")
|
||||
ttk.Entry(box_sup, textvariable=self.rate_min, width=10).grid(row=1, column=3, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(box_sup, text="Rate-Max (Hz)").grid(row=1, column=4, sticky="e")
|
||||
self.rate_max = tk.StringVar(value="")
|
||||
ttk.Entry(box_sup, textvariable=self.rate_max, width=10).grid(row=1, column=5, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(box_sup, text="Jitter-Max (ms)").grid(row=2, column=0, sticky="e")
|
||||
self.jitter_max = tk.StringVar(value="")
|
||||
ttk.Entry(box_sup, textvariable=self.jitter_max, width=10).grid(row=2, column=1, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(box_sup, text="Max-Slope-Abs (phys/s)").grid(row=2, column=2, sticky="e")
|
||||
self.slope_abs = tk.StringVar(value="")
|
||||
ttk.Entry(box_sup, textvariable=self.slope_abs, width=12).grid(row=2, column=3, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(box_sup, text="Max-Slope-Frac (/s)").grid(row=2, column=4, sticky="e")
|
||||
self.slope_frac = tk.StringVar(value="")
|
||||
ttk.Entry(box_sup, textvariable=self.slope_frac, width=12).grid(row=2, column=5, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(box_sup, text="Slope-Quantile").grid(row=3, column=0, sticky="e")
|
||||
self.slope_q = tk.DoubleVar(value=0.95) # 0.95 oder 0.99
|
||||
ttk.Entry(box_sup, textvariable=self.slope_q, width=10).grid(row=3, column=1, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(box_sup, text="Min-Unique-Ratio").grid(row=3, column=2, sticky="e")
|
||||
self.min_uniq = tk.StringVar(value="")
|
||||
ttk.Entry(box_sup, textvariable=self.min_uniq, width=10).grid(row=3, column=3, sticky="w", padx=5)
|
||||
|
||||
# --- Unsupervised ---
|
||||
box_uns = ttk.LabelFrame(frm_params, text="Unsupervised (ohne Range)")
|
||||
box_uns.grid(row=1, column=0, columnspan=6, sticky="nsew", padx=5, pady=5)
|
||||
for c in range(6):
|
||||
box_uns.columnconfigure(c, weight=1)
|
||||
|
||||
ttk.Label(box_uns, text="Min. Smoothness (0..1)").grid(row=0, column=0, sticky="e")
|
||||
self.min_smooth = tk.DoubleVar(value=0.2)
|
||||
ttk.Entry(frm_params, textvariable=self.min_smooth, width=10).grid(row=1, column=1, sticky="w", padx=5)
|
||||
ttk.Entry(box_uns, textvariable=self.min_smooth, width=12).grid(row=0, column=1, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(box_uns, text="Max-Slope-Frac-RAW (/s)").grid(row=0, column=2, sticky="e")
|
||||
self.max_slope_frac_raw = tk.StringVar(value="")
|
||||
ttk.Entry(box_uns, textvariable=self.max_slope_frac_raw, width=12).grid(row=0, column=3, sticky="w", padx=5)
|
||||
|
||||
# --- Allgemein/Output ---
|
||||
box_out = ttk.LabelFrame(frm_params, text="Allgemein & Output")
|
||||
box_out.grid(row=2, column=0, columnspan=6, sticky="nsew", padx=5, pady=5)
|
||||
for c in range(6):
|
||||
box_out.columnconfigure(c, weight=1)
|
||||
|
||||
self.rx_only = tk.BooleanVar(value=False)
|
||||
self.neg_scale = tk.BooleanVar(value=False) # nur bei Range-Fit
|
||||
ttk.Checkbutton(frm_params, text="nur RX", variable=self.rx_only).grid(row=1, column=2, sticky="w")
|
||||
ttk.Checkbutton(frm_params, text="negative Scale erlauben (Range-Fit)", variable=self.neg_scale).grid(row=1, column=3, sticky="w")
|
||||
ttk.Checkbutton(box_out, text="nur RX", variable=self.rx_only).grid(row=0, column=0, sticky="w")
|
||||
|
||||
ttk.Label(frm_params, text="Plots Top-N").grid(row=1, column=4, sticky="e")
|
||||
ttk.Label(box_out, text="Plots Top-N").grid(row=0, column=1, sticky="e")
|
||||
self.plots_top = tk.IntVar(value=8)
|
||||
ttk.Entry(frm_params, textvariable=self.plots_top, width=10).grid(row=1, column=5, sticky="w", padx=5)
|
||||
ttk.Entry(box_out, textvariable=self.plots_top, width=10).grid(row=0, column=2, sticky="w", padx=5)
|
||||
|
||||
ttk.Label(frm_params, text="Output-Label").grid(row=2, column=0, sticky="e")
|
||||
ttk.Label(box_out, text="Output-Label").grid(row=0, column=3, sticky="e")
|
||||
self.out_label = tk.StringVar(value="rangefit")
|
||||
ttk.Entry(frm_params, textvariable=self.out_label, width=16).grid(row=2, column=1, sticky="w", padx=5)
|
||||
ttk.Entry(box_out, textvariable=self.out_label, width=18).grid(row=0, column=4, sticky="w", padx=5)
|
||||
|
||||
self.use_ts = tk.BooleanVar(value=True)
|
||||
ttk.Checkbutton(frm_params, text="Zeitstempel-Unterordner", variable=self.use_ts).grid(row=2, column=2, sticky="w", padx=2)
|
||||
ttk.Checkbutton(box_out, text="Zeitstempel-Unterordner", variable=self.use_ts).grid(row=0, column=5, sticky="w")
|
||||
|
||||
# Start + Konsole
|
||||
# Start + Konsole + Aktionen
|
||||
frm_run = ttk.Frame(self)
|
||||
frm_run.grid(row=3, column=0, sticky="ew", padx=5, pady=5)
|
||||
frm_run.grid(row=2, column=0, sticky="ew", padx=5, pady=5)
|
||||
ttk.Button(frm_run, text="Start Range-/Unsupervised-Fit", command=self._on_run).pack(side="left", padx=5)
|
||||
ttk.Button(frm_run, text="Report öffnen", command=self._open_last_report).pack(side="left", padx=5)
|
||||
ttk.Button(frm_run, text="Output-Ordner öffnen", command=self._open_last_outdir).pack(side="left", padx=5)
|
||||
|
||||
frm_out = ttk.LabelFrame(self, text="Ausgabe")
|
||||
frm_out.grid(row=4, column=0, sticky="nsew", padx=5, pady=5)
|
||||
frm_out.grid(row=3, column=0, sticky="nsew", padx=5, pady=5)
|
||||
frm_out.columnconfigure(0, weight=1); frm_out.rowconfigure(0, weight=1)
|
||||
self.txt = tk.Text(frm_out, height=12); self.txt.grid(row=0, column=0, sticky="nsew")
|
||||
self.txt = tk.Text(frm_out, height=14); self.txt.grid(row=0, column=0, sticky="nsew")
|
||||
sbo = ttk.Scrollbar(frm_out, orient="vertical", command=self.txt.yview); sbo.grid(row=0, column=1, sticky="ns")
|
||||
self.txt.configure(yscrollcommand=sbo.set)
|
||||
|
||||
# --- helpers ---
|
||||
def _append(self, s):
|
||||
self.txt.insert(tk.END, s); self.txt.see(tk.END)
|
||||
|
||||
def _on_run(self):
|
||||
def _stamp(self):
|
||||
import datetime as _dt
|
||||
return _dt.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
def _build_outdir(self, supervised: bool) -> Path:
|
||||
out_root = self.state.analyze_out_root()
|
||||
label = (self.out_label.get().strip() or ("rangefit" if supervised else "unsupervised"))
|
||||
stamp = f"{self._stamp()}_{label}" if self.use_ts.get() else label
|
||||
outdir = out_root / stamp
|
||||
outdir.mkdir(parents=True, exist_ok=True)
|
||||
self._last_outdir = outdir
|
||||
return outdir
|
||||
|
||||
def _selected_trace(self):
|
||||
sel = self.trace_panel.get_selected()
|
||||
if not sel:
|
||||
messagebox.showwarning("Hinweis", "Bitte genau eine .trace-Datei auswählen.")
|
||||
return
|
||||
return None
|
||||
if len(sel) != 1:
|
||||
messagebox.showwarning("Hinweis", "Range-Fit benötigt genau eine .trace-Datei (Single-Select).")
|
||||
return None
|
||||
return sel[0]
|
||||
|
||||
def _maybe(self, val: str, flag: str, args: list):
|
||||
v = (val or "").strip()
|
||||
if v != "":
|
||||
args += [flag, v]
|
||||
|
||||
def _open_path(self, p: Path):
|
||||
try:
|
||||
if sys.platform.startswith("darwin"):
|
||||
subprocess.Popen(["open", str(p)])
|
||||
elif os.name == "nt":
|
||||
os.startfile(str(p)) # type: ignore
|
||||
else:
|
||||
subprocess.Popen(["xdg-open", str(p)])
|
||||
except Exception as e:
|
||||
messagebox.showwarning("Fehler", f"Konnte nicht öffnen:\n{p}\n{e}")
|
||||
|
||||
def _open_last_outdir(self):
|
||||
if self._last_outdir and self._last_outdir.exists():
|
||||
self._open_path(self._last_outdir)
|
||||
else:
|
||||
messagebox.showinfo("Hinweis", "Noch kein Output-Ordner vorhanden.")
|
||||
|
||||
def _open_last_report(self):
|
||||
if not (self._last_outdir and self._last_outdir.exists()):
|
||||
messagebox.showinfo("Hinweis", "Noch kein Report erzeugt.")
|
||||
return
|
||||
# versuche ein *_report.md im letzten Outdir zu finden
|
||||
md = list(Path(self._last_outdir).glob("*_report.md"))
|
||||
if not md:
|
||||
messagebox.showinfo("Hinweis", "Kein Report gefunden.")
|
||||
return
|
||||
self._open_path(md[0])
|
||||
|
||||
def _on_run(self):
|
||||
trace = self._selected_trace()
|
||||
if not trace:
|
||||
return
|
||||
|
||||
trace = sel[0]
|
||||
# supervised?
|
||||
rmin = self.rmin.get().strip()
|
||||
rmax = self.rmax.get().strip()
|
||||
supervised = bool(rmin) and bool(rmax)
|
||||
|
||||
out_root = self.state.analyze_out_root()
|
||||
label = (self.out_label.get().strip() or ("rangefit" if supervised else "unsupervised"))
|
||||
stamp = f"{now_stamp()}_{label}" if self.use_ts.get() else label
|
||||
outdir = ensure_dir(out_root / stamp)
|
||||
outdir = self._build_outdir(supervised)
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
@@ -801,25 +903,38 @@ class TabRangeFit(ttk.Frame):
|
||||
|
||||
if supervised:
|
||||
cmd += ["--rmin", rmin, "--rmax", rmax, "--min-hit", str(self.min_hit.get())]
|
||||
if self.neg_scale.get():
|
||||
if self.allow_neg.get():
|
||||
cmd.append("--allow-neg-scale")
|
||||
self._maybe(self.rate_min.get(), "--rate-min", cmd)
|
||||
self._maybe(self.rate_max.get(), "--rate-max", cmd)
|
||||
self._maybe(self.jitter_max.get(), "--jitter-max-ms", cmd)
|
||||
self._maybe(self.slope_abs.get(), "--max-slope-abs", cmd)
|
||||
self._maybe(self.slope_frac.get(), "--max-slope-frac", cmd)
|
||||
cmd += ["--slope-quantile", str(self.slope_q.get())]
|
||||
self._maybe(self.min_uniq.get(), "--min-uniq-ratio", cmd)
|
||||
else:
|
||||
# unsupervised
|
||||
cmd += ["--min-smooth", str(self.min_smooth.get())]
|
||||
self._maybe(self.max_slope_frac_raw.get(), "--max-slope-frac-raw", cmd)
|
||||
cmd += ["--slope-quantile", str(self.slope_q.get())] # wird intern für p95/p99 gewählt
|
||||
|
||||
self._run_cmd(cmd)
|
||||
self._append(f"\nDone. Output: {outdir}\n")
|
||||
self._append(f"\n>>> RUN: {' '.join(cmd)}\n")
|
||||
t = threading.Thread(target=self._run_cmd, args=(cmd,), daemon=True)
|
||||
t.start()
|
||||
|
||||
def _run_cmd(self, cmd):
|
||||
self._append(f"\n>>> RUN: {' '.join(cmd)}\n")
|
||||
try:
|
||||
with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) as proc:
|
||||
for line in proc.stdout: self._append(line)
|
||||
for line in proc.stdout:
|
||||
self._append(line)
|
||||
rc = proc.wait()
|
||||
if rc != 0: self._append(f"[Exit-Code {rc}]\n")
|
||||
if rc != 0:
|
||||
self._append(f"[Exit-Code {rc}]\n")
|
||||
else:
|
||||
self._append(f"\nDone. Output: {self._last_outdir}\n")
|
||||
except Exception as e:
|
||||
self._append(f"[Fehler] {e}\n")
|
||||
|
||||
|
||||
# ---------------- App Shell ----------------
|
||||
class App(tk.Tk):
|
||||
def __init__(self):
|
||||
|
Reference in New Issue
Block a user