mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 16:51:12 -07:00
Commission the laser duty floor; record how the factory sets power
The pthresh ladder on scrap puts the tube's two thresholds far apart: the discharge strikes between 2 and 3 percent duty, but nothing lases usefully below 16 percent (PWMSAR 20), and the rungs between show only a spot at each line start. $35 ships at 16 (grblhal-glowforge 9466f76, pinned here). The drill said current lift-off and first mark share a rung; this run falsifies that, so its docstring and read-the-material text now name both thresholds and warn that a start-of-line spot is below the threshold, not at it. A start-of-line spot is also what a full-power leak at a kernel run start would look like, so laser_stream_test gains a ladder session (rule 10): every FIRE tick must ride a commanded duty, and the fire ticks must divide evenly across rungs. Both hold exactly - six commanded duties, no others, and 28296 fire ticks on every rung - so the spots are the tube, not the stream. The harness now derives its expectations from the floor, which moves the M4 session's S500 plateau from 63 to 73. laser.power-floor is a new auto acceptance test, the suite's only non-firing one: a machine must actually carry the commissioned floor, since stored settings beat freshly baked defaults. Three cloud cuts of one square at Precision Power 1, 100 and Full Power show what the analog path is competing with: the power byte is pinned at 127 in all three, dose is FIRE-bit density on a fixed 7-tick period with the on-count dithered between adjacent integers, and the power setting never reaches the machine at all. Facts bank and item 17 carry the numbers; CAMPAIGN-LOG carries both sessions.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""laser.* - LIVE laser tests, ported from `scripts/bench/live_fire_drills.py`.
|
||||
|
||||
Every test here can emit. The page starts one only with the operator's
|
||||
Every test here can emit, except `laser.power-floor`, which only reads
|
||||
settings. The page starts a firing one only with the operator's
|
||||
eye-protection / fire-watch / exhaust acknowledgment; the test then
|
||||
prompts for the scrap and the button, streams a small job through the
|
||||
controller, and the machine fires only after the operator presses the
|
||||
@@ -24,6 +25,12 @@ _LASER_COVERS = [("grblhal-glowforge", "src/**"), ("kernel-module-glowforge", "*
|
||||
("forgectrl", "src/super.c"), ("forgectrl", "src/cool.c"),
|
||||
("forgectrl", "src/status.c"), ("forgectrl", "src/main.c")]
|
||||
|
||||
# boards/glowforge.h: DEFAULT_SPINDLE_PWM_MIN_VALUE against the hardware's
|
||||
# 127-count PWM period. The tube's measured lasing threshold is PWMSAR 20.
|
||||
POWER_FLOOR_PCT = 16.0
|
||||
PWM_PERIOD = 127
|
||||
PWMSAR_FLOOR_MIN = 20
|
||||
|
||||
ARM_CUE = ("LIVE FIRE. Eye protection on, exhaust running, fire watch and extinguisher in reach, "
|
||||
"scrap under the head with room to move (%s), lid closed. When the job starts the "
|
||||
"white button lights and the stream blocks until you press the physical arm button; "
|
||||
@@ -218,6 +225,48 @@ def wait_disarm(ctx, timeout):
|
||||
return None
|
||||
|
||||
|
||||
@test("laser.power-floor", title="The shipped duty floor holds commanded power above the lasing threshold",
|
||||
subsystem="laser", kind="auto", est_min=1,
|
||||
covers=[("grblhal-glowforge", "src/**")],
|
||||
description="The tube lases only above ~16 % duty, so $35 must floor every nonzero S there: "
|
||||
"unfloored, M4's velocity-scaled power falls into the dead band at every corner "
|
||||
"and reversal and marks nothing. Reads $$ and checks the floor is the "
|
||||
"commissioned percent, that $31 is 0 (the floor, not $31, sets the bottom of "
|
||||
"the range), and that the floor lands at or above PWMSAR 20.")
|
||||
def power_floor(ctx):
|
||||
ev = ctx.evidence
|
||||
with ctx.grbl() as g:
|
||||
lines = g.command("$$", timeout=5)
|
||||
vals = {}
|
||||
for ln in lines:
|
||||
key, _, val = ln.partition("=")
|
||||
if key.startswith("$"):
|
||||
try:
|
||||
vals[key] = float(val)
|
||||
except ValueError:
|
||||
pass
|
||||
ctx.check(vals, "no $-settings in the response to $$")
|
||||
|
||||
floor_pct = vals.get("$35")
|
||||
rpm_min = vals.get("$31")
|
||||
ev["settings"] = {k: vals.get(k) for k in ("$30", "$31", "$32", "$35", "$36")}
|
||||
ctx.check(floor_pct is not None, "$35 missing from the settings report")
|
||||
counts = int(PWM_PERIOD * floor_pct / 100.0)
|
||||
ev["pwmsar_floor"] = counts
|
||||
ctx.log("$35=%.1f%% -> PWMSAR %d of %d ($31=%s, $32=%s, $36=%s)", floor_pct, counts,
|
||||
PWM_PERIOD, rpm_min, vals.get("$32"), vals.get("$36"))
|
||||
|
||||
ctx.check(abs(floor_pct - POWER_FLOOR_PCT) < 0.05,
|
||||
"$35 is %.1f %%, expected the commissioned %.1f %% - a machine carrying stored "
|
||||
"settings from before the floor needs `$RST=$` once", floor_pct, POWER_FLOOR_PCT)
|
||||
ctx.check(counts >= PWMSAR_FLOOR_MIN,
|
||||
"the floor lands at PWMSAR %d, below the %d the tube needs to lase",
|
||||
counts, PWMSAR_FLOOR_MIN)
|
||||
ctx.check(rpm_min == 0, "$31 is %s, not 0: the bottom of the S range is no longer the floor",
|
||||
rpm_min)
|
||||
ctx.log("PASS: nonzero S never commands less than PWMSAR %d", counts)
|
||||
|
||||
|
||||
@test("laser.emission-witness", title="Live emission witness (S400 vector mark) and job-based disarm",
|
||||
subsystem="laser", kind="live", always=True, est_min=5,
|
||||
covers=_LASER_COVERS,
|
||||
|
||||
Reference in New Issue
Block a user