Positioners

Positioners other than EpicsMotor and SoftPositioner are not “ready-to-use”. They require some customization.

PVPositioner

For example, this code defines a CS700 temperature controller. A temperature controller is a kind of positioner, from ophyd’s point of view, where the “position” is the temperature.

from ophyd import PVPositioner, EpicsSignal, EpicsSignalRO
from ophyd import Component as Cpt

# Define a new kind of device.

class CS700TemperatureController(PVPositioner):
    setpoint = Cpt(EpicsSignal, 'T-SP')
    readback = Cpt(EpicsSignalRO, 'T-I')
    done = Cpt(EpicsSignalRO, 'Cmd-Busy')
    stop_signal = Cpt(EpicsSignal, 'Cmd-Cmd')

# Create an instance of this new kind of device.

prefix = 'XF:28IDC-ES:1{Env:01}'
cs700 = CS700TemperatureController(prefix, name='cs700')

# When the cs700 has reached the set-point temperature, the 'done' signal
# flips to 0.
cs700.done_value = 0
class ophyd.pv_positioner.PVPositioner(prefix='', *, limits=None, name=None, read_attrs=None, configuration_attrs=None, parent=None, egu='', **kwargs)

A Positioner which is controlled using multiple user-defined signals

Keyword arguments are passed through to the base class, Positioner

Parameters:
prefix : str, optional

The device prefix used for all sub-positioners. This is optional as it may be desirable to specify full PV names for PVPositioners.

limits : 2-element sequence, optional

(low_limit, high_limit)

name : str

The device name

egu : str, optional

The engineering units (EGU) for the position

settle_time : float, optional

The amount of time to wait after moves to report status completion

timeout : float, optional

The default timeout to use for motion requests, in seconds.

Attributes:
setpoint : Signal

The setpoint (request) signal

readback : Signal or None

The readback PV (e.g., encoder position PV)

actuate : Signal or None

The actuation PV to set when movement is requested

actuate_value : any, optional

The actuation value, sent to the actuate signal when motion is requested

stop_signal : Signal or None

The stop PV to set when motion should be stopped

stop_value : any, optional

The value sent to stop_signal when a stop is requested

done : Signal

A readback value indicating whether motion is finished

done_val : any, optional

The value that the done pv should be when motion has completed

put_complete : bool, optional

If set, the specified PV should allow for asynchronous put completion to indicate motion has finished. If actuate is specified, it will be used for put completion. Otherwise, the setpoint will be used. See the -c option from caput for more information.

PseudoPositioner

An ophyd PseudoPositioner relates one or more pseudo (virtual) axes to one or more real (physical) axes via forward and inverse calculations. To define such a PseudoPositioner, one must subclass from PseudoPositioner:

from ophyd import (PseudoPositioner, PseudoSingle, EpicsMotor)
from ophyd import (Component as Cpt)
from ophyd.pseudopos import (pseudo_position_argument,
                             real_position_argument)


class Pseudo3x3(PseudoPositioner):
    # The pseudo positioner axes:
    px = Cpt(PseudoSingle, limits=(-10, 10))
    py = Cpt(PseudoSingle, limits=(-10, 10))
    pz = Cpt(PseudoSingle)

    # The real (or physical) positioners:
    rx = Cpt(EpicsMotor, 'XF:31IDA-OP{Tbl-Ax:X1}Mtr')
    ry = Cpt(EpicsMotor, 'XF:31IDA-OP{Tbl-Ax:X2}Mtr')
    rz = Cpt(EpicsMotor, 'XF:31IDA-OP{Tbl-Ax:X3}Mtr')

    @pseudo_position_argument
    def forward(self, pseudo_pos):
        '''Run a forward (pseudo -> real) calculation'''
        return self.RealPosition(rx=-pseudo_pos.px,
                                 ry=-pseudo_pos.py,
                                 rz=-pseudo_pos.pz)

    @real_position_argument
    def inverse(self, real_pos):
        '''Run an inverse (real -> pseudo) calculation'''
        return self.PseudoPosition(px=-real_pos.rx,
                                   py=-real_pos.ry,
                                   pz=-real_pos.rz)

Pseudo3x3 above is a pseudo positioner with 3 pseudo axes and 3 real axes. The pseudo axes are defined in order as (px, py, pz). Similarly, the real positioners are (rx, ry, rz).

There is no restriction that the real axes must be tied to physical hardware. A physical axis could just as well be a SoftPositioner, or any subclass of PositionerBase (with the sole exception of PseudoSingle).

The forward calculation says that, for any given pseudo position, the real motors should move to the opposite position. For example, for a pseudo position of (px=1, py=2, pz=3), the corresponding real position would be (rx=-1, ry=-2, rz=-3). The inverse calculation is similar, in going from a real position to a pseudo position.

The two decorators @real_position_argument and @pseudo_position_argument are used here for convenience so that one can call these functions in a variety of ways, all of which generate a correct PseudoPosition tuple as the first argument to your calculation method. Positions can be specified in the following ways:

  • As positional arguments:
pseudo.forward(px, py, pz)
  • As a sequence or PseudoPosition/RealPosition:
pseudo.forward((px, py, pz))
pseudo.forward(pseudo.PseudoPosition(px, py, pz))
  • As kwargs:
pseudo.forward(px=1, py=2, pz=3)

move is decorated like this on PseudoPositioner, meaning you can also call it with this syntax.

class ophyd.pseudopos.PseudoSingle(prefix='', *, limits=None, egu='', parent=None, name=None, source='computed', target_initial_position=False, **kwargs)

A single axis of a PseudoPositioner

This should not be instantiated on its own, but rather used as a Component in a PseudoPositioner subclass.

Parameters:
prefix : str, optional

The PV prefix, for compatibility with the Device hierarchy

limits : (low_limit, high_limit)

User-defined limits for this pseudo axis.

egu : str, optional

The engineering units (EGU) for the position

parent : PseudoPositioner instance

The instance of the parent PseudoPositioner

name : str, optional

The name of the positioner

source : str, optional

Metadata indicating the source of this positioner’s position. Defaults to ‘computed’

target_initial_position : bool, optional

Use initial inverse-calculated position as the default setpoint/target for this axis

settle_time : float, optional

The amount of time to wait after moves to report status completion

timeout : float, optional

The default timeout to use for motion requests, in seconds.

class ophyd.pseudopos.PseudoPositioner(prefix='', *, concurrent=True, read_attrs=None, configuration_attrs=None, name, egu='', auto_target=True, **kwargs)

A pseudo positioner which can be comprised of multiple positioners

Parameters:
prefix : str

The PV prefix for all components of the device

concurrent : bool, optional

If set, all real motors will be moved concurrently. If not, they will be moved in order of how they were defined initially

read_attrs : sequence of attribute names

the components to include in a normal reading (i.e., in read())

configuration_attrs : sequence of attribute names

the components to be read less often (i.e., in read_configuration()) and to adjust via configure()

name : str, optional

The name of the device

egu : str, optional

The user-defined engineering units for the whole PseudoPositioner

auto_target : bool, optional

Automatically set the target position of PseudoSingle devices when moving to a single PseudoPosition

parent : instance or None

The instance of the parent device, if applicable

settle_time : float, optional

The amount of time to wait after moves to report status completion

timeout : float, optional

The default timeout to use for motion requests, in seconds.

SoftPositioner

A SoftPositioner is a positioner which has no corresponding physical motor. On its own, it is most useful for debugging scanning logic when moving physical motors is either undesirable or not possible.

Used as-is, a SoftPositioner will “move” to the requested position immediately.

PseudoSingle and PseudoPositioner, for example, are implemented as heavily customized SoftPositioner subclasses.

class ophyd.positioner.PositionerBase(*, name=None, parent=None, settle_time=0.0, timeout=None, **kwargs)

The positioner base class

Subclass from this to implement your own positioners.

Note

Subclasses should add an additional ‘wait’ keyword argument on the move method. The MoveStatus object returned from PositionerBase can then be waited on after the subclass finishes the motion configuration.

class ophyd.positioner.SoftPositioner(*, egu='', limits=None, source='computed', init_pos=None, **kwargs)

A positioner which does not communicate with any hardware

SoftPositioner ‘moves’ immediately to the target position when commanded to do so.

Parameters:
limits : (low_limit, high_limit)

Soft limits to use

egu : str, optional

Engineering units (EGU) for a position

source : str, optional

Metadata indicating the source of this positioner’s position. Defaults to ‘computed’

from ophyd import SoftPositioner
my_positioner = SoftPositioner(name='my_positioner')