Fix/ddg1 shutter delay #315

Merged
menzel merged 2 commits from fix/ddg1-shutter-delay into main 2026-09-09 12:30:16 +02:00
2 Commits
Author SHA1 Message Date
menzel 3d24ea5651 fix(cont_grid): wait for the move to reach the motor before triggering
CI for csaxs_bec / test (push) Successful in 2m3s
CI for csaxs_bec / test (pull_request) Successful in 1m57s
at_each_point issues the line move and then sleeps before firing the
burst. The sleep was acc_time alone, which covers the acceleration ramp
and nothing else -- while the move still has to cross the scan server ->
device server -> EPICS -> controller path first. acc_time shrinks with
the scan velocity; that path does not. On a slow scan the burst
therefore began well before the stage moved, and the first points of
every line piled up at the line start.

Measured from the position readback of five commissioning scans, as the
distance the stage was behind the trigger grid once the lag stopped
growing:

    scan   exp_time   v_cmd         lag       latency
     254     20 ms    0.5   mm/s   29.7 pts   0.594 s
     450     50 ms    0.1   mm/s   11.9 pts   0.595 s
     324     15 ms    0.667 mm/s   41.5 pts   0.623 s
     473    100 ms    0.065 mm/s    6.3 pts   0.630 s
     411     50 ms    0.1   mm/s   12.7 pts   0.635 s

Constant to +-3.5% across a 6.7x range of exposure and a 10x range of
velocity, on two axes. Scan 473 is sampled 74 times per line and shows
the shape plainly: the lag appears in the first interval and then holds
at 6.3-6.4 points for the rest of the line while the velocity sits at
exactly the commanded 0.065 mm/s. A start-up offset, not a velocity
error, and the stage itself is blameless.

Deliberately not solved by polling for motion. Observing the readback
costs a round trip of this same ~0.6 s, so it would trade a systematic
offset for a jitter of similar size -- and a constant offset displaces
every line equally, where a varying one shears the image line by line
and cannot be undone afterwards. The reproducibility is the asset here,
not the enemy.

The value lives on ddg1 next to the shutter delay, with a setter in
USER_ACCESS, because that is where cont_grid already fetches its trigger
timing. It is not a property of the delay generator and the docstring
says so. Setting it to 0 reproduces exactly the previous timing without
a redeploy, which is the intended way back if this makes things worse.

The real fix is to trigger the DDG from the motor
(scan_type: hardware_triggered), taking the round trip out of the timing
chain rather than compensating for it. This is the stopgap until then,
and it rests on the latency staying constant -- which is worth
re-checking with the same measurement whenever the deployment changes.
2026-08-25 23:28:55 +02:00
menzel 78810999d0 fix(ddg1): give the shutter the time it actually needs to open
CI for csaxs_bec / test (push) Successful in 1m55s
The first point of every line is under-exposed. Four commissioning scans
put a number on it, against the 2e-3 head start that was in place:

    scan   exp_time   first/near   lost
     324     15 ms       0.655     5.18 ms
     254     20 ms       0.732     5.36 ms
     411     50 ms       0.881     5.93 ms
     450     50 ms       0.879     6.04 ms

A fixed time, not a fixed fraction: it varies by 17% across a 3.3x range
of exposure while the fraction varies by 2.9x. The same deficit appears
on the integrated scattering, a different detector behind a different
gate, so the cause is upstream of both readout chains rather than in
either of them. 2e-3 allowed and ~5.6e-3 still lost means the shutter
needs about 7.6 ms, rounded up to 8 ms: the spread across the four scans
is 0.9 ms, so the third digit is not meaningful, and overshooting costs
only the difference in dead time at the start of each line.

The trigger scheme was already right -- the shutter fires on cd at t0
and the acquisition on ab is held back by _shutter_to_open_delay, with
the widths, burst_period and cont_grid's acc_time and premove all
derived from it. Only the value was wrong, and it was a literal in two
places, so setting one and not the other would have been silently undone
by keep_shutter_open_during_scan.

Lifts it to DEFAULT_SHUTTER_TO_OPEN_DELAY next to the other defaults,
with the measurement recorded, and adds set_shutter_to_open_delay to
USER_ACCESS so the value can be converged from the client instead of by
redeploying the device server. It is bounded, because a delay is paid on
every line and a fat-fingered value would stretch the scan rather than
fail.

Cost at the new value is 6 ms per line -- 0.26 s over scan 450 -- and
0.8 um of extra premove.

The existing stage test asserted the 2e-3 literal and now asserts the
constant. New tests cover the default, the bound, the USER_ACCESS entry,
that a set value actually reaches the ab channel while cd still fires at
t0, and that keep_shutter_open_during_scan discards a tuned value, which
is a sharp edge worth pinning rather than leaving to be rediscovered.
2026-08-25 21:21:21 +02:00