GUI sweep: supply-trip protection after the 80V OVP shutdown

Run 2026-07-06 13:08: stepping 78->80V released the 46A load mid-ramp,
the supply output overshot to 80.46V and a protection (OVP by all log
evidence) shut it down; the sweep then logged garbage rows for the rest
of the grid. Three fixes:

- release the load down to l_start BEFORE each supply voltage step
  (down-steps only; descending load sweeps keep the gated path)
- OVP preflight: query the programmed OVP level at sweep start, abort
  under 2V margin above the sweep max, warn under 5V
- dead-supply abort: supply readback ~0V mid-sweep drops the garbage
  point, queries which protection fired (OVP/OV/OC/OP/OT) via
  _supply_trip_cause, reports it, and stops -- collected points are
  still ramped down and saved

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
janik
2026-07-06 14:49:07 +07:00
co-authored by Claude Fable 5
parent 668ce30a1d
commit 5b168439e8
2 changed files with 73 additions and 0 deletions
+10
View File
@@ -106,6 +106,16 @@ The GUI provides:
backstop additionally drops any point where the supply actually exceeded
the limit and backs the load off. Manual CC/CP load setpoints are checked
against the same limit using live Vin/Vout readings.
- Supply-trip protection: before each voltage step the load is released
back to I start first, so a heavy load release never lands mid-ramp
(that overshoots the setpoint -- a 46 A release ramping to 80 V peaked
at 80.46 V and tripped the supply OVP). At sweep start the supply's
programmed OVP level is checked against the sweep maximum (abort under
2 V margin, warn under 5 V). If the supply reads ~0 V mid-sweep (a
protection shut the output down), the sweep aborts immediately, reports
which protection fired (OVP / OV / OC / OP / OT), and saves the points
collected so far instead of logging garbage rows for the rest of the
grid.
- Load range pinning: a mid-sweep auto-range transition on the Prodigit
momentarily unloads the converter, so at sweep start the CC range is
pinned to Range II for the whole run (auto-ranging restored after, with
+63
View File
@@ -1715,6 +1715,22 @@ class TestbenchGUI(tk.Tk):
return None
return b.etemp, b.btemp
@staticmethod
def _supply_trip_cause(bench) -> str:
"""Best-effort query of which supply protection fired."""
causes = []
try:
if bench.supply.get_ovp_tripped():
causes.append("OVP")
except Exception:
pass
try:
qs = bench.supply.get_questionable_status()
causes += [k for k, hit in qs.items() if hit]
except Exception:
pass
return "/".join(dict.fromkeys(causes)) or "unknown"
def _thermal_hold(self, bench, load_mode: str, vout_est: float,
stop: threading.Event) -> float | None:
"""Pause the sweep while etemp/btemp are near the firmware trips.
@@ -1792,6 +1808,26 @@ class TestbenchGUI(tk.Tk):
max_load_setpoint=max_l,
)
# OVP preflight: a sweep whose top voltage sits at/above the
# supply's programmed OVP is a guaranteed trip, and transient
# overshoot eats into the margin (a 46A load release mid-ramp
# overshot the setpoint by ~0.5V and tripped OVP, run 2026-07-06).
try:
ovp = (bench.supply.get_ovp_level()
if bench.supply.get_ovp_state() else None)
except Exception as e:
self._console(f"OVP preflight skipped ({e})", "warn")
ovp = None
if ovp is not None:
if ovp < max_v + 2.0:
raise ValueError(
f"supply OVP {ovp:g}V leaves no margin above the sweep "
f"max {max_v:g}V - raise OVP or lower V stop")
if ovp < max_v + 5.0:
self._console(
f"Supply OVP {ovp:g}V is within 5V of the sweep max "
f"{max_v:g}V - transients may trip it", "warn")
if self._sweep_temps() is None:
self._console(
"STM32 not linked - thermal pause guard inactive for this "
@@ -1861,6 +1897,18 @@ class TestbenchGUI(tk.Tk):
if v_step < 0 and v < v_stop + v_step / 2:
break
# Release the load down to l_start BEFORE stepping the
# supply voltage: a heavy load release landing mid-ramp
# overshoots the setpoint (46A release while ramping to
# 80V peaked at 80.46V and tripped the supply, run
# 2026-07-06). Down-steps only -- for descending load
# sweeps l_start is the heavy end and gets applied after
# the voltage step through the usual feasibility gate.
if abs(applied) > abs(l_start):
bench._apply_load_value(load_mode, l_start)
applied = l_start
time.sleep(0.5)
bench.supply.set_voltage(v)
ll = l_start
rejected = 0
@@ -1896,6 +1944,21 @@ class TestbenchGUI(tk.Tk):
point = bench._record_point(v, current_limit, load_setpoint=ll)
results.append(point)
n += 1
# Dead-supply abort: ~0V readback with the output
# supposed to be ON means a protection shut it down.
# Report which one and stop instead of logging garbage
# rows for the rest of the grid (collected points are
# still saved).
if v > 5.0 and point.supply_voltage < 1.0:
results.pop() # this point is garbage
n -= 1
cause = self._supply_trip_cause(bench)
self._console(
f"Supply output is OFF at V={v:g}V (protection: "
f"{cause}) - aborting sweep, saving "
f"{n} collected point(s)", "error")
stop.set()
break
if point.load_voltage > 5.0:
vout_est = point.load_voltage
# Measured backstop: the estimate can be off (eff, Vout)