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 backstop additionally drops any point where the supply actually exceeded
the limit and backs the load off. Manual CC/CP load setpoints are checked the limit and backs the load off. Manual CC/CP load setpoints are checked
against the same limit using live Vin/Vout readings. 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 - Load range pinning: a mid-sweep auto-range transition on the Prodigit
momentarily unloads the converter, so at sweep start the CC range is 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 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 None
return b.etemp, b.btemp 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, def _thermal_hold(self, bench, load_mode: str, vout_est: float,
stop: threading.Event) -> float | None: stop: threading.Event) -> float | None:
"""Pause the sweep while etemp/btemp are near the firmware trips. """Pause the sweep while etemp/btemp are near the firmware trips.
@@ -1792,6 +1808,26 @@ class TestbenchGUI(tk.Tk):
max_load_setpoint=max_l, 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: if self._sweep_temps() is None:
self._console( self._console(
"STM32 not linked - thermal pause guard inactive for this " "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: if v_step < 0 and v < v_stop + v_step / 2:
break 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) bench.supply.set_voltage(v)
ll = l_start ll = l_start
rejected = 0 rejected = 0
@@ -1896,6 +1944,21 @@ class TestbenchGUI(tk.Tk):
point = bench._record_point(v, current_limit, load_setpoint=ll) point = bench._record_point(v, current_limit, load_setpoint=ll)
results.append(point) results.append(point)
n += 1 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: if point.load_voltage > 5.0:
vout_est = point.load_voltage vout_est = point.load_voltage
# Measured backstop: the estimate can be off (eff, Vout) # Measured backstop: the estimate can be off (eff, Vout)