mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 08:41:13 -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:
+15
-1
@@ -737,6 +737,11 @@ not a release.
|
||||
`min_ticks` 3 at that density - and 6 is outside anything the factory
|
||||
does. Below ~5 % no pulse shape reaches the tube: the interval grows as
|
||||
1/density, so 1 % implies an 11 ms gap, five times what already failed.
|
||||
**The scale closes that gap instead:** `$35` = 10 maps S onto 9.4-100 %
|
||||
density, putting a commanded 1 % at 10.2 %, and a ladder weighted to the
|
||||
bottom (1, 2, 5, 10, 20, 40, 70, 100 % of S) then marked on **all eight
|
||||
rungs**, with eight current segments and means rising 136 -> 968. So a
|
||||
user's 1 % is a real, visible mark rather than silence.
|
||||
- **Cooling operating point**: 40 % heater duty, 50 s window, flow-rise
|
||||
threshold 14.4 °C, re-checks every 150 s. Below ~40 % duty the stagnant loop
|
||||
sheds the heater's output by convection well enough to mimic flow (at 30 %,
|
||||
@@ -1133,7 +1138,16 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
|
||||
onto that band, which is what the factory does and what `$35` is under
|
||||
this model.
|
||||
|
||||
Owed: the user-facing scale. The factory maps its whole 1-100 power
|
||||
Owed: the shipping defaults. The model, the minimum pulse and the
|
||||
scale are all proven on hardware, but `laser_power_model` still
|
||||
defaults to `analog`, so nothing of this reaches a machine until the
|
||||
key is set. Flipping the default means `$35` must move with it - 16 is
|
||||
the analog duty floor, 10 is the density floor, and the wrong pairing
|
||||
is a dead band either way - and it wants one real job at a production
|
||||
feed first: every ladder here ran at F300 or F100, where dose per
|
||||
millimeter is generous, and no raster has run at all.
|
||||
|
||||
For reference, the factory maps its whole 1-100 power
|
||||
scale onto density 18.9-79.5 % (fit from the three captures; Full Power
|
||||
is off that line at ~99.7 %), so its "1 %" is the bottom of the band
|
||||
that does useful work rather than 1 % of the physical range. Under the
|
||||
|
||||
+39
-5
@@ -3009,11 +3009,45 @@ in the direction that fails. The bench is back at 3.
|
||||
**Measured band for this tube: strikes from ~5 %, marks from ~10 % at F300.**
|
||||
|
||||
Which closes the pulse-structure route to a usable 1 %. The interval grows as
|
||||
1/density, so 1 % implies an 11 ms gap, five times what already failed —
|
||||
no pulse shape reaches down there. The low end is a scaling problem: map the
|
||||
user's 1–100 onto the band that works, via `$35` as the density floor, which
|
||||
is what the factory does and why its cut scale starts at 18.9 % while its
|
||||
engraves reach 6.5 %. Both sit inside the band measured here independently.
|
||||
1/density, so 1 % implies an 11 ms gap, five times what already failed — no
|
||||
pulse shape reaches down there. The low end is a scaling problem.
|
||||
|
||||
### The seventh ladder: the scale, and the goal met
|
||||
|
||||
`$35 = 10` — a density floor under this model, not a duty floor — with the
|
||||
ladder reweighted to the bottom of the user scale (1, 2, 5, 10, 20, 40, 70,
|
||||
100 % of S), since with a floor in place what matters is whether the lowest
|
||||
levels a user can dial in still mark.
|
||||
|
||||
The mapping puts S onto 9.4–100 % density, so a commanded 1 % lands at 10.2 %,
|
||||
just above the ~10 % marking floor the earlier ladders measured. **All eight
|
||||
rungs marked.** The trace carries eight current segments — boundaries at 14.3,
|
||||
19.5, ~24.85, 30.2, 35.4, 40.9 and 46.1 s, fire spanning 9.1 → 51.1 s = 42.0 s
|
||||
against exactly 8 × 5.25 — with means climbing monotonically:
|
||||
|
||||
| rung | commanded | density | mean current |
|
||||
|---|---|---|---|
|
||||
| 1 | **1 %** | 10.2 % | 136 |
|
||||
| 2 | 2 % | 11.0 % | 190 |
|
||||
| 3 | 5 % | 13.4 % | 214 |
|
||||
| 4 | 10 % | 18.1 % | 262 |
|
||||
| 5 | 20 % | 27.6 % | 331 |
|
||||
| 6 | 40 % | 45.7 % | 340 |
|
||||
| 7 | 70 % | 72.4 % | 444 |
|
||||
| 8 | 100 % | 100 % | 968 flat |
|
||||
|
||||
So the original goal is met: a user's 1 % is a real, visible mark rather than
|
||||
silence, and 100 % is full power. It took the density model to make every
|
||||
level real pulses, the minimum pulse to keep them strikeable, and the floor to
|
||||
put the user's range on the band that works — the same three pieces the
|
||||
factory uses, arrived at from this bench's own measurements.
|
||||
|
||||
Owed: the shipping defaults. `laser_power_model` still defaults to `analog`,
|
||||
so none of this reaches a machine until the key is set, and `$35` must move
|
||||
with the model (16 is the analog duty floor, 10 the density floor; the wrong
|
||||
pairing is a dead band either way). Both want one real job at a production
|
||||
feed first — every ladder here ran at F300 or F100, where dose per millimeter
|
||||
is generous, and no raster has run at all.
|
||||
|
||||
## Superseded status notes
|
||||
|
||||
|
||||
@@ -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