Retract the pulse-length conclusion; show the minimum in the drill table

The fifth ladder, the first with a minimum pulse, moved the floor down a
full rung: only 5 percent failed to mark, and 5 percent now strikes. The
trace carries eight current segments where the run before it had seven,
with fire beginning at 6.2 s exactly at rung 1 and the usual flat
saturated final segment anchoring the count from the other end.

That retracts what the previous entry concluded. Pulse length is not
irrelevant: 10 percent moved from no mark at F100, with three times the
dose per millimeter, to a mark at F300 at the same density, the only
change being its pulses growing from 36-71 us stubs to 106 us. The
matched-pairs argument was sound but drawn entirely from comparisons at
or above 20 percent density, where every pulse length in play was already
long enough - it generalized from the one regime where pulse length does
not bite. Above ~100 us dose governs; below it pulse length does; below
~36 us the supply does not strike. The factory's 100 us quantum sits on
that boundary.

dladder now reads laser_pulse_min_ticks and prints what is actually
emitted. Without that its table reports the pulse density alone would
give, which is wrong wherever the minimum applies - at min 6 the bottom
four rungs all emit 213 us and vary their rate instead, and the operator
reads that table to interpret the material.
This commit is contained in:
ScottW514
2026-08-17 21:47:40 -04:00
parent a3e83c4fd5
commit ff454ed537
3 changed files with 76 additions and 20 deletions
+20 -14
View File
@@ -1090,22 +1090,28 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
under both models - the motion grid is identical and every density FIRE
tick is one the analog run also fired, so the model only ever masks.
The base period is settled: four bench ladders (F300 at periods 20, 40
and 10, then F100 at 20) put the same six rungs on the material every
time, and the matched pairs across periods separate the variables - at
**Both dose and pulse length matter, in different regimes.** Four
ladders run with no minimum pulse (F300 at periods 20, 40 and 10, then
F100 at 20) put the same six rungs on the material every time - 20 %
and up - and their matched pairs looked like a clean answer: at
identical pulse length, halving density killed the mark; at identical
density, varying pulse length 3x changed nothing. Feed did not move it
either: 10 % at F100 carries 44 % more energy per millimeter than 20 %
at F300, which marks, and still left nothing. So the low-end marking
limit is average power, not dose per length and not pulse length, and
the period can be chosen on other grounds. It stays at 20.
density, varying pulse length 3x changed nothing; and feed did not move
it either, 10 % at F100 carrying 44 % more energy per millimeter than a
marking 20 % at F300 and still leaving nothing.
What the ladders did expose is a floor of our own making, since fixed:
at 5 % the model emitted 36 us stubs and the `hv_current` trace shows
**no discharge at all** for that rung, while 10 % drew current for its
full 15 s and simply marked nothing. The factory never emits below one
100 us tick, and reaches low density by skipping windows instead -
which is now what `laser_pulse_min_ticks` does.
That reading was too broad. Every one of those comparisons sat at or
above 20 % density, where the pulses in play were already long enough.
A fifth ladder with a 3-tick minimum moved 10 % from nothing to a mark
at the **same density and less dose**, purely by lengthening its pulses
from 36-71 us to 106 us. So above ~100 us the outcome follows dose;
below it pulse length dominates - too short and the energy does
nothing, shorter still (36 us, one tick) and the supply does not strike
at all, which the `hv_current` trace showed as a rung with no discharge
for its full 15 s. That is what the factory's 100 us quantum protects,
and what `laser_pulse_min_ticks` now protects.
With a minimum in place the low end is decoupled from the base period,
so the period is free to be chosen on other grounds. It stays at 20.
Owed: the user-facing scale. The factory maps its whole 1-100 power
scale onto density 18.9-79.5 % (fit from the three captures; Full Power
+41 -4
View File
@@ -2949,10 +2949,47 @@ only what it must. Rule 15 in the stream harness holds both halves: no burst
below the minimum (excepting one clipped by fire going off mid-burst), and
the rendered density still exact.
Owed next: the user-facing scale. Under this model `$35` and `$36` are a
density floor and ceiling, so mapping S onto the usable band is a settings
choice rather than new code — but the floor's value wants a finer ladder than
the 10 %→20 % step these four runs give.
### The fifth ladder, and a conclusion retracted
Same ladder, period 20, F300, with the 3-tick minimum in place. **The floor
moved down a full rung: only 5 % failed to mark, and 5 % now strikes.**
The trace carries eight current segments where the F100 run had seven.
Segmenting by time rather than by zeros — at 5 % density the sampled current
aliases, so isolated zeros appear mid-rung and cannot serve as boundaries —
the rung period is 5.25 s and lines up end to end: fire begins at 6.2 s,
exactly at rung 1's start, boundaries fall at 11.4, 16.6, 21.9, 27.3, 32.6,
38.1 and 43.3 s, and the span is 42.0 s against 41.4 s for eight rungs. The
final segment reads 937–981 flat and saturated, which can only be full
density. Rung 1 shows peaks of 291, 286 and 204 where the F100 run held a
flat zero for the rung's entire fifteen seconds.
**This retracts the conclusion in the entry above.** Pulse length is not
irrelevant: 10 % moved from no mark at F100 — with three times the dose per
millimeter — to a mark at F300, at the same density, the only change being
its pulses growing from 36–71 µs stubs to 106 µs. The matched-pairs argument
was sound but drawn entirely from comparisons at or above 20 % density, where
every pulse length in play was already sufficient; it generalized from the one
regime where pulse length does not bite. Above ~100 µs dose governs, below it
pulse length does, and below ~36 µs the supply does not strike at all. The
factory's 100 µs quantum sits exactly on that boundary.
Not read into: the low-rung current means (76 and 82 raw for 5 % and 10 %).
At those duties a 3.3 Hz point sample of a pulsed current carries
presence-versus-absence and nothing more. Noted as a confound, though it cuts
against the result rather than for it — this ladder started at MPos 0,0 after
the controller restart, so it may be on different material than the stacked
Y=0/24/48/72 runs.
Owed next: whether a longer minimum reaches further down (`min_ticks = 6`,
213 µs, is set on the bench for the next ladder), and then the user-facing
scale. Under this model `$35` and `$36` are a density floor and ceiling, so
mapping S onto the usable band is a settings choice rather than new code, but
the floor's value wants a finer ladder than the 10 %→20 % step. The trick has
a ceiling of its own: a longer minimum at fixed dose means longer gaps, and
once gap × feed approaches the beam spot a line dots. At 5 mm/s a 4.5 ms gap
is 22 µm against a ~200 µm spot; at 2000 mm/min it is 150 µm, where dotting
would start to show.
## Superseded status notes
+15 -2
View File
@@ -521,6 +521,8 @@ DLADDER_PITCH = 3.0 # mm between rungs
STREAM_RATE_HZ = 28160 # machine tick (GFSINK_RATE default)
PWM_PERIOD = 127 # 7-bit power byte against PWMSAR
CONF = os.environ.get('GFHOME_CONF') or '/data/forgefirm.conf'
PULSE_MIN_KEY = 'laser_pulse_min_ticks'
PULSE_MIN_DEFAULT = 3 # glowforge_laser.c PULSE_MIN_TICKS_DEFAULT
def conf_get(key):
@@ -621,9 +623,16 @@ def drill_dladder(g):
# What each rung actually emits. The on-count is dithered between
# adjacent integers, so the burst below is the mean.
min_ticks = int(conf_get(PULSE_MIN_KEY) or PULSE_MIN_DEFAULT)
if min_ticks < 1:
min_ticks = 1
print('period %d ticks = %.0f us at %d Hz -> %.0f Hz pulse rate'
% (period, period * 1e6 / STREAM_RATE_HZ, STREAM_RATE_HZ,
STREAM_RATE_HZ / float(period)))
print('minimum pulse %d ticks = %.0f us (%s): below it the model skips'
% (min_ticks, min_ticks * 1e6 / STREAM_RATE_HZ,
PULSE_MIN_KEY if conf_get(PULSE_MIN_KEY) else 'driver default'))
print('periods and carries the debt, so the pulse never falls under it.')
print('rungs (drawn in order, alternating direction, +Y between):')
levels = []
for pct in DLADDER_PCT:
@@ -638,8 +647,12 @@ def drill_dladder(g):
# skipped instead - that is the short end this drill is for.
lo = int(on)
tick_us = 1e6 / STREAM_RATE_HZ
if lo == 0:
burst = '1 tick (%.0f us) on ~%.0f%% of periods' % (tick_us, on * 100)
if on < min_ticks:
# Below the minimum the model skips periods and carries the
# debt, so the pulse holds at the minimum and the rate drops.
burst = '%d ticks (%.0f us) every %.1f periods (%.0f Hz)' % (
min_ticks, min_ticks * tick_us, min_ticks / on,
STREAM_RATE_HZ / float(period) * on / min_ticks)
elif on == lo:
burst = '%d ticks (%.0f us)' % (lo, lo * tick_us)
else: