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
+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: