Integration of PandA with Motion and Sensors#

In this section we will outline how to interface motion and sensors with the PandA, and how to setup the PandA for use with ophyd-async.

Encoder I/O#

In order to leverage motor positions in your flyscanning setup, you will require a compatible encoder. The encoder should be RS422, and be one of:

  • Incremental (A Quad B)

  • SSI

  • BISS-C

  • enDat

In all cases, adapt the encoder cable pinout to that of the PandA - pinout documentation is readily available in the manual and online. Connect the encoder cable to one of the four encoder ports on the PandA.

Next, in the Web UI, drag-and-drop the corresponding INENC block onto the GUI, and click on it to open the configuration menu on the right hand side of the page.

Here, find the option for setting encoder signal type, and match it to the one configured for your motor. Once this is done, move the motor and confirm that the value reported by the encoder block moves by an expected number of counts. You can also double check this by looking at the raw encoder count value from the motor controller, and confirming that the delta from position A to B is the same for the controller and the PandA.

In the event that the encoder direction is reversed on the PandA and the controller, it can be re-inverted by feeding it’s signal to a CALC block.

One additional feature of the INENC block is the toggle to Reset on Z. This will reset the PandA’s encoder block reading to zero when a z-pulse is encountered. This feature is especially useful for rotatry motions that we want represented as an angle; the reset on Z option ensures that we can retrieve a degree value without needing to perform a modulo division first.

Onboard ADC#

The PandA optionally provides ADC and DAC capabilities. If the ADC card has been installed, the readings from the ADCs can be accessed via the FMC_IN block (and similarly FMC_OUT for DAC).

Please consult the PandA manual for referencing ADC specifications. Note that just like with any other signal, the PandA can be configured to capture the value, mean, max, min, etc. of each ADC channel.

TTL I/O#

The PandA offers ten TTL output ports and six input ports as standard. These operate with the typical 0V low, 5V high. In order to trigger devices externally, see the device manuals for wiring. You may need BNC cable splitouts or adapters.

As a rule of thumb, use a single TTL output for each acquisition triggering frequency you need. For example, if you have two detectors being triggered at 10 Hz, use a single TTL output with a splitter if possible.

The TTL inputs can be used by other devices in order to initiate DAQ, or to keep track of when a detector was exposing. This can be useful in indentifying dropped frames; storing whether a detector ready signal is high/low when a trigger is sent can easily indicate a drop, or potentially be used as a condition to wait until the detector is ready.

It is even possible to use the TTL Input ready signal to trigger the detector directly, immediately after it is back to being ready, hence minimizing deadtime between exposures.

Ophyd-Async#

In order to utilize all of the above in DAQ plans, you will need to instantiate an ophyd-async PandA device, as well as devices for each of your triggered detectors.

To create a PandA ophyd_async device, you may use the following snippet:

from pathlib import Path
from ophyd_async.fastcs.panda import HDFPanda
from ophyd_async.core import init_devices, StaticPathProvider, UUIDFilenameProvider
from bluesky.run_engine import RunEngine

path_provider = StaticPathProvider(
    UUIDFilenameProvider(),         # Generates UUID filenames
    Path("/tmp/panda_data"),        # Base directory path
    create_dir_depth = -1           # Determines how many levels of directories to create
)

RE = RunEngine({})

with init_devices():
    panda = HDFPanda("MY:PV:PREFIX", path_provider, name="panda")

Note that the instantiation of the run engine in the namespace is required prior to calling the init_devices context manager, since the asynchronous connection operation will be executed in the run engine’s event loop.

Once created, you can see the available blocks with:

list(panda.children())

Specific blocks are either attributes (if there is one instance of the block), or elements in a list that is an attribute. For example, to access the INENC1 block, we use:

panda.inenc[1]

We can then see the signals that are provided by this block with the same children generator:

list(panda.inenc[1].children())

In order for the PandA’s ophyd_async device to generate resource and datum documents for any specific dataset, it must first be configured to be captured, and then a scientifically relevant name must be given to the dataset.

For example, for a tomography experiment where we are interested in recording the angle at which the rotatry motion was at for each trigger pulse, we have the PandA record the value of INENC1.VAL at each trigger (scaling it by a constant to convert encoder positions to angles), and then give the resulting dataset a name of Angles.

To do this via the ophyd device, we can do the following:

from ophyd_async.core import Settings
from ophyd_async.plan_stubs import apply_panda_settings

tomo_capture_settings = Settings(
    panda,
    {
        panda.inenc[1].val_capture: "Value",
        panda.inenc[1].val_dataset: "Angles"
    }
)

RE(apply_panda_settings(tomo_capture_settings))

The apply_panda_settings stub is a special version of apply_settings_if_different that ensures that the settings applied to the PandA are done in the correct order. For example, units are set before values etc.

Example Configurations#

Below you will find guides for confiugring a full stack flyscanning solution using the described hardware and methodologies above for certain techniques.

Tomography#

A tomography flyscan can more-or-less be generalized to a single motion axis flyscan (the rotation axis), and can be setup to be performed via equidistant in position triggers, or equidistant in time.

In both cases, connect your movement axis encoder signal to an encoder input on the PandA, and confirm that it is being read correctly. Consult the instructions above for this.

Next, connect the appropriate encoder block’s output to the input of the PCOMP block. This is the block responsible for comparing values.

In the case of equidistant in position triggers, the output of this block is fed directly to the trigger input of PCAP, and the TTLOUT blocks connected to external detectors.

Alternatively, in the case of equidistant in time, the output is redirected to PULSE block.

In either case, construct a settings object to setup the requisit parameters for the PCOMP block: