10 KiB
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://gitea.psi.ch/lin-epics-modules/turboPmac). It consists of the following four objects:
detectorTowerController
: This is an expanded variant ofturboPmacController
provided by the Turbo PMAC library linked above.It is needed to operate adetectorTowerAngleAxis
, but it can also be used to operate a "normal"turboPmacAxis
.detectorTowerAngleAxis
: This is a virtual axis which controls multiple physical motors (x
andz
) in order to provide a combined movement. Moving it results in a rotation of the entire beam around the support axis position (\alpha
).detectorTowerLiftAxis
: This is a virtual axis which controls multiple physical motors in order to provide a combined movement. Moving it results in a vertical lift (z_{lift}
).detectorTowerSupportAxis
: This is an axis at the rotation center of the beam which is part of the combined movements. Its origin can be shifted manually for small adjustments, resulting in a corresponding movement. Other than that, it is not meant to move on its own, hence setting a new value to theVAL
field of the motor record won't cause it to move.
User guide
The centerpiece of this driver is the detectorTowerAngleAxis
, which controls the angle of the detector flight tube. It is supported by two secondary axes, detectorTowerLiftAxis
and detectorTowerSupportAxis
. All three axes are created by a single IOC shell command detectorTowerAxis
(see Usage in IOC shell). All three axes use absolute encoders and therefore cannot perform a reference / home drive.
The first two axes can be moved independently from each other or together as a combined movement by issuing both move orders within a certain time span. This time span can be configured via the IOC shell function setDeferredMovementWait
and defaults to 100 ms. This allows the user to start a combined movement e.g. via caput:
caput $(ControllerPV):angle 2 & $(ControllerPV):caput lift 10
which moves the angle to 2 degree and shifts the entire beam vertically by 10 mm. When using the axis from NICOS, using the maw
or move
command with multiple devices has the same effect:
move("angle", 2, "lift", 10)
If one axis is already moving, no new move command can be issued until the movement is finished. The detectorTowerSupportAxis
cannot be moved directly.
It is possible to shift the origin of all three axes (and therefore also moving the detectorTowerSupportAxis
) for small adjustments. The current origin position in the axis unit (degree for detectorTowerAngleAxis
, mm for detectorTowerLiftAxis
and encoder steps for detectorTowerSupportAxis
) can be read from the PV $(INSTR)$(M):Origin
. The origin can be shifted by the amount of axis units written to $(INSTR)$(M):AdjustOrigin
. Since this is a relative movement, writing "10" two times to $(INSTR)$(M):AdjustOrigin
shifts the origin by 20 axis units. Therefore, the limits are only there to prevent too large shifts during a single write (since the origin can be shifted to any position by writing to $(INSTR)$(M):AdjustOrigin
repeatedly).
The detector tower can be moved into a so-called "changer position" by writing "1" to the PV $(INSTR)$(M):ChangeState
. This causes the entire tower to move vertically down. When it is in the changer position, the axes cannot be moved at all. In order to go back to the "working state" where the axes can be moved again, write "0" to $(INSTR)$(M):ChangeState
. The current state of the axis can be read out from $(INSTR)$(M):ChangingStateRBV
. In case starting a change movement succeeds, $(INSTR)$(M):ChangeStateRBV
changes its value accordingly, otherwise it stays at its current value.
The utilities provided in the utils
folder in turboPmac/utils
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 adetectorTowerAngleAxis
, adetectorTowerLiftAxis
and adetectorTowerSupportAxis
object and link them to each other
The constructor function for detectorTowerAxis
has the following syntax:
detectorTowerAngleAxis("$(NAME)",1, 2, 3)
with 1 being the axis number / index of the detectorTowerAngleAxis
, 2 being the detectorTowerLiftAxis
and 3 being the detectorTowerSupportAxis
. 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 the three detector tower axis, while the slots 4 and 5 are "normal" Turbo PMAC axes.
detectorTowerAngleAxis("$(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","$(detectorTower_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(detectorTower_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:
detectorTowerAngleAxis("$(NAME)",1, 2, 3);
turboPmacAxis("$(NAME)",4);
turboPmacAxis("$(NAME)",5);
file "$(SINQDBPATH)"
{
pattern
{ AXIS, M, DESC, EGU, DIR, MRES, MSGTEXTSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED }
{ 1, "angle", "Angle of the beam guide", degree, Pos, 0.001, 200, 0, 1.0, 0 }
{ 2, "lift", "Detector vertical lift offset", mm, Pos, 0.001, 200, 0, 1.0, 0 }
{ 3, "suppoer", "Support axis", mm, 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 detectorTowerAngleAxis
, which controls the angle of the beam guide and acts as a "master" axis which contains pointers to its attached detectorTowerLiftAxis
and detectorTowerSupportAxis
. All three axes are polled at once via the function detectorTowerController::pollDetectorAxes
, which is called from the individual poll
functions of the axes (the doPoll
mechanism from sinqMotor
is not used). To avoid polling the axes multiple times during one controller cycle, the function detectorTowerController::pollDetectorAxes
is only executed for the axis with the smallest index. Since this axis is polled first, the other two axes are therefore already up-to-date when they execute their own poll function. If one of the axes is moving, all three axes are marked as moving. The same is true for errors.
In order to save on movement time, movement commands to auxiliary axes and the detectorTowerAngleAxis
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 detectorTowerAngleAxis->deferredMovementWait_
seconds and checks if commands for other axes are given as well. Then, it calls detectorTowerAngleAxis::startCombinedMove
which combines all commands to a single request which is sent to the MCU. detectorTowerAngleAxis->deferredMovementWait_
can be set with the IOC shell function setDeferredMovementWait
and defaults to 100 ms.
The detectorTowerController
is a thin wrapper around a turboPmacController
which overwrites the readInt32
, writeInt32
and writeFloat64
methods in order to support the additional PVs defined in db/detectorTower.db
. Any calls to these two methods not concerning the aforementioned PVs are directly forwarded to turboPmacController::readInt32
/ turboPmacController::writeInt32
.
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
This driver can be compiled and installed by running make install
from the same directory where the Makefile is located. However, since it uses the git submodule turboPmac, make sure that the correct version of the submodule repository is checked out AND the change is commited (git status
shows no non-committed changes). Please see the section "Usage as static dependency" in https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md for more details.