Skip to content

Add acquisition automation modes and fine-tune pipeline#397

Open
cfremling wants to merge 4 commits intomainfrom
feature/acq-automation-clean
Open

Add acquisition automation modes and fine-tune pipeline#397
cfremling wants to merge 4 commits intomainfrom
feature/acq-automation-clean

Conversation

@cfremling
Copy link
Collaborator

Summary

  • Implement three acquisition automation modes (MANUAL, AUTOMATIC, FULL_AUTOMATIC) controlled via the new acqmode command, enabling configurable hands-off target acquisition
  • Integrate ngps_acq fine-acquisition algorithm in slicecamd (source detection, centroiding, TCS offsets, closed-loop acquisition) via C callback API
  • Publish ACAM guiding/acquiring state and preserve guiding during offsets
  • Add seq_progress_gui (X11+ZMQ) for real-time observation phase display (SLEW/SOLVE/FINE/OFFSET/EXPOSE)
  • Add configuration parameters for acquisition timeouts and fine-tune settings

Files changed (19)

New files (4): slicecamd/ngps_acq.c, common/ngps_acq_embed.h, utils/seq_progress_gui.cpp, common/message_keys.h, run/seq-progress

Modified files (14): sequencerd/sequence.cpp, slicecamd/slicecam_interface.cpp, sequencerd/sequence.h, sequencerd/sequencer_server.cpp, slicecamd/slicecam_interface.h, Config/sequencerd.cfg.in, Config/slicecamd.cfg.in, acamd/acam_interface.cpp, utils/CMakeLists.txt, slicecamd/CMakeLists.txt, common/sequencerd_commands.h, common/slicecamd_commands.h, sequencerd/sequencerd.cpp, slicecamd/slicecam_server.cpp

Test plan

  • Build with cmake .. && make -j$(nproc) — verify all 19 files compile cleanly
  • Verify acqmode command accepts MANUAL/AUTOMATIC/FULL_AUTOMATIC
  • Test acquisition state machine transitions: SLEW → SOLVE → FINE → OFFSET → EXPOSE
  • Verify seq_progress_gui launches and displays phase updates via ZMQ
  • Test fine-acquisition loop with slicecam source detection and TCS offsets
  • Confirm ACAM guiding state is preserved during offset_goal operations

🤖 Generated with Claude Code

Properly rebased against main, preserving all existing functionality
(can_expose wait, channel activate/deactivate, camerad ZMQ handler).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cfremling cfremling force-pushed the feature/acq-automation-clean branch from 723cc0a to 527fe81 Compare March 7, 2026 01:57
cfremling and others added 2 commits March 6, 2026 19:15
Latch ontarget=true when received; only clear on new target via
reset_progress_only(). Prevents blinking caused by transient 1-second
is_ontarget signal being repeatedly published as false.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
get_logical() was reading detrows/detcols (skip-adjusted) and
osrows/oscols (binning-divided), causing set_image_size to shrink
the image on every call. Use defrows/defcols and defosrows/defoscols
(original config values) so repeated bin commands are idempotent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a single thread foreground process with one loop that does everything in turn on each pass through the loop:

  1. process X-window events
  2. reads the ZMQ message buffer
  3. it forces EVERY daemon to republish a snapshot of its telemetry, regardless if it didn't change
  4. sends a TCP/IP socket command to sequencerd, requesting its state (which was in the published message in step 3)
  5. sends a TCP/IP socket command to sequencerd, requesting its wait state (which was in the published message in step 3)
  6. sends a TCP/IP socket command to sequencerd, requesting its acquisition mode (which was in the published message in step 3)
  7. sends a TCP/IP socket command to acamd, requesting is acquisition mode (which was in the published message in step 3)
  8. redraws the GUI

This must takes tens to hundreds of milliseconds and with the built-in sleep could take on the order of half a second to get through the loop. Since it is single threaded and pulls out only one ZMQ message at a time, the ZMQ buffer could fill faster than this loop can read it, causing it to get behind. A daemon will publish whenever there is a change in status so there are more messages than the GUI is forcing to be generated.

One benefit of the PUB-SUB model is that subscribers need only listen and will be told if something changes. This is forcing every daemon in the instrument to re-publish, generating excess network traffic, on top of the four TCP/IP socket writes that request redundant information.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed all of these points in the latest commit:

  1. ZMQ buffer drain — process_zmq() now drains up to 50 messages per loop iteration using a non-blocking check
    (has_message_nonblock()), so bursts from daemon publishes are consumed promptly before a single draw() call.
  2. Removed all 4 redundant TCP commands — poll_sequencer() (state, wstate, acqmode) and poll_acam() (acquire) are
    deleted entirely. All this data is already in the ZMQ topics the GUI subscribes to: seq_seqstate, seq_waitstate,
    seq_progress (contains acqmode), and acamd (contains ACAM_GUIDING/ACAM_ACQUIRE_MODE).
  3. Snapshot requests rate-limited — request_snapshot() is now rate-limited to once per 5s, and maybe_poll()
    only requests snapshots when data is actually stale (10s without updates, checked every 15s). Removed the aggressive
    1s snapshot request during USER wait states.
  4. ZMQ fd in select() — Added the ZMQ socket fd alongside the X11 fd in select(), so the loop wakes immediately when
    messages arrive instead of waiting the full 200ms timeout.

One required addition: since acamd doesn't publish a snapshot when do_acquire() fails internally (e.g., guiding lost
due to bad seeing/weather), added iface.publish_snapshot() in dothread_framegrab right after the acquire(TARGET_NOP)
call on failure. This ensures guiding state changes are immediately visible to ZMQ subscribers without polling.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cfremling why do we need this GUI? Per the design document, we would just have the main observing GUI change it's status as needed based on the sequencer's state

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the observer needs to see what is going on in a more fine grained way, mainly in case there is a failure at some step, so that the observer can know what to do next. It would be ideal if the main gui did it (but I needed something to look at when developing the algorithm). If we can have the same or very similar features and indicator progress somewhere on the main gui I am all for it.

…ffer

- Add get_fd() and has_message_nonblock() to PubSub for non-blocking
  ZMQ usage and select() integration
- Replace single-message ZMQ read with bounded drain loop (max 50/cycle)
- Add ZMQ fd to select() so GUI wakes immediately on messages
- Remove all TCP polling (poll_sequencer, poll_acam, poll_status,
  should_poll_acam) — data already available via ZMQ topics
- Simplify maybe_poll() to staleness-only snapshot requests (15s)
- Rate-limit request_snapshot() to min 5s between calls
- Remove request_snapshot() from handle_waitstate() USER path
- Remove acam_iface_ member and eager TCP open in init()
- Downgrade NBPORT check from fatal to warning (TCP only needed
  for ontarget/usercontinue buttons)
- Add publish_snapshot() in acamd dothread_framegrab on acquire
  failure so guiding state changes are immediately visible via ZMQ

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants