diff --git a/docs/BRINGUP.md b/docs/BRINGUP.md index 58bb55b..af0d3cf 100644 --- a/docs/BRINGUP.md +++ b/docs/BRINGUP.md @@ -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 diff --git a/docs/CAMPAIGN-LOG.md b/docs/CAMPAIGN-LOG.md index e27d2f2..bded739 100644 --- a/docs/CAMPAIGN-LOG.md +++ b/docs/CAMPAIGN-LOG.md @@ -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 diff --git a/scripts/bench/live_fire_drills.py b/scripts/bench/live_fire_drills.py index 9de4ef4..0bb58a9 100644 --- a/scripts/bench/live_fire_drills.py +++ b/scripts/bench/live_fire_drills.py @@ -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: