#%TITLE% BCDU8.MAC
#
#%NAME%
# Macros to control the 8-channel Bunch Clock Delay Unit (BCDU8).
#
#%DESCRIPTION%
#
# This macroset implements macromotors to drive the variuous
# programmable delays in a BCDU8 module. It also allows to open a
# dedicated window to dialogate interactively with the module, check
# and/or set the configuration and give the possibility of executing
# directly any BCDU8 command.
# The macro %B%bcdu8%B% opens the interactive window. The macros
# %B%bcdu_rflock%B% and %B%bcdu8_sync%B% perform initialisation and
# synchronisation actions. The configuration of macro motors is explained
# below .
#
#%DL%
#%DT%Configuring macro motors %DD%
# %DL%
# %DT% 1)
# You must have an entry in "MOTORS" table for each BCDU8 module
# %DL%
# %DT% -
# The "DEVICE" field must be set to string "bcdu8"
# %DT% -
# The "ADDR" must be set to the module hostname.
# The communication port can optionaly be specified also
# (ex: "bcdu8id261")
# %DT% -
# The "NUM" should be set to 8
# %DT% -
# The "TYPE" field must be set to "Macro Motors"
# %XDL%
# %DT% 2)
# For each delay value to control you must define a motor with:
# %DL%
# %DT% -
# The "Controller" field set to "MAC_MOT"
# %DT% -
# The "Unit" field, numbered from 0,
# set to MOTORS entry
# %DT% -
# The "Module" field, specify the delay value to control:
# %DL%
# %DT% - 1:
# coarse delay, unit is RF clock cycles
# %DT% - 2:
# fine delay, unit is RF clock cycles
# %DT% - 3:
# global delay, the channel field is not used
# %XDL%
# %DT% -
# The "Chan" field, numbered from 1 to 8,
# must be set to the particular BCDU8 channel
# %DT% -
# The "Backlash" value set to 0
# %XDL%
# %DT% 3)
# Extra "Custom Parameters":
# %DL%
# %DT% "user_unit"%DD%
# Specifies the time unit used by the macro motor,
# possible values are "ps" "ns" "us" "ms" "s" "#".
# If the parameter is not set, then the unit is RF clock cycles.
# %XDL%
#
# %DT% 4)
# Extra motor_par() parameters:
# %DL%
# %DT% motor_par(motor,"ramp_dd",delay)%DD%
# %DT% motor_par(motor,"ramp_dt",time)%DD%
# Instead of changing the delay values instantaneously, macromotors may vary
# the channel delays progressively by approximating the variation by a
# stepping ramp.
# The amplitud of each step is given by ramp_dd, while the minimum duration
# of each step is ramp_dt in seconds.
# In order to activate this special ramping mode, both parameters must
# be specified.
# %XDL%
#
#
#%XDL%
#%END%
#
#
#%DEPENDENCIES%
# These macros make use of the following macro sets:
# %UL%
# %LI%deepdevice.mac
# %XUL%
#%END%
#
#
# --------------------------- global definitions ---------------------------
#
def dprint 'print "BCDU8: "'
def dprinterr 'dprint "ERROR: "'
def dprintdbg 'if(BCDU8DEBUG) dprint'
constant BCDU8_COARSE 1
constant BCDU8_FINE 2
constant BCDU8_GLOBAL 3
constant BCDU8_UNITS " ps ns us ms s # "
#
#
#
need deepdevice
#
# ----------------------- MACRO MOTOR implementation -----------------------
#
#%IU%
#%MDESC%
# MACRO MOTOR:
# Called by spec after reading the config file
#
def bcdu8_config(num,type,p1,p2,p3) '{
local ans
local dev
local cmd
local typ
local cha
local uni
#
# p1==controller index p2==number of motors supported
#
if(type=="ctrl") {
if(whatis("bcdu8_ADDR") & 0x08000000) {
dprinterr "Missing \"ADDR\" field, must be set to hostname"
return ".error."
}
# try to connect to the device and check its type
if(!deepdev_add(bcdu8_ADDR,bcdu8_ADDR,3,"BCDU8")) {
dprinterr "unusable controller \""bcdu8_ADDR"\""
return ".error."
}
dprintdbg "using controller: \""bcdu8_ADDR"\""
}
#
# p1==unit p2==module p3==channel
#
if(type=="mot") {
dprintdbg "configuring motor: \""motor_mne(num)"\""
# check parameter type to control
if(p2 == BCDU8_COARSE) {
dprintdbg " type : coarse delay"
}
else if(p2 == BCDU8_FINE) {
dprintdbg " type : fine delay"
}
else if(p2 == BCDU8_GLOBAL) {
dprintdbg " type : global delay"
}
else {
dprinterr "unsupported parameter type, hint: set \"module\" field"
return ".error."
}
# check channel to use
if((p2 != BCDU8_GLOBAL) && ((p3 < 1) || (p3 > 8))) {
dprinterr "invalid channel, hint: to be set from 1 to 8"
return ".error."
}
# check fine delay channels
if((p2 == BCDU8_FINE) && (p3 != 1) && (p3 != 2)) {
dprinterr "invalid fine delay channel, hint: to be set to 1 or 2"
return ".error."
}
dprintdbg " channel: O"p3
# check that the selected channel is configured
dev = bcdu8_ADDR
typ = p2
cha = p3
if(typ != BCDU8_GLOBAL) {
cmd = sprintf("?CHAN O%d",cha)
ans = deepdev_comm(dev,cmd)
if(ans == DEEPDEV_ERRANSW) {
dprinterr "unable to get channel config"
return ".error."
}
dprintdbg " config : "ans
if(index(ans,"OFF")) {
dprinterr "channel \"O"cha"\" not configured"
return ".error."
}
}
# at this point we have a valid motor configuration
motor_par(num,"delay_mod",typ,"add")
motor_par(num,"channel", cha,"add")
motor_par(num,"ramp_dd", 0,"add")
motor_par(num,"ramp_dt", 0,"add")
# check if unit will be other than RF clock cycles
if(uni = motor_par(num,"user_unit")) {
if(!index(BCDU8_UNITS, sprintf(" %s ",uni))) {
dprinterr "invalid unit, hint: edit \"user_unit\" parameter in config"
# do not forget to remove bad parameter
motor_par(num,"user_unit",0)
return ".error."
}
dprintdbg " unit : "uni
}
# check if requested delay is within limits
bcdu8_update_limits(num)
}
}'
#%IU%
#%MDESC%
# MACRO MOTOR:
# Called by spec on motor_par()
#
def bcdu8_par(num,key,todo,p1) '{
# minimum auto documentation
if (key == "?") {
if (todo == "get") {
return "ramp_dd, ramp_dt, user_unit"
}
}
if (key == "ramp_dd") {
if (todo == "set") {
# NOTE MP 2011/01/01: once implemented in the BCDU module
# add here code to set the ramp inside the module
dprintdbg "ramping delta changed: "p1
}
}
if (key == "ramp_dt") {
if (todo == "set") {
dprintdbg "ramping dtime changed: "p1
}
}
}'
#%IU%(motor)
#%MDESC%
# MACRO MOTOR:
# Update the motor software limits if needed
#
def bcdu8_update_limits(num) '{
local ans
local dev
local cmd
local typ
local cha
local uni
local lim_low
local lim_high
dev = motor_par(num,"address")
cha = motor_par(num,"channel")
typ = motor_par(num,"delay_mod")
uni = motor_par(num,"user_unit") ; if(!uni) { uni="" }
# NOTE MP 2011/02/28: only the fine delay limits are dynamic but
# no optimization for the moment
if(typ == BCDU8_COARSE) {
cmd = sprintf("?DELAYLM O%d", cha)
} else if(typ == BCDU8_FINE) {
cmd = sprintf("?FDELAYLM O%d",cha)
} else if(typ == BCDU8_GLOBAL) {
cmd = sprintf("?GFDELAYLM")
}
# specify the unit wanted for the answer
cmd = sprintf("%s %s", cmd, uni)
ans = deepdev_comm_ack(dev,cmd)
if(ans == DEEPDEV_ERRANSW) {
dprinterr "unable to get limits"
return ".error."
}
if(sscanf(ans,"%g %g",lim_low, lim_high) != 2) {
dprinterr "wrong limits format"
return ".error."
}
dprintdbg "lim_high: "lim_high
dprintdbg "lim_low : "lim_low
if((get_lim(num,1) != lim_high) || (get_lim(num,-1) != lim_low)) {
dprintdbg "limits : changed"
set_lim(num, lim_low, lim_high)
}
}'
#%IU%
#%MDESC%
# MACRO MOTOR:
# Called by spec on motor operation.
#
def bcdu8_cmd(num,key,p1,p2) '{
local devcmd
local chacmd
local ans
local dev
local cmd
local typ
local cha
local uni
#
# Handle actions at controller level
#
if(num == "..") {
return
}
#
# Handle actions at motor level
#
dev = motor_par(num,"address")
cha = motor_par(num,"channel")
typ = motor_par(num,"delay_mod")
uni = motor_par(num,"user_unit") ; if(!uni) { uni="" }
# prepare channel string
chacmd = (typ == BCDU8_GLOBAL)?"":sprintf("O%d",cha)
#
# return the current motor position in user unit
#
if (key == "position") {
if(typ == BCDU8_GLOBAL) {
devcmd = "?GFDELAY"
} else {
devcmd = "?DELAY"
}
# specify the unit wanted for the answer
cmd = sprintf("%s %s %s", devcmd, chacmd, uni)
ans = deepdev_comm(dev,cmd)
if(ans == DEEPDEV_ERRANSW) {
dprinterr "unable to get delay value"
return ".error."
}
# normal end
dprintdbg "delay : "ans
return ans
}
#
# start a motion (p1==abs pos, p2==rel pos, with pos in mm or deg)
#
if (key == "start_one") {
local i
local p0
local ps
local pp
# check if requested delay is within limits
bcdu8_update_limits(num)
# request delay modification
if(typ == BCDU8_COARSE) {
devcmd = "DELAY"
} else if(typ == BCDU8_FINE) {
devcmd = "FDELAY"
} else if(typ == BCDU8_GLOBAL) {
devcmd = "GFDELAY"
}
# check if ramping well configured
ramp_dt = fabs(motor_par(num,"ramp_dt"))
ramp_dd = fabs(motor_par(num,"ramp_dd"))
if(ramp_dt || ramp_dd) {
if(!ramp_dt && ramp_dd) {
dprinterr "missing motor_par("motor_mne(num)",\"ramp_dt\",value)"
return ".error."
}
if(!ramp_dd && ramp_dt) {
dprinterr "missing motor_par("motor_mne(num)",\"ramp_dd\",value)"
return ".error."
}
# check if ramping is needed
if(fabs(p2) > ramp_dd) {
p0 = p1 - p2
ps = p2/fabs(p2)
dprintdbg "ramping"
for(i=0;i<int(fabs(p2)/ramp_dd);i++) {
pp = p0 + i*ramp_dd*ps
dprintdbg "move to : "pp
# special case of "fine delay steps" unit
if(uni == "#") {
cmd = sprintf("%s %s %s%d", devcmd, chacmd, uni, int(pp))
} else {
cmd = sprintf("%s %s %f%s", devcmd, chacmd, pp, uni)
}
ans = deepdev_comm_ack(dev,cmd)
if(ans == DEEPDEV_ERRANSW) {
dprinterr "unable to change delay"
return ".error."
}
sleep(ramp_dt)
}
}
}
# trigger change
dprintdbg "move to : "p1
# special case of "fine delay steps" unit
if(uni == "#") {
cmd = sprintf("%s %s %s%d", devcmd, chacmd, uni, int(p1))
} else {
cmd = sprintf("%s %s %f%s", devcmd, chacmd, p1, uni)
}
ans = deepdev_comm_ack(dev,cmd)
if(ans == DEEPDEV_ERRANSW) {
dprinterr "unable to change delay"
return ".error."
}
# For coarse motion update position+limits for each fine
# motor with the same channel (cf avoid PR+NQ in config)
# TODO MP: do the motor list scan only once
if(typ == BCDU8_COARSE) {
local i
local our_device_id our_address
local our_channel
local pos
our_device_id = motor_par(num, "device_id")
our_address = motor_par(num, "address")
our_channel = motor_par(num, "channel")
for(i=0;i<MOTORS;i++) {
if(i == num) { continue}
if(motor_par(i,"device_id") != our_device_id) { continue }
if(motor_par(i,"address") != our_address) { continue }
if(motor_par(i,"channel") != our_channel) { continue }
if(motor_par(i,"module") != BCDU8_FINE) { continue }
# Update also the position silently
# NOTE MP: the read_motors() will update position for all motors,
# no other way, except the NQ flag in config file
read_motors(0x06, i)
dprintdbg "fine motor to update: "motor_mne(i)
bcdu8_update_limits(i)
}
}
# normal end of start_one
return
}
}'
#
# ---------------------------- user macros -----------------------------
#
#%UU% [hostname]
#%MDESC%
# Launch an interactive command line interface in an external xterm window.
# This allows for instance to check the configuration of the module with the
# ?CONFIG query.
#
def bcdu8 '{
local ndev
local dev
local cmd
dev = $#?"$1":""
if(!dev && (ndev=list_n(DEEP_CONF))>=1) {
if(ndev>1) {
dprint "oops, not implemented yet, hint: give hostname"
}
i = 1
name = DEEP_CONF[i]
dev = DEEP_CONF[name]["comdev"]
}
cmd = sprintf("deep %s", dev)
eval(cmd)
}'
#%UU% [hostname]
#%MDESC%
# Initialize RF clock locking. To be used if the BCDU8 RFMODE is set
# to USER and the RF clock lock is lost.
#
def bcdu8_rflock '{
local dev
local ans
dev = deepdev_setdefault("BCDU8","$*")
if (dev) {
ans = deepdev_comm_ack(dev,"RFLOCK")
if(ans == DEEPDEV_ERRANSW) {
dprinterr "initializing RF clock"
}
}
}'
#%UU% [hostname]
#%MDESC%
# Try to synchronize the BCDU8 channels with external signal.
# The module waits for an external pulse at the 'SYNC IN' input.
# Automatically gives up after a while.
#
def bcdu8_sync '{
local dev
local ans
local cmd
local tbeg
dev = deepdev_setdefault("BCDU8","$*")
if (dev) {
ans = deepdev_comm_ack(dev,"SYNC")
if(ans == DEEPDEV_ERRANSW) {
dprinterr "synchronizing with external signal"
} else {
cmd = sprintf("print \"aborted !!\" ; \
deepdev_comm(\"%s\",\"SYNC CLEAR\")",dev)
cdef("cleanup_once",cmd)
print "waiting for external signal synchronization..."
tbeg = time()
while(deepdev_comm_ack(dev,"?SYNC") == "WAIT") {
if((time()-tbeg) > 2) {
print "timeout !!"
eval(cmd)
break
}
sleep(0.1)
}
}
}
}'
#%UU%
#%MDESC%
# Switches debug mode
#
def bcdu8debug '{
DEBUG ^= 0x80000000
dprint "debug mode is now:", BCDU8DEBUG? "On":"Off"
}'
def BCDU8DEBUG '(DEBUG & 0x80000000)'
#%MACROS%
#%IMACROS%
#%AUTHOR% PF+MP BLISS (Original 2011/Feb).
#%BR%$Revision: 1.1 $ / $Date: 2012/03/26 12:15:08 $
#%TOC%
|