id3c.utils.flyscan_3idc_analysis#

Post-run analysis helpers for flyscan_3idc runs.

This module pairs each detector frame with the motor’s interpolated position at the frame’s IOC timestamp, using the monitor streams that flyscan_3idc.flyscan sets up via @bpp.monitor_during_decorator. No bluesky, no ophyd, no RunEngine — purely operates on BlueskyRun-shaped objects from tiled / databroker.

Usage#

from tiled.client import from_profile
cat = from_profile("your_profile")["your_tree"]
run = cat[-1]
from id3c.utils.flyscan_3idc_analysis import pair_frames_to_positions
df = pair_frames_to_positions(run)
# df columns: image_number, timestamp, position
# df.index: absolute timestamp (float seconds since epoch)
df.to_csv("scan.csv")

Design notes#

  • IOC timestamps are the system of record for pairing (per the flyscan_3idc strategy doc, Phase 0.2 / Phase 0e). The primary-stream snapshots from the plan are a progress indicator; this module’s output is the high-fidelity pairing.

  • Empirically (verified during the 2026-06-08 commissioning session against adsimdet + gp:m1): - the m1 monitor stream’s record-order is interleaved across

    multiple CA dispatcher segments; sorting by timestamp yields a strictly increasing position trace at constant velocity in the in-scan window.

    • the HDF array_counter monitor stream’s record-order is also interleaved, but sorting by timestamp yields strictly monotonic counter values (0, 1, 2, …, contiguous).

  • The function uses linear interpolation of motor position vs motor IOC timestamp. Linear is exact for a motor at constant velocity in the in-scan window (which is the entire reason the plan sets velocity = (p_end-p_start)/(num_frames*t_period) and taxis the motor up to scan velocity before crossing p_start).

  • Frames whose timestamps fall outside the motor stream’s time range are dropped (extrapolation is rejected, never silent).

  • Frames whose interpolated positions fall outside [p_start, p_end] are dropped (this is “frames captured during taxi-in / coast-out” — they’re in the HDF5 file but not part of the scan).

Attributes#

Functions#

pair_frames_to_positions(→ pandas.DataFrame)

Pair each in-scan HDF frame with the interpolated motor position.

Module Contents#

id3c.utils.flyscan_3idc_analysis.logger[source]#
id3c.utils.flyscan_3idc_analysis.pair_frames_to_positions(run) pandas.DataFrame[source]#

Pair each in-scan HDF frame with the interpolated motor position.

Reads everything from the run’s start-document metadata and the standard monitor streams set up by flyscan_3idc.flyscan:

  • <flymotor_name>_monitor — motor position vs IOC timestamp.

  • <det_name>_hdf1_array_counter_monitor — HDF array_counter vs IOC timestamp.

  • p_start / p_end from run.metadata["start"].

Frames captured outside [p_start, p_end] are dropped. Frames whose IOC timestamps fall outside the motor stream’s time range are dropped (extrapolation never happens silently).

Parameters:

run (BlueskyRun) – Tiled / databroker run object. Must have .metadata["start"] with the keys p_start, p_end, flymotor_name, det_name, and must expose monitor streams named per the convention above.

Returns:

Columns: image_number (int64, the HDF array_counter value at frame capture), timestamp (float, IOC time of capture), position (float, motor position in engineering units, linearly interpolated). Indexed by timestamp ascending.

Return type:

pandas.DataFrame

Raises:
  • KeyError – Required metadata key or monitor stream is missing from the run.

  • ValueError – Motor stream has fewer than 2 samples (cannot interpolate).