mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 16:51:12 -07:00
The density floor closes the low end: a commanded 1 percent now marks
$35 = 10 under the density model is a density floor, not a duty floor. It maps S onto 9.4-100 percent density, so a commanded 1 percent lands at 10.2 percent, just above the marking floor the earlier ladders measured. A ladder reweighted to the bottom of the user scale - 1, 2, 5, 10, 20, 40, 70, 100 percent of S - marked on all eight rungs, with eight current segments over a 42.0 s window against exactly 8 x 5.25, and segment means climbing 136 to 968. That meets the goal the ladders started from: a user's 1 percent is a real visible mark rather than silence, and 100 percent is full power. It took all three pieces - density so every level is real pulses, the minimum pulse so they stay strikeable, the floor so the user's range sits on the band that works. dladder no longer tells the operator to re-run at other base periods to choose one. The period cancels out of the low end, and what a failing rung now indicates is a floor set too low.
This commit is contained in:
@@ -59,8 +59,10 @@ Drills (pass a name):
|
||||
~1.43 kHz, and 40 and 10 bracket it. Two questions the
|
||||
material answers: does mark depth track density linearly,
|
||||
and how short a burst still marks. Requires
|
||||
laser_power_model = density and $35 = 0 (a floor lifts every
|
||||
rung); sets laser_pulse_ticks itself when run on the board.
|
||||
laser_power_model = density; reads $30/$31/$35/$36 and
|
||||
reports the mapping, so a run with a density floor set shows
|
||||
what a shipped machine would actually emit. Sets
|
||||
laser_pulse_ticks itself when run on the board.
|
||||
dladder [period] [F] e.g. dladder 20 300
|
||||
expstop Armed kill on the EXPECTED-stop path: start a mark job,
|
||||
then mid-burn POST /controller/stop (the supervisor stops
|
||||
@@ -513,9 +515,10 @@ def drill_pthresh(g):
|
||||
|
||||
# --- density ladder -------------------------------------------------------
|
||||
|
||||
# Dose levels in percent of full. Even spacing, because the question is
|
||||
# whether mark depth tracks density linearly rather than where it stops.
|
||||
DLADDER_PCT = (5, 10, 20, 30, 40, 60, 80, 100)
|
||||
# Dose levels in percent of full, weighted to the bottom: with a density
|
||||
# floor set, what matters is whether the lowest levels a user can dial in
|
||||
# still mark, not how the top half behaves.
|
||||
DLADDER_PCT = (1, 2, 5, 10, 20, 40, 70, 100)
|
||||
DLADDER_LEN = 25.0 # mm of burn per rung
|
||||
DLADDER_PITCH = 3.0 # mm between rungs
|
||||
STREAM_RATE_HZ = 28160 # machine tick (GFSINK_RATE default)
|
||||
@@ -603,13 +606,27 @@ def drill_dladder(g):
|
||||
print('Set it in %s and re-run. The model is read at each arm, so' % CONF)
|
||||
print('this key needs no controller restart.')
|
||||
return 2
|
||||
# The core maps S onto the level this model renders as density, and
|
||||
# $35/$36 are its floor and ceiling. Read them rather than assuming:
|
||||
# with a floor set, the ladder is testing the shipping mapping, and
|
||||
# every rung sits higher than its commanded percent.
|
||||
floor = grbl_setting(g, '$35')
|
||||
if floor is None or floor > 0.0:
|
||||
print('PRECONDITION FAILED: $35 is %s, need 0.' % floor)
|
||||
print('Send $35=0 and restart the controller - the S -> duty mapping')
|
||||
print('is precomputed once, when the spindle is enabled, so a runtime')
|
||||
print('write does not reach it.')
|
||||
ceil = grbl_setting(g, '$36')
|
||||
rpm_max = grbl_setting(g, '$30')
|
||||
rpm_min = grbl_setting(g, '$31')
|
||||
if None in (floor, ceil, rpm_max, rpm_min) or rpm_max <= rpm_min:
|
||||
print('PRECONDITION FAILED: cannot read $30/$31/$35/$36 (%s/%s/%s/%s)'
|
||||
% (rpm_max, rpm_min, floor, ceil))
|
||||
return 2
|
||||
min_value = int(PWM_PERIOD * floor / 100.0)
|
||||
max_value = int(PWM_PERIOD * ceil / 100.0)
|
||||
gradient = (max_value - min_value) / (rpm_max - rpm_min)
|
||||
print('mapping: $30=%g $31=%g $35=%g $36=%g -> density %.1f%%..%.1f%%'
|
||||
% (rpm_max, rpm_min, floor, ceil,
|
||||
100.0 * min_value / PWM_PERIOD, 100.0 * max_value / PWM_PERIOD))
|
||||
if floor > 0.0:
|
||||
print('a floor is set, so the rungs below it all land on it - that is')
|
||||
print('the shipping mapping, not the raw range.')
|
||||
if conf_set('laser_pulse_ticks', str(period)):
|
||||
print('laser_pulse_ticks = %d (written to %s)' % (period, CONF))
|
||||
else:
|
||||
@@ -636,8 +653,9 @@ def drill_dladder(g):
|
||||
print('rungs (drawn in order, alternating direction, +Y between):')
|
||||
levels = []
|
||||
for pct in DLADDER_PCT:
|
||||
sval = int(round(1000 * pct / 100.0))
|
||||
level = int(sval * PWM_PERIOD / 1000) # the core's mapping at $35 = 0
|
||||
sval = int(round(rpm_max * pct / 100.0))
|
||||
level = int((sval - rpm_min) * gradient) + min_value
|
||||
level = min(level, max_value)
|
||||
dens = level / float(PWM_PERIOD)
|
||||
on = dens * period
|
||||
levels.append((pct, sval, dens))
|
||||
@@ -710,9 +728,10 @@ def drill_dladder(g):
|
||||
print(' 2. THE SHORT END - the lowest rung that still marks cleanly. Its')
|
||||
print(' burst length in us is the number to keep; a rung that stops')
|
||||
print(' marking sets the floor this base period can reach.')
|
||||
print('Then run the same ladder at 40 and at 10 on the same material and')
|
||||
print('compare the low rungs across the three: that is what picks the')
|
||||
print('base period. Nothing else in the stack can answer it.')
|
||||
print('The base period does not decide the low end: below the minimum')
|
||||
print('the pulse interval is min_ticks x tick / density, which the')
|
||||
print('period cancels out of. What sets the bottom is the density')
|
||||
print('floor ($35), so a rung that fails is telling you to raise it.')
|
||||
return samples
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user