Fix/ddg1 shutter delay #315
No Reviewers
Labels
Clear labels
issue::not confirmed
issue::not reproducible
issue::reproducible
priority::critical
priority::needed
priority::nice to have
scope::backend
scope::frontend
stage::in development
stage::on hold
stage::pre_release
stage::ready for review
stage::selected for development
stage::weekly backlog
type::bug
type::discussion
type::doc
type::feature
type::improvement
type::test
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
Used if an issue has not yet been confirmed by a developer
Used if an issue is not reproducible
Used if a developer was able to reproduce the issue
Should be done as soon as possible
An important update to the code base
Not really needed but a good improvement
An issue regarding the backend services
An issue regarding the user-facing services
This issue is currently being worked on
Something is blocking the development
For issues that have been merged into a pre-release branch and are waiting for their release.
For issues that have been resolved and are ready for review
This issue has been selected for development but has not been scheduled yet
This issue has been selected for development for the next development cycle
It's a bug!
A discussion is wanted/needed
Improvements to the documentation
A new feature
Neither a feature nor a bug but an improvement to the code base
Missing tests or improvements to the test suite
Something is not working
This issue or pull request already exists
New feature
Need some help
Something is wrong
More information is needed
This won't be fixed
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Assignees
acerbo_a (acerbo_a)
bec-gitea
debenjak_l (debenjak_l)
diaz (diaz)
divall_e (divall_e)
guizar_m (guizar_m)
haghta_s (haghta_s)
holler (holler)
lombosi_c (lombosi_c)
malu_s (malu_s)
menzel (menzel)
perl_d (perl_d)
usov_i (usov_i)
wakonig_k (wakonig_k)
wang_x1 (wang_x1)
wyzula_j (wyzula_j)
zamofing_t (zamofing_t)
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: bec/csaxs_bec#315
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two independent causes, both quantified during August commissioning: the shutter was given ~2 ms to open when it needs ~8 ms, and at_each_point slept only for acc_time before firing the burst — which covers the acceleration ramp but not the scan server → device server → EPICS → controller path, so on slow scans the trigger preceded the stage. The measurements and the scans they came from are in the commit messages and the code.
Touches scans_v4/cont_grid.py as well as ddg_1.py.
Deployed at cSAXS on 2026-08-25 (
f17af60,648c0e9) and in production since; the parameters read back 0.008 / 0.6. Re-measuring the first-point deficit on a fresh scan is still outstanding.Merged into current main: 101 failed / 546 passed / 12 skipped, against a baseline of 101 / 532 / 12 — pre-existing motor-controller failures from a local ophyd_devices skew.
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.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.