detectorTower

Please read the documentation of sinqMotor first: https://git.psi.ch/sinq-epics-modules/sinqmotor

Overview

This is a driver for the detector tower which is based on the Turbo PMAC driver (https://git.psi.ch/sinq-epics-modules/turboPmac). It consists of the following three objects:

  • detectorTowerAxis: This is a virtual axis which controls multiple physical motors in order to provide a synchronized movement.
  • detectorTowerController: This is an expanded variant of turboPmacController provided by the Turbo PMAC library linked above.It is needed to operate a detectorTowerAxis, but it can also be used to operate a "normal" turboPmacAxis.
  • auxiliaryAxis: This is an auxiliary axis type attached to the main detector axis. Multiple instances of these axes are constructed automatically when creating a detectorTowerAxis. The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.

User guide

The centerpiece of this driver is the detectorTowerAxis, which controls the angle of the detector flight tube. Creating an instance of this axis type also creates multiple so-called auxiliaryAxis, which are used to perform secondary movements.

The utilities provided in the utils folder of https://git.psi.ch/sinq-epics-modules/turboPmac work with this driver as well.

Usage in IOC shell

detectorTower exports the following IOC shell functions:

  • detectorTowerController: Create a new controller object.
  • detectorTowerAxis: Create a new axis object.

The constructor function for detectorTowerAxis has the following syntax:

detectorTowerAxis("$(NAME)",1, 2, 3)

with 1 being the axis number / index of the detector flight tube angle axis, 2 being the lift offset axis and 3 being the tilt offset axis. These axes are parametrized in the same way as any "normal" axes via a substitution file (see corresponding section below).

"Normal" turboPmacAxis may be used together with this controller:

# Define the name of the controller and the corresponding port
epicsEnvSet("NAME","mcu")
epicsEnvSet("ASYN_PORT","p$(NAME)")

# Create the TCP/IP socket used to talk with the controller. The socket can be adressed from within the IOC shell via the port name
drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")

# Create the controller object with the defined name and connect it to the socket via the port name.
# The other parameters are as follows:
# 8: Maximum number of axes
# 0.05: Busy poll period in seconds
# 1: Idle poll period in seconds
# 1: Socket communication timeout in seconds
detectorTowerController("$(NAME)", "$(ASYN_PORT)", 8, 0.05, 1, 1);

# Slot 1, 2 and 3 are occupied by a detector tower axis and its attached auxiliary axes, while the slots 4 and 5 are "normal" Turbo PMAC axes.
detectorTowerAxis("$(NAME)",1, 2, 3);
turboPmacAxis("$(NAME)",4);
turboPmacAxis("$(NAME)",5);

# Set the number of subsequent timeouts 
setMaxSubsequentTimeouts("$(NAME)", 20);

# Configure the timeout frequency watchdog:
setThresholdComTimeout("$(NAME)", 100, 1);

# Parametrize the EPICS record database with the substitution file named after the MCU.
# Since this driver is based on Turbo PMAC, we need to parametrize turboPmac.db in addition to sinqMotor.db and detectorTower.db.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/turboPmac.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(detectorTower_DB)/detectorTower.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")

Substitution file

From the perspective of EPICS, the main detector flight tube axis and the auxiliary axes are independent axes and therefore each axis needs its own parametrization in the substitution file. If additional axes are used in the axis, they are parametrized as usual:

detectorTowerAxis("$(NAME)",1, 2, 3);
turboPmacAxis("$(NAME)",4);
turboPmacAxis("$(NAME)",5);
file "$(SINQDBPATH)"
{
pattern
{ AXIS,      M,         DESC,                               EGU,    DIR, MRES,          MSGTEXTSIZE,         ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED }
{ 1,    "tower",        "Angle of the beam guide",          degree, Pos, 0.001,         200,                 0,                 1.0,          0 }
{ 2,    "liftZeroCorr", "Detector vertical lift offset",    mm,     Pos, 0.001,         200,                 0,                 1.0,          0 }
{ 3,    "tiltZeroCorr", "Detector tilt offset",             degree, Pos, 0.001,         200,                 0,                 1.0,          0 }
{ 4,    "other axis A", "A normal axis",                    degree, Pos, 0.001,         200,                 1,                 2.0,          1 }
{ 5,    "other axis B", "Another normal axis",              degree, Pos, 0.001,         200,                 1,                 2.0,          0 }
}

Note that the speed of the detector tower axes 1, 2 and 3 cannot be set. Setting CANSETSPEED to 1 does not change this behaviour.

Developer guide

Code architecture

The code is designed around the detectorTowerAxis, which controls the angle of the beam guide and acts as a "master" axis. Other movements are realized as auxiliary axes (e.g. for the vertical lift offset and the tilt offset). The detectorTowerAxis has pointers to all associated auxiliary axes and (as described above) its IOC shell constructor also builds the associated auxiliary axes.

The doPoll implementation for detectorTowerAxis queries the status of both the detectorTowerAxis itself and all associated auxiliary axes. If any of the axes is moving, the detectorTowerAxis is set to "moving" as well. In turn, the doPoll implementation of a auxiliaryAxis checks if its associated detectorTowerAxis is moving and sets its own movement status accordingly.

The detectorTowerController is a thin wrapper around a turboPmacController which overwrites the readInt32 and writeInt32 in order to support the PVs "$(INSTR)$(M):ChangeState", "$(INSTR)$(M):PositionStateRBV" and "$(INSTR)$(M):ChangingStateRBV". Any calls to these two methods not concerning the aforementioned PVs are directly forwarded to turboPmacController::readInt32 / turboPmacController::writeInt32.

In order to save on movement time, movement commands to auxiliary axes and the detectorTowerAxis are collected and then send as a single resulting movement command to the MCU. In order to do so, a "collector" thread is running which checks if a movement request has been send to one of the axes. If that is the case, it waits for some time and checks if commands for other axes are given as well. Then, it calls detectorTowerAxis::startCombinedMove which combines all commands to a single request which is sent to the MCU. This allows the user to start a combined movement e.g. via caput:

caput tower 2 & caput liftZeroCorr 10

When using the axis from NICOS, using the maw or move command with multiple devices has the same effect:

move("tower", 2, "liftZeroCorr", 10)

Versioning

Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.

How to build it

Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.

Description
Driver for the detector tower at AMOR (based on the turboPmac driver)
Readme 212 KiB
Languages
C++ 98.4%
Makefile 1.6%