Add CP load mode to 2D sweep and supply sanity checks
- 2D sweep now supports CC (constant current) and CP (constant power) load modes via --load-mode flag (CLI) and combobox (GUI) - Supply capability check before all sweeps: validates max voltage against supply range and prints V/I/P summary - Renamed sweep-vi args from --i-start/stop/step to --l-start/stop/step to reflect that the load setpoint can be current or power - GUI labels update dynamically based on selected load mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -316,6 +316,9 @@ class MPPTTestbench:
|
|||||||
if v_step == 0:
|
if v_step == 0:
|
||||||
raise ValueError("v_step cannot be zero")
|
raise ValueError("v_step cannot be zero")
|
||||||
|
|
||||||
|
max_v = max(abs(v_start), abs(v_stop))
|
||||||
|
self.check_supply_capability(max_v, current_limit)
|
||||||
|
|
||||||
# Auto-correct step direction
|
# Auto-correct step direction
|
||||||
if v_start < v_stop and v_step < 0:
|
if v_start < v_stop and v_step < 0:
|
||||||
v_step = -v_step
|
v_step = -v_step
|
||||||
@@ -378,6 +381,8 @@ class MPPTTestbench:
|
|||||||
if i_step == 0:
|
if i_step == 0:
|
||||||
raise ValueError("i_step cannot be zero")
|
raise ValueError("i_step cannot be zero")
|
||||||
|
|
||||||
|
self.check_supply_capability(voltage, current_limit)
|
||||||
|
|
||||||
# Auto-correct step direction
|
# Auto-correct step direction
|
||||||
if i_start < i_stop and i_step < 0:
|
if i_start < i_stop and i_step < 0:
|
||||||
i_step = -i_step
|
i_step = -i_step
|
||||||
@@ -553,6 +558,33 @@ class MPPTTestbench:
|
|||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
# ── Sanity checks ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def check_supply_capability(
|
||||||
|
self,
|
||||||
|
max_voltage: float,
|
||||||
|
current_limit: float,
|
||||||
|
) -> None:
|
||||||
|
"""Check that the supply can deliver the requested V and I.
|
||||||
|
|
||||||
|
Queries the supply's voltage range (max V) and verifies the
|
||||||
|
requested parameters are within bounds. Raises ValueError if not.
|
||||||
|
"""
|
||||||
|
supply_max_v = self.supply.get_voltage_range()
|
||||||
|
if max_voltage > supply_max_v:
|
||||||
|
raise ValueError(
|
||||||
|
f"Requested voltage {max_voltage:.1f}V exceeds supply "
|
||||||
|
f"range {supply_max_v:.1f}V"
|
||||||
|
)
|
||||||
|
|
||||||
|
max_power = max_voltage * current_limit
|
||||||
|
print(
|
||||||
|
f" Supply check: V_max={max_voltage:.1f}V, "
|
||||||
|
f"I_limit={current_limit:.1f}A, "
|
||||||
|
f"P_max={max_power:.0f}W "
|
||||||
|
f"(range {supply_max_v:.0f}V)"
|
||||||
|
)
|
||||||
|
|
||||||
# ── 2D Voltage × Current Sweep ─────────────────────────────────────
|
# ── 2D Voltage × Current Sweep ─────────────────────────────────────
|
||||||
|
|
||||||
def sweep_vi(
|
def sweep_vi(
|
||||||
@@ -560,16 +592,17 @@ class MPPTTestbench:
|
|||||||
v_start: float,
|
v_start: float,
|
||||||
v_stop: float,
|
v_stop: float,
|
||||||
v_step: float,
|
v_step: float,
|
||||||
i_start: float,
|
l_start: float,
|
||||||
i_stop: float,
|
l_stop: float,
|
||||||
i_step: float,
|
l_step: float,
|
||||||
current_limit: float,
|
current_limit: float,
|
||||||
settle_time: float = 2.0,
|
settle_time: float = 2.0,
|
||||||
|
load_mode: str = "CC",
|
||||||
) -> list[SweepPoint]:
|
) -> list[SweepPoint]:
|
||||||
"""2D sweep: voltage (outer) × load current (inner).
|
"""2D sweep: voltage (outer) × load setpoint (inner).
|
||||||
|
|
||||||
At each supply voltage, sweeps the load current through the full
|
At each supply voltage, sweeps the load through the full
|
||||||
range, recording efficiency at every (V, I) combination. Produces
|
range, recording efficiency at every combination. Produces
|
||||||
a complete efficiency map of the DUT.
|
a complete efficiency map of the DUT.
|
||||||
|
|
||||||
After the sweep the load turns OFF and the supply returns to
|
After the sweep the load turns OFF and the supply returns to
|
||||||
@@ -579,16 +612,25 @@ class MPPTTestbench:
|
|||||||
v_start: Starting supply voltage (V).
|
v_start: Starting supply voltage (V).
|
||||||
v_stop: Final supply voltage (V).
|
v_stop: Final supply voltage (V).
|
||||||
v_step: Voltage step size (V). Sign is auto-corrected.
|
v_step: Voltage step size (V). Sign is auto-corrected.
|
||||||
i_start: Starting load current (A).
|
l_start: Starting load setpoint (A for CC, W for CP).
|
||||||
i_stop: Final load current (A).
|
l_stop: Final load setpoint.
|
||||||
i_step: Current step size (A). Sign is auto-corrected.
|
l_step: Load step size. Sign is auto-corrected.
|
||||||
current_limit: Supply current limit (A) for the entire sweep.
|
current_limit: Supply current limit (A) for the entire sweep.
|
||||||
settle_time: Seconds to wait after each setpoint change.
|
settle_time: Seconds to wait after each setpoint change.
|
||||||
|
load_mode: Load mode — "CC" (constant current) or "CP"
|
||||||
|
(constant power).
|
||||||
"""
|
"""
|
||||||
if v_step == 0:
|
if v_step == 0:
|
||||||
raise ValueError("v_step cannot be zero")
|
raise ValueError("v_step cannot be zero")
|
||||||
if i_step == 0:
|
if l_step == 0:
|
||||||
raise ValueError("i_step cannot be zero")
|
raise ValueError("l_step cannot be zero")
|
||||||
|
load_mode = load_mode.upper()
|
||||||
|
if load_mode not in ("CC", "CP"):
|
||||||
|
raise ValueError(f"load_mode must be CC or CP, got {load_mode!r}")
|
||||||
|
|
||||||
|
# Sanity check
|
||||||
|
max_v = max(abs(v_start), abs(v_stop))
|
||||||
|
self.check_supply_capability(max_v, current_limit)
|
||||||
|
|
||||||
# Auto-correct step directions
|
# Auto-correct step directions
|
||||||
if v_start < v_stop and v_step < 0:
|
if v_start < v_stop and v_step < 0:
|
||||||
@@ -596,21 +638,25 @@ class MPPTTestbench:
|
|||||||
elif v_start > v_stop and v_step > 0:
|
elif v_start > v_stop and v_step > 0:
|
||||||
v_step = -v_step
|
v_step = -v_step
|
||||||
|
|
||||||
if i_start < i_stop and i_step < 0:
|
if l_start < l_stop and l_step < 0:
|
||||||
i_step = -i_step
|
l_step = -l_step
|
||||||
elif i_start > i_stop and i_step > 0:
|
elif l_start > l_stop and l_step > 0:
|
||||||
i_step = -i_step
|
l_step = -l_step
|
||||||
|
|
||||||
# Count steps for progress display
|
# Count steps for progress display
|
||||||
v_count = int(abs(v_stop - v_start) / abs(v_step)) + 1
|
v_count = int(abs(v_stop - v_start) / abs(v_step)) + 1
|
||||||
i_count = int(abs(i_stop - i_start) / abs(i_step)) + 1
|
l_count = int(abs(l_stop - l_start) / abs(l_step)) + 1
|
||||||
total = v_count * i_count
|
total = v_count * l_count
|
||||||
print(f" Grid: {v_count} voltage × {i_count} current = {total} points")
|
unit = "A" if load_mode == "CC" else "W"
|
||||||
|
print(
|
||||||
|
f" Grid: {v_count} voltage × {l_count} {load_mode} "
|
||||||
|
f"= {total} points"
|
||||||
|
)
|
||||||
|
|
||||||
self.supply.set_current(current_limit)
|
self.supply.set_current(current_limit)
|
||||||
self.supply.output_on()
|
self.supply.output_on()
|
||||||
self.load.set_mode("CC")
|
self.load.set_mode(load_mode)
|
||||||
self.load.set_cc_current(i_start)
|
self._apply_load_value(load_mode, l_start)
|
||||||
self.load.load_on()
|
self.load.load_on()
|
||||||
|
|
||||||
results: list[SweepPoint] = []
|
results: list[SweepPoint] = []
|
||||||
@@ -625,30 +671,30 @@ class MPPTTestbench:
|
|||||||
break
|
break
|
||||||
|
|
||||||
self.supply.set_voltage(v)
|
self.supply.set_voltage(v)
|
||||||
i = i_start
|
ll = l_start
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if i_step > 0 and i > i_stop + i_step / 2:
|
if l_step > 0 and ll > l_stop + l_step / 2:
|
||||||
break
|
break
|
||||||
if i_step < 0 and i < i_stop + i_step / 2:
|
if l_step < 0 and ll < l_stop + l_step / 2:
|
||||||
break
|
break
|
||||||
|
|
||||||
self.load.set_cc_current(i)
|
self._apply_load_value(load_mode, ll)
|
||||||
time.sleep(settle_time)
|
time.sleep(settle_time)
|
||||||
|
|
||||||
point = self._record_point(v, current_limit, load_setpoint=i)
|
point = self._record_point(v, current_limit, load_setpoint=ll)
|
||||||
results.append(point)
|
results.append(point)
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f" [{n:>4d}/{total}] "
|
f" [{n:>4d}/{total}] "
|
||||||
f"V={v:6.1f}V I_load={i:6.2f}A "
|
f"V={v:6.1f}V {load_mode}={ll:6.2f}{unit} "
|
||||||
f"P_in={point.input_power:8.2f}W "
|
f"P_in={point.input_power:8.2f}W "
|
||||||
f"P_out={point.output_power:8.2f}W "
|
f"P_out={point.output_power:8.2f}W "
|
||||||
f"EFF={point.efficiency:6.2f}%"
|
f"EFF={point.efficiency:6.2f}%"
|
||||||
)
|
)
|
||||||
|
|
||||||
i += i_step
|
ll += l_step
|
||||||
|
|
||||||
v += v_step
|
v += v_step
|
||||||
|
|
||||||
|
|||||||
@@ -439,10 +439,12 @@ def cmd_efficiency(bench: MPPTTestbench, args: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def cmd_sweep_vi(bench: MPPTTestbench, args: argparse.Namespace) -> None:
|
def cmd_sweep_vi(bench: MPPTTestbench, args: argparse.Namespace) -> None:
|
||||||
"""Run a 2D voltage × load current sweep."""
|
"""Run a 2D voltage × load sweep."""
|
||||||
|
mode = args.load_mode
|
||||||
|
unit = "A" if mode == "CC" else "W"
|
||||||
print(
|
print(
|
||||||
f"2D sweep: V={args.v_start:.1f}-{args.v_stop:.1f}V (step {args.v_step:.1f}), "
|
f"2D sweep: V={args.v_start:.1f}-{args.v_stop:.1f}V (step {args.v_step:.1f}), "
|
||||||
f"I_load={args.i_start:.2f}-{args.i_stop:.2f}A (step {args.i_step:.2f}), "
|
f"{mode}={args.l_start:.2f}-{args.l_stop:.2f}{unit} (step {args.l_step:.2f}), "
|
||||||
f"I_limit={args.current_limit:.1f}A, settle={args.settle:.1f}s"
|
f"I_limit={args.current_limit:.1f}A, settle={args.settle:.1f}s"
|
||||||
)
|
)
|
||||||
print()
|
print()
|
||||||
@@ -451,11 +453,12 @@ def cmd_sweep_vi(bench: MPPTTestbench, args: argparse.Namespace) -> None:
|
|||||||
v_start=args.v_start,
|
v_start=args.v_start,
|
||||||
v_stop=args.v_stop,
|
v_stop=args.v_stop,
|
||||||
v_step=args.v_step,
|
v_step=args.v_step,
|
||||||
i_start=args.i_start,
|
l_start=args.l_start,
|
||||||
i_stop=args.i_stop,
|
l_stop=args.l_stop,
|
||||||
i_step=args.i_step,
|
l_step=args.l_step,
|
||||||
current_limit=args.current_limit,
|
current_limit=args.current_limit,
|
||||||
settle_time=args.settle,
|
settle_time=args.settle,
|
||||||
|
load_mode=mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
_write_sweep_csv(results, args.output)
|
_write_sweep_csv(results, args.output)
|
||||||
@@ -564,7 +567,8 @@ examples:
|
|||||||
%(prog)s sweep --v-start 10 --v-stop 50 --v-step 1 --current-limit 10 -o sweep.csv
|
%(prog)s sweep --v-start 10 --v-stop 50 --v-step 1 --current-limit 10 -o sweep.csv
|
||||||
%(prog)s sweep-load --voltage 75 --current-limit 10 --i-start 1 --i-stop 20 --i-step 1 -o load.csv
|
%(prog)s sweep-load --voltage 75 --current-limit 10 --i-start 1 --i-stop 20 --i-step 1 -o load.csv
|
||||||
%(prog)s efficiency --voltage 36 --current-limit 10 --samples 10
|
%(prog)s efficiency --voltage 36 --current-limit 10 --samples 10
|
||||||
%(prog)s sweep-vi --v-start 35 --v-stop 100 --v-step 5 --i-start 0.5 --i-stop 30 --i-step 1 --current-limit 35 -o map.csv
|
%(prog)s sweep-vi --v-start 35 --v-stop 100 --v-step 5 --l-start 0.5 --l-stop 30 --l-step 1 --current-limit 35 -o map.csv
|
||||||
|
%(prog)s sweep-vi --v-start 35 --v-stop 100 --v-step 5 --l-start 50 --l-stop 500 --l-step 50 --load-mode CP --current-limit 35 -o map_cp.csv
|
||||||
%(prog)s shade-profile --profile cloud_pass.csv --settle 2.0 -o shade_results.csv
|
%(prog)s shade-profile --profile cloud_pass.csv --settle 2.0 -o shade_results.csv
|
||||||
%(prog)s supply set --voltage 24 --current 10
|
%(prog)s supply set --voltage 24 --current 10
|
||||||
%(prog)s supply on
|
%(prog)s supply on
|
||||||
@@ -651,13 +655,14 @@ examples:
|
|||||||
p_eff.add_argument("--load-value", type=float)
|
p_eff.add_argument("--load-value", type=float)
|
||||||
|
|
||||||
# sweep-vi (2D)
|
# sweep-vi (2D)
|
||||||
p_svi = sub.add_parser("sweep-vi", help="2D voltage × load current sweep (efficiency map)")
|
p_svi = sub.add_parser("sweep-vi", help="2D voltage × load sweep (efficiency map)")
|
||||||
p_svi.add_argument("--v-start", type=float, required=True, help="Start voltage (V)")
|
p_svi.add_argument("--v-start", type=float, required=True, help="Start voltage (V)")
|
||||||
p_svi.add_argument("--v-stop", type=float, required=True, help="Stop voltage (V)")
|
p_svi.add_argument("--v-stop", type=float, required=True, help="Stop voltage (V)")
|
||||||
p_svi.add_argument("--v-step", type=float, required=True, help="Voltage step (V)")
|
p_svi.add_argument("--v-step", type=float, required=True, help="Voltage step (V)")
|
||||||
p_svi.add_argument("--i-start", type=float, required=True, help="Start load current (A)")
|
p_svi.add_argument("--l-start", type=float, required=True, help="Start load setpoint (A for CC, W for CP)")
|
||||||
p_svi.add_argument("--i-stop", type=float, required=True, help="Stop load current (A)")
|
p_svi.add_argument("--l-stop", type=float, required=True, help="Stop load setpoint")
|
||||||
p_svi.add_argument("--i-step", type=float, required=True, help="Current step (A)")
|
p_svi.add_argument("--l-step", type=float, required=True, help="Load step size")
|
||||||
|
p_svi.add_argument("--load-mode", choices=["CC", "CP"], default="CC", help="Load mode: CC (current) or CP (power)")
|
||||||
p_svi.add_argument("--current-limit", type=float, required=True, help="Supply current limit (A)")
|
p_svi.add_argument("--current-limit", type=float, required=True, help="Supply current limit (A)")
|
||||||
p_svi.add_argument("--settle", type=float, default=2.0, help="Settle time per step (s)")
|
p_svi.add_argument("--settle", type=float, default=2.0, help="Settle time per step (s)")
|
||||||
p_svi.add_argument("-o", "--output", help="CSV output file")
|
p_svi.add_argument("-o", "--output", help="CSV output file")
|
||||||
|
|||||||
@@ -502,7 +502,7 @@ class TestbenchGUI(tk.Tk):
|
|||||||
self._profile_after_id = None
|
self._profile_after_id = None
|
||||||
|
|
||||||
def _build_sweep_vi_controls(self, parent) -> None:
|
def _build_sweep_vi_controls(self, parent) -> None:
|
||||||
frame = ttk.LabelFrame(parent, text="2D Sweep (V × I)", padding=8)
|
frame = ttk.LabelFrame(parent, text="2D Sweep (V × Load)", padding=8)
|
||||||
frame.pack(fill=tk.X, padx=4, pady=4)
|
frame.pack(fill=tk.X, padx=4, pady=4)
|
||||||
|
|
||||||
# Voltage range
|
# Voltage range
|
||||||
@@ -521,21 +521,31 @@ class TestbenchGUI(tk.Tk):
|
|||||||
self._svi_v_step.insert(0, "5")
|
self._svi_v_step.insert(0, "5")
|
||||||
self._svi_v_step.pack(side=tk.LEFT, padx=2)
|
self._svi_v_step.pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
# Current range
|
# Load mode selector
|
||||||
row = ttk.Frame(frame)
|
row = ttk.Frame(frame)
|
||||||
row.pack(fill=tk.X, pady=1)
|
row.pack(fill=tk.X, pady=1)
|
||||||
ttk.Label(row, text="I start:", width=10).pack(side=tk.LEFT)
|
ttk.Label(row, text="Load mode:", width=10).pack(side=tk.LEFT)
|
||||||
self._svi_i_start = ttk.Entry(row, width=7)
|
self._svi_load_mode = ttk.Combobox(row, values=["CC", "CP"], width=4, state="readonly")
|
||||||
self._svi_i_start.insert(0, "0.5")
|
self._svi_load_mode.set("CC")
|
||||||
self._svi_i_start.pack(side=tk.LEFT, padx=2)
|
self._svi_load_mode.pack(side=tk.LEFT, padx=2)
|
||||||
|
self._svi_load_mode.bind("<<ComboboxSelected>>", self._on_svi_mode_change)
|
||||||
|
|
||||||
|
# Load setpoint range
|
||||||
|
row = ttk.Frame(frame)
|
||||||
|
row.pack(fill=tk.X, pady=1)
|
||||||
|
self._svi_l_label = ttk.Label(row, text="I start:", width=10)
|
||||||
|
self._svi_l_label.pack(side=tk.LEFT)
|
||||||
|
self._svi_l_start = ttk.Entry(row, width=7)
|
||||||
|
self._svi_l_start.insert(0, "0.5")
|
||||||
|
self._svi_l_start.pack(side=tk.LEFT, padx=2)
|
||||||
ttk.Label(row, text="stop:").pack(side=tk.LEFT)
|
ttk.Label(row, text="stop:").pack(side=tk.LEFT)
|
||||||
self._svi_i_stop = ttk.Entry(row, width=7)
|
self._svi_l_stop = ttk.Entry(row, width=7)
|
||||||
self._svi_i_stop.insert(0, "30")
|
self._svi_l_stop.insert(0, "30")
|
||||||
self._svi_i_stop.pack(side=tk.LEFT, padx=2)
|
self._svi_l_stop.pack(side=tk.LEFT, padx=2)
|
||||||
ttk.Label(row, text="step:").pack(side=tk.LEFT)
|
ttk.Label(row, text="step:").pack(side=tk.LEFT)
|
||||||
self._svi_i_step = ttk.Entry(row, width=5)
|
self._svi_l_step = ttk.Entry(row, width=5)
|
||||||
self._svi_i_step.insert(0, "1")
|
self._svi_l_step.insert(0, "1")
|
||||||
self._svi_i_step.pack(side=tk.LEFT, padx=2)
|
self._svi_l_step.pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
# Supply current limit + settle
|
# Supply current limit + settle
|
||||||
row = ttk.Frame(frame)
|
row = ttk.Frame(frame)
|
||||||
@@ -890,6 +900,13 @@ class TestbenchGUI(tk.Tk):
|
|||||||
return _fmt_eng(val)
|
return _fmt_eng(val)
|
||||||
return _fmt(val, decimals=4)
|
return _fmt(val, decimals=4)
|
||||||
|
|
||||||
|
def _on_svi_mode_change(self, _event=None) -> None:
|
||||||
|
mode = self._svi_load_mode.get()
|
||||||
|
if mode == "CC":
|
||||||
|
self._svi_l_label.config(text="I start:")
|
||||||
|
else:
|
||||||
|
self._svi_l_label.config(text="P start:")
|
||||||
|
|
||||||
def _set_range(self, channel: int, vi: str, combo: ttk.Combobox) -> None:
|
def _set_range(self, channel: int, vi: str, combo: ttk.Combobox) -> None:
|
||||||
"""Set voltage or current range for a HIOKI channel."""
|
"""Set voltage or current range for a HIOKI channel."""
|
||||||
val = combo.get()
|
val = combo.get()
|
||||||
@@ -1084,11 +1101,12 @@ class TestbenchGUI(tk.Tk):
|
|||||||
"v_start": float(self._svi_v_start.get()),
|
"v_start": float(self._svi_v_start.get()),
|
||||||
"v_stop": float(self._svi_v_stop.get()),
|
"v_stop": float(self._svi_v_stop.get()),
|
||||||
"v_step": float(self._svi_v_step.get()),
|
"v_step": float(self._svi_v_step.get()),
|
||||||
"i_start": float(self._svi_i_start.get()),
|
"l_start": float(self._svi_l_start.get()),
|
||||||
"i_stop": float(self._svi_i_stop.get()),
|
"l_stop": float(self._svi_l_stop.get()),
|
||||||
"i_step": float(self._svi_i_step.get()),
|
"l_step": float(self._svi_l_step.get()),
|
||||||
"current_limit": float(self._svi_ilimit.get()),
|
"current_limit": float(self._svi_ilimit.get()),
|
||||||
"settle_time": float(self._svi_settle.get()),
|
"settle_time": float(self._svi_settle.get()),
|
||||||
|
"load_mode": self._svi_load_mode.get(),
|
||||||
}
|
}
|
||||||
except ValueError:
|
except ValueError:
|
||||||
messagebox.showerror("Invalid Input", "Check sweep parameters.")
|
messagebox.showerror("Invalid Input", "Check sweep parameters.")
|
||||||
@@ -1104,9 +1122,11 @@ class TestbenchGUI(tk.Tk):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
mode = params["load_mode"]
|
||||||
|
unit = "A" if mode == "CC" else "W"
|
||||||
self._console(
|
self._console(
|
||||||
f"2D sweep: V={params['v_start']}-{params['v_stop']}V "
|
f"2D sweep: V={params['v_start']}-{params['v_stop']}V "
|
||||||
f"I={params['i_start']}-{params['i_stop']}A",
|
f"{mode}={params['l_start']}-{params['l_stop']}{unit}",
|
||||||
"success",
|
"success",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1149,28 +1169,34 @@ class TestbenchGUI(tk.Tk):
|
|||||||
|
|
||||||
def _sweep_vi_loop(self, p: dict, stop: threading.Event) -> list:
|
def _sweep_vi_loop(self, p: dict, stop: threading.Event) -> list:
|
||||||
"""Run the 2D sweep on a background thread. Returns list of SweepPoint."""
|
"""Run the 2D sweep on a background thread. Returns list of SweepPoint."""
|
||||||
from testbench.bench import SweepPoint, IDLE_VOLTAGE
|
from testbench.bench import IDLE_VOLTAGE
|
||||||
|
|
||||||
bench = self.bench
|
bench = self.bench
|
||||||
v_start, v_stop, v_step = p["v_start"], p["v_stop"], p["v_step"]
|
v_start, v_stop, v_step = p["v_start"], p["v_stop"], p["v_step"]
|
||||||
i_start, i_stop, i_step = p["i_start"], p["i_stop"], p["i_step"]
|
l_start, l_stop, l_step = p["l_start"], p["l_stop"], p["l_step"]
|
||||||
current_limit = p["current_limit"]
|
current_limit = p["current_limit"]
|
||||||
settle = p["settle_time"]
|
settle = p["settle_time"]
|
||||||
|
load_mode = p.get("load_mode", "CC")
|
||||||
|
unit = "A" if load_mode == "CC" else "W"
|
||||||
|
|
||||||
# Auto-correct directions
|
# Auto-correct directions
|
||||||
if v_start < v_stop and v_step < 0:
|
if v_start < v_stop and v_step < 0:
|
||||||
v_step = -v_step
|
v_step = -v_step
|
||||||
elif v_start > v_stop and v_step > 0:
|
elif v_start > v_stop and v_step > 0:
|
||||||
v_step = -v_step
|
v_step = -v_step
|
||||||
if i_start < i_stop and i_step < 0:
|
if l_start < l_stop and l_step < 0:
|
||||||
i_step = -i_step
|
l_step = -l_step
|
||||||
elif i_start > i_stop and i_step > 0:
|
elif l_start > l_stop and l_step > 0:
|
||||||
i_step = -i_step
|
l_step = -l_step
|
||||||
|
|
||||||
|
# Sanity check
|
||||||
|
max_v = max(abs(v_start), abs(v_stop))
|
||||||
|
bench.check_supply_capability(max_v, current_limit)
|
||||||
|
|
||||||
bench.supply.set_current(current_limit)
|
bench.supply.set_current(current_limit)
|
||||||
bench.supply.output_on()
|
bench.supply.output_on()
|
||||||
bench.load.set_mode("CC")
|
bench.load.set_mode(load_mode)
|
||||||
bench.load.set_cc_current(i_start)
|
bench._apply_load_value(load_mode, l_start)
|
||||||
bench.load.load_on()
|
bench.load.load_on()
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
@@ -1185,20 +1211,20 @@ class TestbenchGUI(tk.Tk):
|
|||||||
break
|
break
|
||||||
|
|
||||||
bench.supply.set_voltage(v)
|
bench.supply.set_voltage(v)
|
||||||
i = i_start
|
ll = l_start
|
||||||
|
|
||||||
while not stop.is_set():
|
while not stop.is_set():
|
||||||
if i_step > 0 and i > i_stop + i_step / 2:
|
if l_step > 0 and ll > l_stop + l_step / 2:
|
||||||
break
|
break
|
||||||
if i_step < 0 and i < i_stop + i_step / 2:
|
if l_step < 0 and ll < l_stop + l_step / 2:
|
||||||
break
|
break
|
||||||
|
|
||||||
bench.load.set_cc_current(i)
|
bench._apply_load_value(load_mode, ll)
|
||||||
time.sleep(settle)
|
time.sleep(settle)
|
||||||
if stop.is_set():
|
if stop.is_set():
|
||||||
break
|
break
|
||||||
|
|
||||||
point = bench._record_point(v, current_limit, load_setpoint=i)
|
point = bench._record_point(v, current_limit, load_setpoint=ll)
|
||||||
results.append(point)
|
results.append(point)
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
@@ -1233,13 +1259,13 @@ class TestbenchGUI(tk.Tk):
|
|||||||
|
|
||||||
self.after(
|
self.after(
|
||||||
0,
|
0,
|
||||||
lambda v=v, i=i, pt=point, n=n: self._svi_status.config(
|
lambda v=v, ll=ll, pt=point, n=n: self._svi_status.config(
|
||||||
text=f"[{n}] V={v:.1f}V I={i:.1f}A "
|
text=f"[{n}] V={v:.1f}V {load_mode}={ll:.1f}{unit} "
|
||||||
f"EFF={pt.efficiency:.1f}%"
|
f"EFF={pt.efficiency:.1f}%"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
i += i_step
|
ll += l_step
|
||||||
v += v_step
|
v += v_step
|
||||||
finally:
|
finally:
|
||||||
bench.load.load_off()
|
bench.load.load_off()
|
||||||
|
|||||||
Reference in New Issue
Block a user