#%TITLE% OSCILLATION.MAC
#%NAME%
# Oscillation macros for X-ray diffractaion data collection
#
#%CATEGORY% Scans
#
#%DESCRIPTION%
# Use for standard single crystal X-ray diffraction data collection.
# The sample is exposed to the X-ray beam rotating with constant speed on a
# goniometer spindle while the diffraction pattern is recorded by an area
# detector (X-ray image intensifier/CCD).
# Because of the short exposure times at a synchtrotron (down to 0.1 s) the
# timing of X-ray shutter and rotation has to be done with care.
#%BR%
# The stepper motor pulses for the spindle rotation are monitored by counting
# module (VME Counter-Timer 6). This card generates a gate pulse opening and
# closing an electromagnetical fast X-ray shutter at the begin and end of the
# oscillation range. Acceleration and deceleration ramp are taken into account
# as well.
#
#%BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR%
#%BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR%
# On ID30 the VCT6 card has to be connected the following way:
#%BR% %BR%
# From the Mtheta motor the "%B%TOPMOT%B%" signal MUST be connected to both
# %B%STRT 2! (not 1)%B% and equally %B%IN 2! (not 1)%B%.
#%BR% %BR%
# This is in the little rectangle on the front of the Vct6:
#%BR% %BR%
#%PRE%
# ----------------
# | O ......... O |<-
# | 1 . STRT .. 2 | |
# | O ......... O | |--- connect both to TOPMOT mth
# | 1 . STOP .. 2 | |
# | O ......... O | |
# | 1 GATE-IN . 2 | |
# | O ......... O |<-
# | 1 . IN .... 2 |
# | O ......... O | Sorry all the dots are just to
# | 1 GATE-OUT. 2 | make the picture look correct.
# ----------------
#%PRE%
# You must also connect the fast shutter cable to the Vct6 plug 1 in the lower
# rectangle at the bottom of the card.
#%PRE%
# ----------------
# | ............. |
# | ... O. 1 .. |< ---- fast shutter
# | ............. |
# | ... O. 2 .. |
# | ............. |
# | ... O. 3 .. |
# | ............. |
# | ... O. 4 .. |
# | ............. |
# ----------------
#%PRE%
# Dark current substraction is done online automatically. Every time you
# change the exposure time a new dark current image will be read out to be
# substracted from the image data.
#%END%
#%SETUP%
#
#
# Please make sure the input 1 of the VCT6 card are configured for TTL level
# (low = 0V, high = +5V).
#%BR%
# Open the file ~dserver/dbase/res/ID30/id301Vct6_1.res und check whether
# the following lines are present:
#%PRE%
#
# ID30/vct301_1/1/GateLevel: TTL
# id30/vct301_1/1/InputLevel: TTL
#
#%PRE%
# Restart the "Vct6" device server:
#%PRE%
#
# opid30@megabar> telnet id301
# User name?: root rt
# id301[1]: procs ! grep Vct
# 85 0 0.0 128 92.00k 0 s 0.21 127:00 Vct6 <>>>nil
# id301[2]: os9kill -2 85
# id301[3]: ID301Vct6_0.startup
#
#%PRE%
# I hope a future release of the VCT6 device server will provide a device server
# call for the input level so this operation can be included in the SPEC
# macro.
#
#%LOG%
#$Revision: 1.14 $
#$Log: oscillation.mac,v $
#Revision 1.14 2012/06/05 09:33:31 witsch
#function max() was called within oscillation, but had been commented out.
#
#Using max2, as suggested from spec_utils.mac
#
#Revision 1.13 2011/09/16 07:54:39 guilloud
#* load spec_utils.mac (for max() function)
#* removed max() function
#
#Revision 1.12 2008/08/12 15:24:49 rey
#documentation changes
#
#Revision 1.10 2005/09/21 12:51:41 pepellin
#Use clscreen()
#
#Revision 1.9 2005/04/07 13:35:11 claustre
#create a oscillation_restore macro to well manage vct6 and vpap default
#configuration
#
#Revision 1.8 2004/08/27 07:17:43 claustre
#Restore use of safety-shutter options.
#Check now if the calculated speed is not smaller than the base-rate.
#Add some sleep() when polling motor position during oscillation.
#
#Revision 1.6 2004/03/03 08:32:48 claustre
#create oscillation_user_* macros to allow beamline add-on implementation.
#
#Revision 1.5 2004/02/18 16:24:50 claustre
# Hoops, a bug introduced with the last version, take_still_image macro.
#
#Revision 1.4 2004/02/18 15:42:50 claustre
#special premove and postmove cdef'ed macros for ID09 usage, fixe pb
#with prepare_mar() to initialize spec internal data about file pars.
#
#Revision 1.3 2003/01/23 16:06:10 claustre
#This is the real oscillation macros used on beamline.
#
#%END%
# Modifications history:
# 16/04/2003 LC fix a bug in oscillation_cleanup() which restored a wrong acceleration
# change cleanup rdef with cdef() on cleanup_once.
# create oscillation_premove and oscillation_postmove for cdef() usage on ID09
# 20/07/2001 HW image_par now sends a big string with lots of different
# information, the range is now
# 04/07/2001 HW add handling for the Bruker detector. It can't be used like a
# any other ccd, because it doesn't allow external trigger.
# 13/02/2001 MP+JK remove the CntInit when getting out of main menu (don't ask
# why, please!!)
# 30/01/2001 HW change the MAR IP handling to match the new device server which
# simulates a CCD type device. (Edit token : #HW30101)
# 18/06/2000 HW take out all reference to diodes.
# Reset the vct6 for it to work as time base after the
# oscillation macro
# 16/06/2000 HW cable the Vct6 so that channel 2 is the gate channel, setup of
# fourc
# 12/06/2000 HW put all access to old fast shutter to comment.
# Access to new fast shutter should be handled by macros in the
# new-fast-shutter.mac file.
# 28/04/99 MP correct if() bug after SPEC update
# 28/02/99 LF number of digits in "spindle at" message increased from 2 to 3
# 25/02/99 MP add fast shutter opening time as a parameter to oscillsetup
# 24/02/99 LF real_time added to OSCILL and beam intensity output per second
# 23/02/99 MP add value check on speed entering (enter_speed_parameter,...)
# 23/02/99 MP replace adc with VCT6 channel for beam monitoring
# 23/02/99 MP suppress putting in/out the diode at each exposure
# 24/09/98 MP add wait_ms_shutter() needed if open_ms_shutter called with arg
# 24/09/98 MP add f_open_ms_shutter() (allow calls from other macros)
# 01/09/98 MP in fsds_osc_readout, suppress fastscan controler reset
# 01/09/98 MP in take_oscillation_image, force tcp protocol
# 01/09/98 MP add oscillation_prepare_diode, _read_diode, _log_diode
# 01/09/98 MP Err: oscillation_cleanup, add tests on esrf_io()
# 01/09/98 MP Err: oscillation_cleanup, suppress access to trig ch if not CCD
# 07/07/98 MP Err: in take_oscillation_image, gate number not directly from
# OSC_SUP["gateg"] (if several VCT6 cards, channel number wrong)
# 27/06/98 MK in fsds_osc_readout, changed time-delay afer fastscan-reset
# from 2 to 4
# 26/06/98 MP in oscillation_log, add current diode intensity information
# 25/06/98 MP in oscillation_data_collection, change the end condition of for()
# 11/06/98 MP Err: in take_oscillation_image, gate number from OSC_SUP["gateg"]
# 11/06/98 MP Err: in oscillation_log, test on file_info() return value wrong
# 11/06/98 MP Add some printf("\n") and info for clearer display while scaning
# 10/06/98 MP Add "FASTSCAN" as OSCILL["detector"] possible value
# 10/06/98 MP Err: in restore_array, SPEC subsitution wrong, quote missing
# 10/06/98 MP Err: in restore_array, test on file_info() return value wrong
# 10/06/98 MP Err: in take_oscillation_image, add "local exposure"
# 10/06/98 MP Err: in oscillation_initialize "exposure_time" naming
# 10/06/98 MP Add max() function.
# for max() (at least)
need spec_utils
#%UU% %MDESC% (or "collect") a menu driven routine to take complete data sets
def oscillation 'oscillation_data_collection_menu'
#%UU% %MDESC% (same as "oscillation")
def collect 'oscillation_data_collection_menu'
#%UU% [gonio] [detector] [shutter] [vct6] [gate] [trigger] [timer] [ccd]
#%MDESC% Global setup. Define all beeamline dependent parameters. Put this line
# in your beamline's setup file.
def oscillsetup '
{
global OSC_SUP
if ($#>=10) {
OSC_SUP["gonio"]= "$1"
OSC_SUP["detec"]= "$2"
OSC_SUP["shutt"]= "$3"
OSC_SUP["vct6"] = "$4"
OSC_SUP["gateg"]= $5
OSC_SUP["trigg"]= $6
OSC_SUP["watch"]= $7
OSC_SUP["ccd_u"]= $8
# OSC_SUP["b_mon_mne"]= "$9"
OSC_SUP["b_mon_mne"]= $9
OSC_SUP["fs_open"]= $10
} else {
OSC_SUP["gonio"]= getval ("oscillation motor mnemonic",OSC_SUP ["gonio"])
OSC_SUP["detec"]= getval ("detector pos. motor mnemonic",OSC_SUP ["detec"])
OSC_SUP["shutt"]= getval ("safety shutter device name",OSC_SUP ["shutt"])
OSC_SUP["vct6"] = getval ("Vct6 device name",OSC_SUP ["vct6"])
OSC_SUP["gateg"]= getval ("Vct6 shutter gate channel",OSC_SUP ["gateg"])
OSC_SUP["trigg"]= getval ("Vct6 camera gate channel",OSC_SUP ["trigg"])
OSC_SUP["watch"]= getval ("Vct6 timer channel",OSC_SUP ["watch"])
OSC_SUP["ccd_u"]= getval ("CCD Unit configured",OSC_SUP ["ccd_u"])
OSC_SUP["b_mon_mne"] = getval ("Vct6 beam monitor counter", \
OSC_SUP ["b_mon_mne"])
OSC_SUP["fs_open"]= getval ("Fast Shutter opening time (sec)", \
OSC_SUP ["fs_open"])
if(OSC_SUP["fs_open"] < 0) OSC_SUP["fs_open"]=0;
if(OSC_SUP["fs_open"] > 0.01) OSC_SUP["fs_open"]=0.01;
}
OSC_SUP["gonio_n"]= motor_num(OSC_SUP["gonio"])
OSC_SUP["detec_n"]= motor_num(OSC_SUP["detec"])
OSC_SUP["gate"]= OSC_SUP ["vct6"] "/" OSC_SUP ["gateg"]
OSC_SUP["trig"]= OSC_SUP ["vct6"] "/" OSC_SUP ["trigg"]
OSC_SUP["watc"]= OSC_SUP ["vct6"] "/" OSC_SUP ["watch"]
}'
#%IU%
#%MDESC% setup all needed parameters to acquire a full data set, such
# as angular step, rotation range, exposure time and filenames
def oscillation_data_collection_menu '
{
oscillation_initialize
local letter ; letter = ""
while (letter != "\n")
{
# modify oscillations and exposure time because of hardware constraints
check_oscillation_parameters # values taken from "OSCILL" array
# [md] = mode double-bright, [me] = mode attributes end
# [us] = underline start, [ue] = underline end
clscreen() # clear screen
tty_move (1,1,"\[md]\[us]OSCILLATION DATA COLLECTION\[me]\[ue]")
if (OSC_SUP["mar_use"]) tty_move (1,30,"\[md](MAR IP)\[me]")
if (OSC_SUP["bruker_use"]) tty_move (1,30,"\[md](Bruker)\[me]") #HW 04072001
tty_move (1,3, "\[us]\[md]F\[me]\[ue]rom:");
tty_move (20,3,OSC_DATA["start"]); tty_move (27,3,"deg")
tty_move (1,4, "\[us]\[md]T\[me]\[ue]o:"); tty_move (20,4,OSC_DATA["end"]); tty_move(27,4,"deg")
tty_move (40,3,"\[us]\[md]A\[me]\[ue]ngular Step:")
tty_move (60,3,OSC_DATA ["step"]); tty_move(66,3,"deg")
tty_move (40,4,"\[us]\[md]O\[me]\[ue]scillation Range:")
tty_move (60,4,OSCILL["range"]); tty_move(66,4,"deg")
tty_move (40,6,sprintf("(%-2d half-oscillations)",OSCILL["number"]))
tty_move (1,6, "\[us]\[md]E\[me]\[ue]xposure Time:")
tty_move (20,6,sprintf("%.2f",OSCILL["exposure_time"])); tty_move(27,6,"s")
tty_move (1,8, "\[us]\[md]B\[me]\[ue]asename:");
tty_move (20,8,OSC_DATA["basename"] ? OSC_DATA["basename"] : "-")
tty_move (40,8,"\[us]\[md]L\[me]\[ue]og File:");
tty_move (60,8,OSC_DATA["log_file"] ? OSC_DATA["log_file"] : "-")
tty_move (1,9, "\[us]\[md]I\[me]\[ue]mage Number:")
tty_move (20,9,sprintf("%03d",OSC_DATA["image_number"]))
tty_move (40,9,"S\[us]\[md]u\[me]\[ue]ffix:")
tty_move (60,9,OSC_DATA["suffix"] ? OSC_DATA["suffix"] : "-")
tty_move (1,11,"\[us]\[md]P\[me]\[ue]ath:"); tty_move (20,11,tail(OSC_DATA["dir"],58))
if (file_info(OSC_DATA["dir"],"isdir") != 1) printf (" ?")
tty_move (1,12,"\[us]\[md]N\[me]\[ue]ote:");
tty_move (20,12,OSC_DATA["description"] ? OSC_DATA["description"] : "-")
#HW070301begin and change HW04072001
if (OSCILL["detector"] == "MAR IP" || OSCILL["detector"] == "BRUKER") {
tty_move ( 1,14, "X-ray beam \[us]\[md]w\[me]\[ue]avelength in A: ");
tty_move (31,14, OSCILL["wavelength"]);
tty_move (40,14, "Detector-to-sample distance in \[us]\[md]m\[me]\[ue]m: ");
tty_move (75,14, OSCILL["d_s_dist"]);
}
else tty_move (1,14, note = "(" data_set("info") ")" )
#HW070301end
#HW070301 if (OSCILL["detector"]!="FASTSCAN")
tty_move (1,16,sprintf ("(current orientation is %g deg)",A[OSC_SUP["gonio_n"]]))
tty_move (40,16,"(\[us]r\[ue]eset to 0)")
tty_move (1,19,"\[mr] \[md]\[us]S\[ue]\[me]\[mr]INGLE IMAGE \[me]") # [mr] = mode reverse
tty_move (20,19,"\[mr] \[md]\[us]C\[ue]\[me]\[mr]OLLECT \[me]")
tty_move (40,19,"\[md]\[us]H\[ue]\[me]ardware setup... ")
if ((OSCILL["detector"] != "") && OSCILL["detector"] != "none")
tty_move (60,19,"\[md]\[us]D\[ue]\[me]etector... ")
tty_move (1,22,"--> Type underligned letter (lower case, [Return] to quit) ");
# wait until user types hits a key
letter = input(-1); while (asc(letter) == 0) letter = input(-1) ; input(1)
tty_move (1,22,"--> \[ce]");
if (letter == "f")
OSC_DATA["start"] = getval("First oscillation starts at",OSC_DATA["start"])
if (letter == "t")
OSC_DATA["end"] = getval("Last oscillation starts at",OSC_DATA["end"])
if (letter == "a") OSC_DATA["step"] = \
getval("Advance spindle between exposures by",OSC_DATA["step"])
if (letter == "o") OSCILL["range"] = \
OSC_DATA["step"] = getval("Rotate during exposure by",OSCILL["range"])
# if (letter == "e") OSCILL["exposure_time"] = \
# getval("Exposure time [s]",OSCILL["exposure_time"])+0
if (letter == "e" || letter == "o") enter_speed_parameter;
if (letter == "b") { tty_move (20,8); edit OSC_DATA["basename"] 19 }
if (letter == "l") { tty_move (60,8); edit OSC_DATA["log_file"] 19 }
if (letter == "i") OSC_DATA["image_number"] = \
int (getval ("Number appended to image name",OSC_DATA["image_number"]))
if (letter == "u") OSC_DATA["suffix"] = \
getval("Filename ext. (- for none)",OSC_DATA["suffix"])
if (OSC_DATA["suffix"] == "-") OSC_DATA["suffix"] = ""
if (letter == "p")
{
tty_move (20,11); edit OSC_DATA["dir"] 59
# if (file_info(directory,"isdir") != 1) unix (command="mkdir -p "directory)
}
if (letter == "n") { tty_move (20,12); edit OSC_DATA["description"] 59 }
if (letter == "c") oscillation_data_collection
if (letter == "s") single_oscillation_image
if (letter == "h") oscillation_setup
if (letter == "d") oscillation_detector_setup
if (letter == "r") { chg_offset (OSC_SUP["gonio_n"],0); get_angles }
if (letter == "w") OSCILL["wavelength"] = \
getval("X-ray beam wavelength in A:", OSCILL["wavelength"])
if (letter == "m") OSCILL["d_s_dist"] = \
getval("Detector-to-sample distance in mm:", OSCILL["d_s_dist"])
}
save_oscillation_parameters
# Configure the VCT6 channel 2 back to normal. No gate use!
#MP
# esrf_io(OSC_SUP["gate"],"DevCntClear")
local vct6_init
vct6_init[0] = 0 # Mode = master
vct6_init[1] = 0 # Gate number - not used for master
vct6_init[2] = 0 # Clock - 0 = internal, 1 = external
vct6_init[3] = 0 # Clock Frequency - 100kHz if internal, not used if external
vct6_init[4] = 0 # Free run mode - no
vct6_init[5] = 0 # Start counting signal - internal
vct6_init[6] = 0 # Stop counting - internal
vct6_init[7] = 0 # Divider mode - not used (only for free-run)
vct6_init[8] = 0 # Autoclear of counting value before each count (slave only)
vct6_init[9] = 0 # Hold counting value if external start - unused (slave only)
vct6_init[10] = 0 # Precount clock source - internal
#MP
# esrf_io(OSC_SUP["gate"],"DevCntInit",vct6_init)
}'
# %IU% %MDESC% setup menu which contains hardware related parameters which
# you normally do not change during a data collation.
def oscillation_setup '
{
oscillation_initialize
local letter; letter = ""
while (letter != "\n")
{
local text
clscreen() # clear screen
tty_move (1,1,"\[md]\[us]OSCILLATION HARDWARE SETUP\[me]\[ue]")
tty_move (1,3,"\[us]\[md]D\[me]\[ue]etector:")
tty_move (20,3,text="<"OSCILL["detector"]">")
if (OSCILL["detector"] != "none")
tty_move (40,3,"\[us]\[md]S\[me]\[ue]etup...")
tty_move (1,5,"Auto \[us]\[md]o\[me]\[ue]pen safety shutter:")
tty_move (40,5,OSC_SUP["open shutter"] ? "<yes>" : "<no>")
tty_move (1,6,"Auto \[us]\[md]c\[me]\[ue]lose safety shutter:");
tty_move (40,6,OSC_SUP["close shutter"] ? "<yes>" : "<no>")
tty_move (1,22,"--> Type underligned letter (lower case, [Return] to quit) ");
# wait until user types hits a key
letter = input(-1) ; while (asc(letter) == 0) letter = input(-1) ; input(1)
tty_move (1,22,"--> \[ce]"); # clear to the end of line
if (letter == "d")
if (OSCILL["detector"]=="XRII/CCD") OSCILL["detector"]="MAR IP"
else if (OSCILL["detector"]=="MAR IP") OSCILL["detector"]="BRUKER" #HW04072001
else if (OSCILL["detector"]=="BRUKER") OSCILL["detector"]="FASTSCAN" #HW04072001
else if (OSCILL["detector"]=="FASTSCAN") OSCILL["detector"]="none"
else OSCILL["detector"]="XRII/CCD"
if (letter == "s") oscillation_detector_setup
if (letter == "o") OSC_SUP["open shutter"] = ! OSC_SUP["open shutter"]
if (letter == "c") OSC_SUP["close shutter"] = ! OSC_SUP["close shutter"]
}
}'
#%IU% (arg[,param])
#%MDESC% print inforation about the number of images in the data set
# warn if files may be overwritten
# %DL%
# %DT% arg = "filename", param = image_number
# %DD% Return full absolute path name for image data
# %DT% arg = "info"
# %DD% Return informational message string about number of images beeing collected
# and file risking to be overwritten.
# %DT% arg = "overwrite?"
# %DD% prompt the user to comfirm overwriting of files (one for all files in a
# data set). Returns 1 or 0.
# %XDL%
def data_set (arg,param) '{
local images overwriting warning text
if (arg == "filename")
{
local nnn filename
image_number = param
if (OSC_DATA["basename"] == "") return ""
nnn = sprintf("%03d",image_number)
filename = OSC_DATA["dir"]"/"OSC_DATA["basename"]"_"nnn OSC_DATA["suffix"]
return filename
}
if (arg == "info")
{
data_set_check
if (overwriting) warning = ", overwriting "overwriting" file/s"
if (overwriting >= 1000) warning = ", overwriting more than 1000 file/s"
text="Will take "images" image/s" warning
return text
}
if (arg == "overwrite?")
{
data_set_check
if (overwriting)
return yesno (warning = "OK to overwrite "overwriting" file/s",1)
else return 1
}
}'
#%MDESC% used by the "data_set()" macro
def data_set_check '
{
local start step end image_number orientation filename
start = OSC_DATA ["start"]; step = OSC_DATA ["step"]; end = OSC_DATA ["end"]
image_number = OSC_DATA["image_number"]
if (step != 0)
{
for (orientation=start; (orientation-end)/step < 0; orientation += step)
{
images++
filename = data_file(image_number)
if ((filename != "") && file_info (filename,"-e") == 0) overwriting++
image_number++
if (images > 1000) break
}
}
if (step == 0)
{
images = 1
filename = data_file(image_number)
if ((filename != "") && file_info (filename,"-e") == 0) overwriting=1
}
}'
#%IU% (image_number) %MDESC% Return full absolute path name for image
def data_file (image_number) '{
local nnn filename
if (OSC_DATA["basename"] == "") return ""
nnn = sprintf("%03d",image_number)
filename = OSC_DATA["dir"]"/"OSC_DATA["basename"]"_"nnn OSC_DATA["suffix"]
return filename
}'
#%IU%
#%MDESC% Set the user configurable parameters to some resoable defaults
# when the "oscillation" macro is invoked the first time.
# The parameters wont be reinitialized in subsequent calls Since they
# are stored on global variables, which keep their values from one SPEC
# session to the next.
# Only if the parameters seem to be cleared, because of a SPEC restart with
# the -f (from fresh) option or a crash, they will be reloaded from the ASCII
# file, where the "save_oscillation_parameters" macro saves them.
def oscillation_initialize '
{
global OSCILL[] OSC_DATA[]
# if global vars are cleared, try to reload values from ASCII files
restore_array OSCILL
restore_array OSC_DATA
# set some resonable default values
if (! ("exposure_time" in OSCILL)) OSCILL["exposure_time"] = 20
if (! ("range" in OSCILL)) OSCILL["range"] = 5
if (! ("number" in OSCILL)) OSCILL["number"] = 1
if (! ("detector" in OSCILL)) OSCILL["detector"] = "XRII/CCD"
if (! ("start" in OSC_DATA)) OSC_DATA["start"] = "0"
if (! ("end" in OSC_DATA)) OSC_DATA["end"] = "180"
if (! ("step" in OSC_DATA)) OSC_DATA["step"] = "5"
if (! ("dir" in OSC_DATA)) OSC_DATA["dir"] = CWD
}'
#%IU%
#%MDESC% Save all user configurable parameters into an ASCII file so they
# are not not lost when SPEC is restarted with the -f (from fresh) option,
# clearing all global variables. The parameters are hold in 2 global arrays,
# OSCILL and OSC_DATA.
def save_oscillation_parameters '
{
save_array OSCILL
save_array OSC_DATA
}'
#%IU%
#%MDESC% Acquire a complete data set. parameters such as angular step, rotation
# range, exposure time and filenames are passed in the global array
# "OSCILL"
def oscillation_data_collection '
{
if (data_set("overwrite?"))
{
global cancelled ; cancelled = 0
local start step end orientation
start = OSC_DATA["start"]; end = OSC_DATA["end"]; step = OSC_DATA["step"]
oscillation_user_precollect
if (step != 0)
for (orientation = start; (orientation-end)/step < 0; orientation += step)
{
rotate_spindle_to orientation
take_oscillation_image
if (cancelled) break
}
if (step == 0)
{
orientation = OSC_DATA["start"]
rotate_spindle_to orientation
take_oscillation_image
}
oscillation_user_postcollect
}
}'
#%IU% %MDESC% useful for sample screening and optimizing the exposure time
def single_oscillation_image '
{
local orientation; orientation = A[OSC_SUP["gonio_n"]]
local start; start = OSC_DATA["start"]
cancelled = 0
oscillation_user_presingle
take_oscillation_image
oscillation_user_postsingle
}'
#%IU% %MDESC% chain your code to this macro to prepare the image taken
cdef ("oscillation_user_preimage")
#%IU% %MDESC% chain your code to this macro to finish the image taken
cdef ("oscillation_user_postimage")
#%IU% %MDESC% chain your code to this macro to prepare the collection
cdef ("oscillation_user_presingle")
#%IU% %MDESC% chain your code to this macro to finish the collection
cdef ("oscillation_user_postsingle")
#%IU% %MDESC% chain your code to this macro to prepare the collection
cdef ("oscillation_user_precollect")
#%IU% %MDESC% chain your code to this macro to finish the collection
cdef ("oscillation_user_postcollect")
#%IU% %MDESC% chain your code to this macro to restore in case of Ctrl-C
cdef ("oscillation_user_cleanup")
## Here example of cdef() used on ID09 (new-fast-shutter.mac file)
#cdef("oscillation_user_preimage","close_fast_shutter 36000\n","_id9_oscill")
#cdef("oscillation_user_postimage","release_fast_shutter\n","_id9_oscill")
#cdef("oscillation_user_presingle","rotate_spindle_to OSC_DATA[\"start\"];disk down\n","_id9_oscill")
#cdef("oscillation_user_postsingle","disk up;rotate_spindle_to 0\n","_id9_oscill")
#cdef("oscillation_user_precollect","disk down\n","_id9_oscill")
#cdef("oscillation_user_postcollect","disk up;rotate_spindle_to 0\n","_id9_oscill")
#cdef("oscillation_user_cleanup","disk up;release_fast_shutter\n","_id9_oscill")
#%IU% <new_position>
#%MDESC% manages absolute motions of the goniometer axis.
def rotate_spindle_to '{
local spindle ; spindle = OSC_SUP["gonio_n"]
waitmove ; get_angles # needed - other motors could be out of sync
A[spindle]=$1
move_em
while (chk_move)
{
get_angles # update motor position array A[]
notice (sprintf("Rotating spindle... %7.2f deg (ESC to cancel)",A[spindle]))
ESC = "\33"; if (input(-1) == ESC) {stop(); break}
}
sleep(.05)
get_angles # update motor position array A[]
notice (sprintf ("spindle at %7.3f deg",A[spindle]))
printf("\n")
input(1) # back to normal input mode
}'
#%IU% <amplitude>
#%MDESC% manages relative motions of the goniometer axis.
def rotate_spindle_by '{
waitmove; get_angles
A[OSC_SUP["gonio_n"]]+=$1
move_em
move_poll
get_angles # update motor position array A[]
notice (sprintf ("spindle at %7.3f deg",A[OSC_SUP["gonio_n"]]))
printf("\n")
}'
#%IU% filename angle
#%MDESC% Append data collection information to the log file as defined by
# OSC_DATA ["log_file"].
def oscillation_log (filename,orientation) '{
if (OSC_DATA ["log_file"] != "")
{
local real_time
log_file = OSC_DATA ["log_file"]
file_exists = file_info (log_file,"-e") == 0 ? 0 : 1
if (! file_exists)
{
# Start with a short descriptions of the column format as comment line
columns = "date time filename orient. osc. dist.[mm]"
if (OSCILL["detector"]=="FASTSCAN")
columns = "date|time|filename|start_angle|osc_range|expo_time|intensity_average";
fprintf (log_file,"#%s\n",columns)
for (i in LOGGED) LOGGED[i]=0 # nothing is logged yet
}
# Add description, exposure time and info about dark current subtraction
# as comment lines (not always - only if changed)
global LOGGED
if (OSC_DATA["description"] != LOGGED["description"])
fprintf (log_file,"# %s\n",OSC_DATA["description"])
LOGGED["description"] = OSC_DATA["description"]
if (OSCILL["exposure_time"] != LOGGED["exposure_time"])
fprintf (log_file,"# Calculated exposure time %g s\n",OSCILL["exposure_time"])
LOGGED["exposure_time"] = OSCILL["exposure_time"]
real_time = esrf_io (OSC_SUP["watc"],"DevCntRead") / 100000
OSCILL["real_time"] = real_time
# image information needed for the data processing
split (date(),word); month=word[1]; day=word[2]; dtime=word[3]; year=word[4]
fprintf (log_file,"%02d-%s-%02d %s ",day,month,year%100,dtime)
fprintf (log_file,"%-25s %4g %5g ",filename,orientation,OSCILL["range"])
if (OSCILL["detector"]!="FASTSCAN")
{
# distance
if (OSC_SUP["detec_n"]!=-1) fprintf (log_file,"%g",A[OSC_SUP["detec_n"]])
}
fprintf (log_file,"%7.3f ",real_time)
# comment out following line: HW , 19.6.2000
# oscillation_log_diode(log_file)
fprintf (log_file,"\n")
close (log_file)
}
}'
#%IU%
#%MDESC% Display progres info at bottom of screen
# This macro allows subroutine to display messages keeping a clear screen layout
def data_collection_info '
tty_move(1,20); tty_cntl("ce") # clear to the end of line
print '
#%IU% open_safety_shutter
#%MDESC% replaces "shopen" macro, which causes problems because it messes up the sceen
# by unnessary messages and does not wait until the shutter is actually open.
def open_safety_shutter '
{
if ((OSC_SUP["shutt"] != "") && OSC_SUP["open shutter"])
{
ESRF_ERR = -1 # suppress error message in case "esrf_io" signals a failure
esrf_io (OSC_SUP["shutt"],"DevOpen")
if (ESRF_ERR > 0) # use error code to detect failure instead
{
info "Safety shutter won\'t open. Did you interlock the experiments hutch?"
} else {
# This shutter is slow, wait until it has has finished opening (~1.5 s)
state = esrf_io (OSC_SUP["shutt"],"DevState")
while (state != 4) # 1 = Fault, 2 = Moving, 3 == Closed; 4 == Open
{
notice ("Opening safety shutter... (ESC to cancel)")
if (input(-1) == "\33") { cancelled=1; break }
state = esrf_io (OSC_SUP["shutt"],"DevState")
}
if (cancelled) close_safety_shutter
}
}
}'
def close_safety_shutter '
{
if ((OSC_SUP["shutt"] != "") && OSC_SUP["close shutter"])
{
notice ("Closing safety shutter...");
esrf_io (OSC_SUP["shutt"],"DevClose")
# Wait until the shutter has finished closing
state = esrf_io (OSC_SUP["shutt"],"DevState")
# 1 = Fault, 2 = Moving, 3 == Closed; 4 == Open
while (state == 2) state = esrf_io (OSC_SUP["shutt"],"DevState")
}
}'
#%IU% [range [exposure-time [no-of-oscillations] ] ]
#%MDESC% Takes a single oscillation image oscillating by <range> degrees
# in positive direction from the current position. <exposure-time> is the
# total accumulated exposure time the X-ray shutter is openend. The actual
# time used may be longer.
def take_oscillation_image '
{
local exposure
local i
global ID30_BMON_AVG
if ($# >= 1) OSCILL["range"] = $1 # oscillation in deg range starting from current position
if ($# >= 2) OSCILL["exposure_time"] = $2 # accumulated exposure time over all oscillations
if ($# >= 3) OSCILL["number"] = $3 # number of half-oscillations
if (OSCILL["range"] == 0) take_still_image # needs different synchronization
if (OSCILL["range"] != 0)
{
check_oscillation_parameters # adjust the above 3 values to hardware limits
global cancelled ; cancelled = 0
range = OSCILL["range"]
exposure_time = OSCILL["exposure_time"]
nr_oscillations = OSCILL["number"]
# Establish a recovery procedure when canceled by Control-C
OSCILL ["starting_position"] = A[OSC_SUP["gonio_n"]]
OSCILL ["normal_base_rate"] = motor_par (OSC_SUP["gonio_n"],"base_rate")
OSCILL ["normal_steady_state_rate"] = motor_par (OSC_SUP["gonio_n"],"velocity")
OSCILL ["normal_acceleration_time"] = motor_par (OSC_SUP["gonio_n"],"acceleration")/1000
cdef("cleanup_once", "oscillation_cleanup();","_oscillation_")
# Calculate the number of motor steps for one half-oscillation
steps_per_degree = motor_par (OSC_SUP["gonio_n"],"step_size")
steps = round (fabs (range) * steps_per_degree)
info "steps per oscillation = "steps
# Calculate the steady-state speed needed of the rotation motor [steps/s]
speed = steps*nr_oscillations/exposure_time
# ***************************** bug to be fixed here
if (speed > 6000) speed = 6000;
info "required speed = "speed" steps/s"
# In case the steady state stepping speed would by slower than the preconfi-
# gured motor base rate, this parameter has to be changed, too
base_rate = motor_par (OSC_SUP["gonio_n"],"base_rate")
info "normal base rate = "base_rate" steps/s"
if (base_rate >= speed)
{
# The base rate may be rounded up or down by the CY550 controller on the
# ESRF VPAP card. That would no problem I the value would be rounded down.
# But in half of the cases it is rounded up, and then the motor will allways
# run at the base rate and never reach the steady state rate.
# A safe way to program the base rate would be to read it back from the
# hardware and if it is higher than the base rate try a lower value.
# Unfortunately SPEC might crash in the "setpars" routine if both values
# happened to be equal or are rounded to an equal value (OK for the hardware).
# Maybe there is division 0/0 when recalulating the ramp-up time?
# This is the beginning of a list of possible base values taken form CY550
# handbook so I can be sure the there will be no rounding in hardware
values = "15 100 200 300 400 500 640 750 843 926 1002 1072 1138 1199"
split (values,base_rates)
for (i in base_rates)
if (base_rates[i] <= speed) { base_rate = base_rates[i]; break }
}
# SPEC tries to keep constant the ramp-up time so it modifies the acceleration
# every time the base or steady state rate is changed. I will restore
# the normal preconfigured acceleration
normal_speed = motor_par (OSC_SUP["gonio_n"],"velocity")
normal_base_rate = motor_par (OSC_SUP["gonio_n"],"base_rate")
normal_acceleration_time = motor_par (OSC_SUP["gonio_n"],"acceleration") / 1000
if (normal_acceleration_time > 0)
acceleration = (normal_speed - normal_base_rate) / normal_acceleration_time
else acceleration = 0
if (acceleration > 0) acceleration_time = (speed - base_rate) / acceleration
else acceleration_time = 0
info "normal acceleration time = "normal_acceleration_time" s"
info "acceleration = "acceleration" steps/s/s"
# set the new parameters in hardware
info "setting steady state rate to "speed" steps/s..."
motor_par (OSC_SUP["gonio_n"],"velocity",round(speed)) # integer argument
info "setting base rate to "base_rate" steps/s..."
motor_par (OSC_SUP["gonio_n"],"base_rate",base_rate)
info "setting acceleration time to "acceleration_time*1000" ms..."
motor_par (OSC_SUP["gonio_n"],"acceleration",acceleration_time*1000)
# check the values actually set in hardware
info "checking programmed stepper motor parameters..."
# force SPEC to update the changes now rather than at the next move command
# and read the parameters back
motor_par (OSC_SUP["gonio_n"],"setpars") # this command also reads back the rounded values
base_rate = motor_par (OSC_SUP["gonio_n"],"base_rate")
speed = motor_par (OSC_SUP["gonio_n"],"velocity")
acceleration_time = motor_par (OSC_SUP["gonio_n"],"acceleration") / 1000
info "base rate is now "base_rate" steps/s"
info "steady state is now "speed" steps/s"
info "acceleration time is now "acceleration_time*1000" ms"
# Calculate the number of steps needed for the acceleration ramp
if (acceleration_time > 0 && speed > base_rate)
{
acceleration = (speed - base_rate) / acceleration_time
info "acceleration = "acceleration" steps/s/s"
acceleration_time = (speed - base_rate) / acceleration
acceleration_steps = (base_rate+speed)/2 * acceleration_time
acceleration_steps = int (acceleration_steps + 1)
}
else
{
acceleration_steps = 1
# need at least on step to start the counter which won"t be counted
acceleration_time = 0
}
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
acceleration_steps *= 2
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
info "acceleration time = "acceleration_time" s"
info "acceleration steps = "acceleration_steps
# Need the acceleration range in degrees for the "mv" command
acceleration_range = acceleration_steps / steps_per_degree
if (range<0) acceleration_range = -acceleration_range
# both rotations must have the same direction
info "acceleration range = "acceleration_range" deg"
# estimate the exposure time needed for the CCD detector
detector_exposure_time = exposure_time + 2*acceleration_time*nr_oscillations
buffer_time = 0.7 + (nr_oscillations-1)*0.6; # 0.5 and 0.4 are the real values
detector_exposure_time += buffer_time;
info "estimated detector exposure time = "detector_exposure_time" s"
OSCILL["detector_exposure_time"] = detector_exposure_time
# Configure the VCT6 channel 2 as gate gererator opening and closing
# the X-ray shutter by counting stepper motor pulses
esrf_io (OSC_SUP["gate"],"tcp")
esrf_io (OSC_SUP["gate"],"DevCntClear") # must come before "DevCntInit"
vct6_init[0] = 0 # Master
vct6_init[1] = 0 # Gate number - not used
vct6_init[2] = 1 # Clock source - external
vct6_init[3] = 0 # Clock Frequency - not used
vct6_init[4] = 0 # Free run mode - no
vct6_init[5] = 1 # Start counting signal - external
vct6_init[6] = 0 # Stop counting - internal
vct6_init[7] = 0 # Divider mode - not used
vct6_init[8] = 0 # Autoclear of counting value - disabled
vct6_init[9] = 0 # Hold counting value if external start - disabled
vct6_init[10] = 1 # Precount clock source - external
esrf_io (OSC_SUP["gate"],"DevCntInit",vct6_init)
# Use the number of steps the motor needs to come up to speed as a pretrigger
# count on "Start-In 1" before setting the "Gate-Out 1" to positive and
# starting to count the motor steps on "In 1"
info "VCT6 precount edge transactions = "acceleration_steps-1
esrf_io (OSC_SUP["gate"],"DevCntEdgeSet",acceleration_steps-1)
# set the length of the "Gate-Out 1" pulse in number motor pulses
# coming in at "In 1"
info "VCT6 preset value = "steps" "sprintf("(%.15f)",steps)
esrf_io (OSC_SUP["gate"],"DevCntPresetValue",steps)
# get the physical channel number from the logical one (they are different
# if the device server handles more than one VCT6 card and if we are not
# working on the first one)
local gate_info[]
esrf_io (OSC_SUP["gate"],"DevCntState",gate_info)
# Configure the VCT6 channel 3 as a watchdog for the X-ray shutter.
# (Channel 2 is reserved for the extral synchronizaton of the CCD camera)
# It will be counted up by a 100kHz internal clock as long there is a positive
# TTL signal on 1 set by VCT6 counter 1
esrf_io (OSC_SUP["watc"],"tcp")
esrf_io (OSC_SUP["watc"],"DevCntClear")
vct6_init[0] = 1 # Slave
vct6_init[1] = gate_info[7] # Gate number to watch (if slave)
vct6_init[2] = 0 # Clock - internal
vct6_init[3] = 0 # Internal clock frequency - 100 kHz
vct6_init[4] = 0 # Free run mode - no
vct6_init[5] = 0 # Start counting signal - internal
vct6_init[6] = 0 # Stop counting - internal
vct6_init[7] = 0 # Divider mode (when free run) - disabled
vct6_init[8] = 1 # Autoclear of counting value before each count - enabled
vct6_init[9] = 0 # Hold counting value if external start - disabled
vct6_init[10] = 0 # Precount clock source - internal
esrf_io (OSC_SUP["watc"],"DevCntInit",vct6_init)
esrf_io (OSC_SUP["watc"],"DevCntStart")
## has to be chained to oscillation_user_preimage
## see the ID09/ID30 cdef() examples
## close fast shutter ( remember it is inactive open)
#close_fast_shutter 36000
## ce
#shopen
oscillation_user_preimage
open_safety_shutter
# remember the current spindle orientation
get_angles ; orientation = A[OSC_SUP["gonio_n"]]
rotate_spindle_by -acceleration_range
oscillation_prepare_detector
# comment out: hw, 19.6.2000
# oscillation_prepare_diode
for (oscillation_nr = 1; oscillation_nr <= nr_oscillations; oscillation_nr++)
{
# Arm the VCT6 for external start - does not actually start counting
esrf_io (OSC_SUP["gate"],"DevCntStart")
if (oscillation_nr % 2 == 1) direction = 1; else direction = -1
rotation_angle = direction*(acceleration_range+range+acceleration_range)
spindle = OSC_SUP["gonio_n"]
get_angles # update motor position array A[]
A[spindle] += rotation_angle
info "starting rotation of "rotation_angle" deg..."
move_em # Start the rotation, don"t wait to complete
# The VCT6 will open the X-ray shutter after the first
# acceleration range and close again before the second acceleration range
while (chk_move) # during exposure display progress
{
# Get X-ray shutter opening time as recorded by a 100 kHz clock by the
# for debugging VCT6 channel 3 watching the "Gate-Out 1"
counts = esrf_io (OSC_SUP["gate"],"DevCntRead")
opening_time = esrf_io (OSC_SUP["watc"],"DevCntRead") / 100000
get_angles # update A[] motor position array
rotation = sprintf ("spindle at %6.2f deg, ",A[OSC_SUP["gonio_n"]])
exposure = sprintf ("exposure %6.3f s ",opening_time)
notice (text=rotation exposure "(ESC to cancel)")
sleep(0.05)
ESC = "\33"; if (input(-1) == ESC) {cancelled=1; stop(); break}
}
# Force the X-ray shutter closed
# (in case of missing counts the VCT6 gate out 1 would be still at high)
if (esrf_io (OSC_SUP["gate"],"DevState")!=2)
esrf_io (OSC_SUP["gate"],"DevCntStop")
# comment out: HW, 19.6.2000
# oscillation_read_diode
opening_time = esrf_io (OSC_SUP["watc"],"DevCntRead") / 100000
get_angles # update A[] motor position array
rotation = sprintf ("spindle at %7.3f deg, ",A[OSC_SUP["gonio_n"]])
exposure = sprintf ("exposure %6.3f s for %7.3f deg, ",opening_time,range)
notice (text=rotation exposure)
printf("\n")
# for debugging check motor counts recorded by VCT6/1 and opening time by VCT6/3
counts = esrf_io (OSC_SUP["gate"],"DevCntRead")
info "counted "counts" of "steps" motor steps"
if (cancelled) break
}
oscillation_nr--
#HW070301begin and changed HW040701
if (OSCILL["detector"] == "MAR IP" || OSCILL["detector"] == "BRUKER") {
MM_mar_imagepar(CCD_U, orientation, orientation + OSC_DATA["step"], \
OSCILL["exposure_time"], range, oscillation_nr, OSCILL["d_s_dist"] / 10, OSCILL["wavelength"])
}
#HW070301end
## has to be chained to oscillation_user_postimage
## see the ID09/ID30 cdef() examples
## reopen fast shutter
#release_fast_shutter
close_safety_shutter
oscillation_user_postimage
oscillation_readout_detector
# Restore the parameters in the stepper motor controller as they where before
oscillation_restore()
if (! cancelled)
{
filename = data_set ("filename",OSC_DATA["image_number"])
if (filename != "") save_oscillation_image (filename);
oscillation_log (filename,orientation)
if (filename != "") OSC_DATA["image_number"]++
}
}
}'
#%IU% %MDESC% Used instead of "take_oscillation_image" when the oscillation
# range is set to 0. If the spindle axis is not rotated the synchronziation of
# the X-ray shutter must be done in a different way.
def take_still_image '
{
open_safety_shutter # if not already open
# In case of emergency stop by Control-C close the X-ray shutter
cdef("cleanup_once","esrf_io (OSC_SUP[\"gate\"],\"DevCntStop\");", "_oscillation_")
oscillation_prepare_detector
# configure the VCT6
esrf_io(OSC_SUP["gate"],"DevCntClear")
local vct6_init
vct6_init[0] = 0 # Mode = master
vct6_init[1] = 0 # Gate number - not used for master
vct6_init[2] = 0 # Clock - 0 = internal, 1 = external
vct6_init[3] = 0 # Clock Frequency - 100kHz if internal, not used if external
vct6_init[4] = 0 # Free run mode - no
vct6_init[5] = 0 # Start counting signal - internal
vct6_init[6] = 0 # Stop counting - internal
vct6_init[7] = 0 # Divider mode - not used (only for free-run)
vct6_init[8] = 0 # Autoclear of counting value before each count (slave only)
vct6_init[9] = 0 # Hold counting value if external start - unused (slave only)
vct6_init[10] = 0 # Precount clock source - internal
esrf_io(OSC_SUP["gate"],"DevCntInit",vct6_init)
esrf_io(OSC_SUP["gate"],"DevCntPresetValue",OSCILL["exposure_time"]*100000)
esrf_io(OSC_SUP["gate"],"DevCntStart")
# wait for the exposure to finish - show progres info
local busy opening_time
busy = esrf_io (OSC_SUP["gate"],"DevCntStatus") & 0x800
while (busy && ! cancelled)
{
opening_time = esrf_io (OSC_SUP["gate"],"DevCntRead") / 100000
notice (sprintf ("Exposure %4.1f s (ESC to cancel)",opening_time))
# Give the user a chance to cancel by hitting [Escape]
ESC = "\33" ; if (input(-1) == ESC) { cancelled=1; break }
busy = esrf_io (OSC_SUP["gate"],"DevCntStatus") & 0x800
sleep(.05)
}
if (cancelled) oscillation_restore()
opening_time = esrf_io (OSC_SUP["gate"],"DevCntRead") / 100000
info "X-ray shutter opening time was "opening_time" s"
oscillation_readout_detector
oscillation_restore()
if (! cancelled)
{
local filename
filename = data_set ("filename",OSC_DATA["image_number"]++)
if (filename != "") save_oscillation_image (filename)
oscillation_log (filename,orientation)
}
}'
#%IU%
#%MDESC% There are hardware constraints which limit the allowed combinations
# of oscillation range, exposure time and number of oscillations. This routine
# will try to find the closest possible set of parameters to a given set
# first modifying the number of oscillations if the rotation speed is too low or
# too high, then exposure time, but leaving constant the rotation angle.
# The minimum stepping speed of the gonio-rotation is 15 steps/s ,as for all
# stepper motors controlled by an ESRF VPAP card. In principle there is no minimum
# rate for a stepping motor, but unfortunatly the value of 15 Hz is hard coded
# in the CY550 microcontroller the VPAP is based on. The maximum stpping is
# taken as the one preconfigured by SPEC at startup. You may increase it
# using "config" if you feel confident about this.
# The parameters oscillation range, exposure time and number of oscillations
# are passed and returned in a global array called "OSCILL".
def check_oscillation_parameters '
{
if (OSCILL["range"] && OSCILL["number"])
{
local range exposure_time actual_exposure_time speed actual_speed nr_oscillations
if( OSCILL["exposure_time"]+0 <= 0) OSCILL["exposure_time"] = 0.01
range = OSCILL["range"]
exposure_time = OSCILL["exposure_time"]
nr_oscillations = 1 # Reset the number of oscillations to 1.
# save the normal motor parameters to be restored at the end of this macro
normal_base_rate = motor_par (OSC_SUP["gonio_n"],"base_rate")
normal_speed = motor_par (OSC_SUP["gonio_n"],"velocity")
normal_acceleration_time = motor_par (OSC_SUP["gonio_n"],"acceleration")/1000
# Calculate the number of motor steps for one half-oscillation
steps_per_degree = motor_par (OSC_SUP["gonio_n"],"step_size")
info "motor needs "steps_per_degree" steps/deg"
steps = int (fabs (range) * steps_per_degree)
info "steps per oscillation = "steps
# Strange feature of the VCT6: if external start is used the count number
# must be not be even, otherwise a miscounting in the range -1 to -127 occurres
if (steps % 2 == 0) steps++
info "steps per oscillation set to "steps" (VCT6 odd feature)"
range = (range > 0 ? 1 : -1) * steps / steps_per_degree
info "oscillation range set to "range" deg"
# Calculate the stady-state speed needed of the rotation motor [steps/s]
speed = steps*nr_oscillations/exposure_time
info "required speed = "speed" steps/s"
# maybe the number of oscillations has to be reduced because the motor can-
# not run fast enough, I presume the maximum speed is the one set by "config"
if (speed > normal_speed) {
speed = normal_speed
info "speed set to "speed" steps/s (must not exceed "normal_speed" steps/s)"
}
if (speed <= normal_base_rate) {
speed = normal_base_rate
info "speed set to "speed" steps/s (must not be lower than "normal_base_rate" steps/s)"
}
# stepping rate might be quantized in hardware, try out and read back
# compare with what is feasible due to hardware constraints
motor_par (OSC_SUP["gonio_n"],"velocity",speed)
motor_par (OSC_SUP["gonio_n"],"base_rate",speed)
motor_par (OSC_SUP["gonio_n"],"get_pars")
actual_speed = motor_par (OSC_SUP["gonio_n"],"velocity")
info "actual speed is "actual_speed" steps/s (hardware constraints)"
actual_exposure_time = nr_oscillations * steps / actual_speed
info "exposure time set to "actual_exposure_time" s (number of oscillations changed)"
#if the actual exposure time defined by hardware is differing by more
#than 5% of what was asked by the user, ramp up the number of oscillation
#until this condition is matched.
while ( fabs(exposure_time-actual_exposure_time) > max2(0.05,0.05*exposure_time)) {
speed = steps*nr_oscillations/exposure_time
if (speed <= normal_base_rate) {
speed = normal_base_rate
info "speed set to "speed" steps/s (must not be lower than "normal_base_rate" steps/s)"
}
motor_par (OSC_SUP["gonio_n"],"velocity",speed)
motor_par (OSC_SUP["gonio_n"],"base_rate",speed)
motor_par (OSC_SUP["gonio_n"],"get_pars")
actual_speed = motor_par (OSC_SUP["gonio_n"],"velocity")
info "actual speed is "actual_speed" steps/s (hardware constraints)"
actual_exposure_time = nr_oscillations * steps / actual_speed
info "exposure time set to "actual_exposure_time" s (number of oscillations changed)"
nr_oscillations++
}
# the chosen exposure time is the actual_exposure_time last chosen by
# hardware
exposure_time = actual_exposure_time
# Fill the global variables
OSCILL["range"] = range
OSCILL["exposure_time"] = exposure_time
OSCILL["number"] = nr_oscillations
# Restore the parameters in the stepper motor controller as they where before
motor_par (OSC_SUP["gonio_n"],"velocity",normal_speed)
motor_par (OSC_SUP["gonio_n"],"base_rate",normal_base_rate)
motor_par (OSC_SUP["gonio_n"],"acceleration",normal_acceleration_time*1000)
}
angular_step = OSC_DATA["step"] ; start=OSC_DATA["start"] ; end=OSC_DATA["end"]
steps_per_degree = motor_par (OSC_SUP["gonio_n"],"step_size")
steps = int (fabs(angular_step) * steps_per_degree)
info "angular step "steps" motor steps"
if (steps == 0) steps = 1
info "angular step "steps" motor steps (must no be zero)"
angular_step = steps / steps_per_degree * (angular_step > 0 ? 1 : -1)
info "angular step set to "angular_step" deg (motor step size)"
if (sign(angular_step) != sign(end-start)) angular_step *= -1
info "angular step set to "angular_step" deg (must lead from start to end)"
OSC_DATA["step"] = angular_step
}'
#%IU%
#%MDESC% This Macro is executed when an oscillation image is cancelled
# by Control-C. It closes the ms shutter, sets the original values for the
# speed of the rotation motor gonio and rotates it back to the starting
# position.
def oscillation_cleanup() '{
close_safety_shutter
oscillation_user_cleanup
oscillation_restore()
}'
#%IU%
#%MDESC% restore default settings of the VCT6 and of the spindle motor
def oscillation_restore () '{
# Close the X-ray shutter controlled by the VCT6 gate 1 output
info "closing shutter..."
ESRF_ERR=-1
if (esrf_io (OSC_SUP["gate"],"DevState")!=2)
esrf_io (OSC_SUP["gate"],"DevCntStop")
if(ESRF_ERR) {
printf("Error accessing %s\n",OSC_SUP["gate"])
esrf_io(OSC_SUP["gate"],"DevCntClear")
}
# re-configure the VCT6 like a counter/timer [0,2,0,1,0,0,0]
local vct6_init
vct6_init[0] = 0 # Mode = master
vct6_init[1] = 2 # Gate number - not used for master
vct6_init[2] = 0 # Clock - 0 = internal, 1 = external
vct6_init[3] = 1 # Clock Frequency - 100kHz if internal, not used if external
vct6_init[4] = 0 # Free run mode - no
vct6_init[5] = 0 # Start counting signal - internal
vct6_init[6] = 0 # Stop counting - internal
vct6_init[7] = 0 # Divider mode - not used (only for free-run)
vct6_init[8] = 0 # Autoclear of counting value before each count (slave only)
vct6_init[9] = 0 # Hold counting value if external start - unused (slave only)
vct6_init[10] = 0 # Precount clock source - internal
ESRF_ERR=-1
esrf_io(OSC_SUP["gate"],"DevCntInit",vct6_init)
if (ESRF_ERR) {
printf("Error accessing %s\n",OSC_SUP["gate"])
}
# End the CCD exposure by resetting the gate output 2 to 0
# of the VCT6 to 0
if (OSCILL["detector"]=="XRII/CCD") {
ESRF_ERR=-1
if (esrf_io (OSC_SUP["trig"],"DevState")!=2)
esrf_io(OSC_SUP["trig"],"DevCntStop")
if(ESRF_ERR) {
printf("Error accessing %s\n",OSC_SUP["trig"])
}
}
# Restore the normal parameters in the stepper motor controller
# i.e. base rate = 500 steps/s and steady state rate = 1000 steps/s for gonio
info "resetting motor parameters..."
motor_par (OSC_SUP["gonio_n"],"velocity",OSCILL ["normal_steady_state_rate"])
motor_par (OSC_SUP["gonio_n"],"base_rate",OSCILL ["normal_base_rate"])
motor_par (OSC_SUP["gonio_n"],"acceleration",OSCILL["normal_acceleration_time"]*1000)
}'
# Detector independent macros
# %MDESC% Detector independent setup menu. You can add your detector's
# "setup" macro here.
def oscillation_detector_setup '
{
if (OSCILL["detector"] == "XRII/CCD") { xrii_setup }
if (OSCILL["detector"] == "MAR IP") { oscillation_mar_setup() }
if (OSCILL["detector"] == "BRUKER") { oscillation_bruker_setup() } #HW040701
if (OSCILL["detector"] == "FASTSCAN") { fsds_osc_setup() }
}'
# %MDESC% Detector independent macro called once before the exposure starts
# You can add your detector's "prepare" macro here. You should use a function
# macro so in case it included macros from other file which are not normally
# loaded
def oscillation_prepare_detector '
{
if (OSCILL["detector"] == "XRII/CCD") { prepare_ccd() }
if (OSCILL["detector"] == "MAR IP") { prepare_mar() } # added HW 29/1/2001
if (OSCILL["detector"] == "BRUKER") { prepare_bruker() } #HW040701
if (OSCILL["detector"] == "FASTSCAN") { fsds_osc_prepare() }
}'
# %MDESC% Detector independent macro called once after the end of the exposure
# You can add your detector's "readout" macro here.
def oscillation_readout_detector '
{
if (OSCILL["detector"] == "XRII/CCD") { readout_ccd() }
if (OSCILL["detector"] == "MAR IP") { readout_mar() }
if (OSCILL["detector"] == "BRUKER") { readout_bruker() } #HW040701
if (OSCILL["detector"] == "FASTSCAN") { fsds_osc_readout()}
}'
# %MDESC% Detector independent macro called once after the end of the exposure
# You can add your detector's "save_image()" macro here.
def save_oscillation_image (filename)' {
if (OSCILL["detector"] == "XRII/CCD") { save_ccd_image (filename) }
if (OSCILL["detector"] == "FASTSCAN") { fsds_osc_save(filename)}
}'
# CCD detector related macros
#%IU% (parameter,value)
#%MDESC% General setup for CCD detector (Princeton or FRELON)
def ccd (parameter,value) '{
if (whatis("CCD",1) != "A global array.") restore_array CCD
local prompt choices choice
if (parameter == "dark current") return CCD ["dark_current"]
else if (parameter == "dark current?")
{
choices = "\[us]\[md]s\[me]\[ue]ubtract, save to \[us]\[md]f\[me]\[ue]ile, do \[us]\[md]n\[me]\[ue]othing?"
choice = key (prompt="Dark current: "choices" ")
if (choice == "s") CCD ["dark_current"] = "subtract"
if (choice == "f") CCD ["dark_current"] = "save to file"
if (choice == "n") CCD ["dark_current"] = "do nothing"
}
else if (parameter == "dark current file") return CCD ["dark_current_file"]
else if (parameter == "dark current file?") CCD ["dark_current_file"] = \
getval ("Save dark image to",CCD ["dark_current_file"])
else if (parameter == "exposure time") return CCD ["exposure_time"]
else if (parameter == "exposure time?")
CCD ["exposure_time"] = getval ("Exposure time",CCD ["exposure_time"])
else if (parameter == "?")
print "dark current, dark current file, exposure time"
else
{
print "\""parameter"\" is not a parameter for ccd() (\"?\" for a list)"
exit
}
if (index (parameter,"?") > 1) save_array CCD
}'
#%MDESC% Configure the detector and start the exposure. Works for both FRELON
# and Princeton CCD.
def prepare_ccd() '{
# Preprogramming an exposure time of 0 put both Princeton and Frelon CCD
# into external trigger mode and disables the continuos cleans of the
# Princeton CCD
image_par (0,"preset",0)
# make sure that the memory buffer to hold the image is allocated
ccd_createarray
# configure the VCT6 channel 2 to trigger the CCD camera (used by Frelon only)
vct6_init[0] = 0 # Mode = master
vct6_init[1] = 0 # Gate number - not used for master
vct6_init[2] = 0 # Clock - 0 = internal, 1 = external
vct6_init[3] = 0 # Clock Frequency - 100kHz if internal, not used if external
vct6_init[4] = 0 # Free run mode - no
vct6_init[5] = 0 # Start counting signal - internal
vct6_init[6] = 0 # Stop counting - internal
vct6_init[7] = 0 # Divider mode - not used (only for free-run)
vct6_init[8] = 0 # Autoclear of counting value before each count (slave only)
vct6_init[9] = 0 # Hold counting value if external start - unused (slave only)
vct6_init[10] = 0 # Precount clock source - internal
esrf_io(OSC_SUP["trig"],"DevCntInit",vct6_init)
esrf_io(OSC_SUP["trig"],"DevCntPresetValue",1e9) # 10000 s
ccdstart ; # start Princeton CCD - Frelon needs this as well
esrf_io(OSC_SUP["trig"],"DevCntStart") # start Frelon CCD
}'
#%MDESC% Stops the exposure and waits for the data transfer to the workstation's
# memory to finish. The image content will be in the global array image_data.
# The image is not save as a file. You have to call "save_ccd_image" to do this.
# Works for both FRELON and Princeton CCD.
def readout_ccd() '{
global CCD, cancelled
# End the CCD exposure and start the readout by resetting the gate output 2
# of the VCT6 to 0
if (esrf_io (OSC_SUP["trig"],"DevState")!=2)
esrf_io(OSC_SUP["trig"],"DevCntStop")
ccd_exposure_time = esrf_io (OSC_SUP["trig"],"DevCntRead") / 100000
info "CCD exposure time was "ccd_exposure_time" s"
CCD["exposure time"] = ccd_exposure_time
# Wait for the chip readout and transfer to the computer to finish
start=time()
while (wait (0x24) && ! cancelled)
{
# Give the user a chance to cancel by hitting any key
notice (sprintf ("Reading out CCD... %3.1f s (ESC to cancel)",time()-start))
ESC = "\33"; if (input(-1) == ESC) cancelled=1
}
if (! cancelled)
{
info "Copying image to buffer..."
ccdread
info "image done"
camera_online # Bring up image display window
}
}'
def save_ccd_image (filename) '{
correct_image
save_image (filename)
}'
#HW040701 added bruker functions
# Bruker CCD detector related code
#%MDESC% Setup menu
def oscillation_bruker_setup() '{
}'
#%MDESC% Configure the Bruker CCD detector and start the exposure.
def prepare_bruker() '{
#add the safe way of putting the filename into the server.
local exp_t dummy
_ccd_remote_file_setup CCD_U OSC_DATA["dir"] OSC_DATA["basename"] \
dummy OSC_DATA["image_number"]
#start exposure for exposure time plus how much 50ms ?
exp_t = sprintf("%f", OSCILL["exposure_time"] + 0.05)
ccdstart exp_t
}'
#%MDESC% starts the acquisition in the Bruker detector PC.
def readout_bruker() '{
waitacq2
ccdsave
}'
# MAR Image plate scanner related code
#HW300101, added a number of menu points
#%MDESC% Setup menu
def oscillation_mar_setup() '{
restore_array MAR
local letter i; letter = ""
if (MAR["glimpse"] == "") MAR["glimpse"] = "no"
if (MAR["factor"] == "") MAR["factor"] = "10"
#HW070301begin
if (MAR["ccd_u"] == "") {
for (i = 0; i < NO_CCDS; i++) {
if (index(sprintf("%s", image_par(i, "device_id")), "mar345") ) {
MAR["ccd_u"] = i; break;
}
}
}
#HW070301end
while (letter != "\n")
{
local text
clscreen() # clear screen
tty_move ( 1, 1, "\[md]\[us]MAR IMAGE PLATE SCANNER SETUP\[me]\[ue]")
tty_move ( 1, 6, "\[us]\[md]d\[me]\[ue]isplay a low resolution glimpse (thumbmail):")
tty_move (60, 6, MAR["glimpse"]?"YES":"NO")
tty_move ( 1, 7, "The glimpse should be smaller by a \[us]\[md]f\[me]\[ue]actor of :")
tty_move (60, 7, MAR["factor"])
tty_move ( 1, 9, "Set the detector f\[us]\[md]o\[me]\[ue]rmat :")
tty_move (1, 22,"--> Type underligned letter (lower case, [Return] to quit) ");
# wait until user types hits a key
letter = input(-1) ; while (asc(letter) == 0) letter = input(-1) ; input(1)
tty_move (1,22,"--> \[ce]"); # clear to the end of line
if (letter == "p") { tty_move (20,4); ccd_remote_file_setup}
if (letter == "d") { tty_move (60,6); MAR["glimpse"] = yesno("", MAR["glimpse"]) }
if (letter == "f") { tty_move (60,7); edit MAR["factor"] 59 }
if (letter == "o") {
__mode = getval("Mode [1800,1200,2400,1600,3000,2000,3450,2300]",\
image_par(MAR["ccd_u"],"mode"))
image_par(MAR["ccd_u"],"mode",__mode)
}
}
image_par(MAR["ccd_u"], "view_factor", MAR["factor"])#HW300101 set the above display factor
#HW300101 need to redo the array and stuff
ccd_createarray
if (MAR["glimpse"]) {
#HW300101 start onze with appr. arguments
camera_online MAR["ccd_u"]
} else { # or take onze away.
local pid file guicmd arrayname
file = sprintf("/tmp/disgui_%s_%s.pid", USER, SPEC)
hwcommand = sprintf("kill `cat %s >/dev/null` 2>&1", file)
unix(sprintf("kill `cat %s` >/dev/null 2>&1", file), output)
unix(sprintf("/bin/rm -f %s",file))
}
save_array MAR
}'
#%MDESC% Configure the MAR image plate scanner and start the exposure.
def prepare_mar() '{
#HW070301begin: add the safe way of putting the filename into the server.
local temp1 temp2
#LC170403begin: ask spec to load the file parms from the server.
#that avoids error message about wrong file parameters !!
image_par(MAR["ccd_u"],"file_dir")
#LC170403end
temp1 = sprintf("%s/", OSC_DATA["dir"]); temp2=""
_ccd_remote_file_setup MAR["ccd_u"] temp1 OSC_DATA["basename"] \
temp2 OSC_DATA["image_number"]
#HW070301end
# set overwrite mode of file
image_par(MAR["ccd_u"],"overwrite",1)
ccd_createarray
}'
#%MDESC% Scans and erase the MAR IP scanner
def readout_mar() '{
# the newer implementation of the MAR IP device server make an automatic
# copy of the data as soon as it is taken.
# show the glimpse if demanded in setup
ccdstart
waitacq2
if ( MAR["glimpse"] ) ccdread
}'
# FASTSCAN Image plate scanner related code
#%MDESC% Setup menu
def fsds_osc_setup() '{
info sprintf("FASTSCAN: doing setup\n")
}'
#%MDESC%
def fsds_osc_prepare() '{
info sprintf("FASTSCAN: preparing reading\n")
}'
#%MDESC%
def fsds_osc_readout() '{
local counts
# make sure diode 2 is out
diode 2 off
# move diode 0 to expose the steel
# diode 0 off
# move out the beam stop
# umvr bsz -4
# locate the direct beam on the image plate without burning it!
# counts = OSC_SUP["fs_open"] # here are 100000 to much ! #* 100000
# counts = OSCILL["exposure_time"]
# f_open_ms_shutter(counts)
# move in the beam stop
# umvr bsz 4
# move diode 0 back in
# diode 0 on
# read the image plate
printf("\tFASTSCAN: Reading Image Plate with Fastscan on the PC\n")
if(fsds_go()) {
printf("\n\nReading aborted!! No data saved!\n\n")
exit
}
}'
#%MDESC%
def fsds_osc_save(filename) '{
local dirs
local ndirs
local i
local pc_filename
ndirs = split(filename,dirs,"/")
pc_filename = dirs[0]
for (i=1;i<ndirs;i++)
pc_filename = sprintf("%s\\\\%s",pc_filename,dirs[i])
printf("\tFASTSCAN: saving in file: %s ",pc_filename)
if(fsds_save(pc_filename,0)) {
printf("\n\nSaving aborted!! No data saved!\n\n")
exit
}
printf("done\n")
}'
#%UU% [on|off]
#%MDESC% use this macro to obtain more detailed information what is happening
# in case off problems. Informational messages will be written line by line
# rather than on the status line on the botton of the screen erasing each other.
def oscillation_debug '
{
if ("$1" == "on" || $# == 0)
{
# for diagnotic purposes display all messages line by line
global overwrite
rdef info "overwrite=0; print"
}
if ("$1" == "off")
{
# display all messages on a single line at the bottom of the screen
# so they do not mess up the layout
rdef info "dont_print="
}
}'
##%UU% [seconds]
##%MDESC% Utility to open the millisecond X-ray shutter manually for alig-
# nment (without current closed).
# Useful when the shutter is controlled by VCT6 as for the oscillation data
# collection.
# If you give an argument the shutter will close automatically after the
# given number of seconds. Otherwise it will reain open unlimited.
# (But note that the maximum programmable time of the VCT6 is about 12 h.)
##%PRE%
#
# VCT6 1 (mezzanine card, TTL) --> to shutter
#
##%PRE%
#def open_ms_shutter '
#{
# local counts
# # if argument program opening for this time else use maximum value
# if ($# > 0) counts = $1*100000 ; else counts = pow(2,32)-1
#
# f_open_ms_shutter(counts)
#}'
##%IU%
##
#def f_open_ms_shutter(counts) '{
# # configure the VCT6
# local vct6_init
# vct6_init[0] = 0 # Mode = master
# vct6_init[1] = 0 # Gate number - not used for master
# vct6_init[2] = 0 # Clock - 0 = internal, 1 = external
# vct6_init[3] = 0 # Clock Frequency - 100kHz if internal, not used if external
# vct6_init[4] = 0 # Free run mode - no
# vct6_init[5] = 0 # Start counting signal - internal
# vct6_init[6] = 0 # Stop counting - internal
# vct6_init[7] = 0 # Divider mode - not used (only for free-run)
# vct6_init[8] = 0 # Autoclear of counting value before each count (slave only)
# vct6_init[9] = 0 # Hold counting value if external start - unused (slave only)
# vct6_init[10] = 0 # Precount clock source - internal
# esrf_io(OSC_SUP["gate"],"DevCntInit",vct6_init)
# esrf_io(OSC_SUP["gate"],"DevCntPresetValue",counts)
# esrf_io(OSC_SUP["gate"],"DevCntStart")
#}'
##%UU% %MDESC% Utility to close the shutter again after "open_ms_shutter".
#def close_ms_shutter '
#{
# esrf_io(OSC_SUP["gate"],"DevCntStop")
#}'
##%UU% %MDESC% Utility to wait for the shutter closing
## after "open_ms_shutter opening_time".
#def wait_ms_shutter '{
# while(esrf_io(OSC_SUP["gate"],"DevCntStatus") == 2048) sleep(0.1);
#}'
# The following are general utility macros
#%IU%
#%MDESC% round up to the next larger or equal integer, as opposed to int()
# which rounds down
def ceil(x) '{if (int(x) == x) return x; else return int(x+1) }'
#%IU%
#%MDESC% round up to the next integer, as opposed to int()
# which rounds down
def round(x) '{if (x>=0) return int(x+0.5); else return int(x-0.5) }'
#%IU% <message> ...
#%MDESC% "info" used to display informational messages which can be
# helpful for debugging. The reason not simply to use "print" is to the
# collect macro can redirect or supress these messages by redefining "info"
def info 'dont_print='
# %IU% (message)
# %MDESC% utility for display informational messages.
def notice (message) '{
tty_cntl ("ce") # clear to end of line
tty_move (1000,1000,message) # print message starting from current position
tty_move (-1000-length(message),1000) # move cursor back to starting position
}'
#%IU% ([prompt])
#%MDESC% Utility for keybard input in single character no-echo mode.
# The prompt string is optional.
def key (prompt) '{
if (! (whatis("prompt") & 0x08000000)) print_underlined_highlighted (prompt)
local letter
letter = input(-1); while (asc(letter) == 0) letter = input(-1) ; input(1)
return letter
}'
#%IU% <variable> [<max length>]
def edit '{$1 = editline($1,$2)}'
#%IU% (template-text[,max_length])
#%MDESC% Prompts the user for test input, providing a template, like SPEC's
# built-in getval(), but you do not have to retype it but can modify it
# using the cursor keys.
# Note: The cursor keys work only on VT100 terminal emulators, "xterm" and
# "dtterm" are compatible with VT100, however "hpterm" is not.
def editline (template,max) '{
if (max == 0) max = 80
global linebuffer, maxlength, start, cursorpos, currentpos
linebuffer = template ; maxlength = max
cursorpos = length (template) # place the cursor at end of line
# text might be longer than avaiable space
start = 0; currentpos = 0
update_line
local c ; c = ""
while (c != "\n")
{
# wait for charater to be typed, used char-by-char input mode (-1)
c = input (-1); while (asc(c) == 0) c = input (-1)
if (asc(c) == 27) escape_sequence
if (asc(c) == 8) delete_char
if (asc(c) == 127) delete_char
if (asc(c) >= 32 && asc(c) < 127) insert_char(c)
update_line
}
input (1) # back to line-by-line input mode (1)
return linebuffer
}'
#%MDESC% used by "editline"
def update_line '
{
scroll # make sure that cursor is within bounds
# relative cursor positioning is done by adding an offset of 1000 or -1000
tty_move(-1000-currentpos,1000)
local text; text = substr(linebuffer,start+1,maxlength)
while (length(text) < maxlength) text = text" " # pad with blanks
printf ("%s",text)
tty_move(-1000-length(text),1000)
tty_move(1000+cursorpos-start,1000)
currentpos = cursorpos-start
}'
#%MDESC% used by "editline". Scroll text so cursor is within bounds
def scroll '
{
if (cursorpos >= start+maxlength) start = cursorpos-maxlength+1;
if (cursorpos < start) start = cursorpos;
}'
#%MDESC% used by "editline". Decode VT100 escape sequences generated by the
# cursor keys
def escape_sequence '
{
local c
# wait for end of escape-seqence (always with upper-case letter)
while (asc(c) < asc("A") || asc(c) > asc("Z")) c = input(-1)
if (c == "C") cursor_right
if (c == "D") cursor_left
# ignore all other escape-codes
}'
#%MDESC% used by "editline"
def cursor_left 'if (cursorpos > 0) cursorpos--'
#%MDESC% used by "editline"
def cursor_right 'if (cursorpos < length (linebuffer)) cursorpos++'
#%MDESC% used by "editline"
def insert_char(c) '{
part1 = substr (linebuffer,1,cursorpos)
part2 = substr (linebuffer,cursorpos+1)
linebuffer = part1 c part2 # concatenate strings
cursorpos++
}'
#%MDESC% used by "editline"
def delete_char '{
if (cursorpos > 0)
{
part1 = substr (linebuffer,1,cursorpos-1)
part2 = substr (linebuffer,cursorpos+1)
linebuffer = part1 part2 # concatenate strings
cursorpos--
}
}'
#%IU% <array>
#%MDESC% Useful for save global variables across a "spec -f".
# Writes the contents of all members of an array to an ASCII file,
# so they can be reloaded later by the "restore_array" macro. The file is in
# the directory ~specadm/local/userconf and has the same name as the array
# (no extension).
def save_array '
{
local dir file command
dir = BLISSADM"/local/userconf/"SPEC
if (file_info (dir,"isdir") != 1) unix (command="mkdir -p "dir)
file = dir"/$1"
unix (command = "rm -f "file)
for (i in $1) fprintf (file,"%s=%s\n",i,$1[i])
close (file)
}'
#%IU% <array>
#%MDESC% Loads back saved values from an ASCII file from a previous "save_array"
# call.
def restore_array '
{
local file line words tag value n
file = BLISSADM"/local/userconf/"SPEC"/$1"
if (file_info(file,"-e") == 1)
{
global $1
getline (file,"open")
while ((line = getline(file)) != -1)
{
n = split (line,words,"="); tag = words[0]; value = words [1]
if (n < 1) continue
split (value,words,"\n"); value = words [0] # get rid of trailing new-line
if (!(tag in $1)) $1[tag] = value
}
getline (file,"close")
}
}'
#%IU% (x)
#%MDESC% returns 1 if x>0 -1 if x<0 0 if x=0
def sign (x) '{
if (x>0) return 1
if (x<0) return -1
if (x==0) return 0
}'
#%IU% (string,n)
#%MDESC% returns the last n characters of a string. If the sting length
# is less the n then the full string is returned
def tail (s,n) '{ return substr(s,length(s)-n+1) }'
# #%IU% (n1,n2)
# #%MDESC% returns the greater value of the two input arguments.
#
# def max(n1,n2) '{ return n1>n2?n1:n2 }'
#
# please use spec_utils.mac version.
#
# Beam intensity measurement macros
#
#%IU%
# %MDESC% Beam intensity measurement macro, called once before the exposure (ie
# before the set of half-oscillations).
def oscillation_prepare_diode '{
local i
local vct6_init
local gate_info[]
ID30_BMON_AVG = 0
# Look for the VCT6 channel used for beam monitor counter
# for(i=0; (i<COUNTERS) && (cnt_mne(i)!=OSC_SUP["b_mon_mne"]); i++);
# if(i == COUNTERS)
# {
# printf("\nError: VCT6 beam monitor counter (%s) not found\n", \
# OSC_SUP["b_mon_mne"])
# exit
# }
# OSC_SUP["b_mon"] = OSC_SUP ["vct6"] "/" counter_par(i,"channel")
OSC_SUP["b_mon"] = OSC_SUP ["vct6"] "/" OSC_SUP["b_mon_mne"]
info sprintf("VCT6 beam monitor counter (%s) using: %s", \
OSC_SUP["b_mon_mne"], OSC_SUP["b_mon"])
# get the physical channel number from the logical one (they are different
# if the device server handle more than one VCT6 card and if we are not
# working on the first one)
esrf_io (OSC_SUP["gate"],"DevCntState",gate_info)
# Configure a VCT6 channel to count during the fast shutter opening time
esrf_io (OSC_SUP["b_mon"],"tcp")
esrf_io (OSC_SUP["b_mon"],"DevCntClear")
vct6_init[0] = 1 # Slave
vct6_init[1] = gate_info[7] # Gate number to watch (if slave)
vct6_init[2] = 1 # Clock - External
vct6_init[3] = 0 # Internal clock frequency - 100 kHz
vct6_init[4] = 0 # Free run mode - no
vct6_init[5] = 0 # Start counting signal - internal
vct6_init[6] = 0 # Stop counting - internal
vct6_init[7] = 0 # Divider mode (when free run) - disabled
vct6_init[8] = 1 # Autoclear of counting value before each count - enabled
vct6_init[9] = 0 # Hold counting value if external start - disabled
vct6_init[10] = 0 # Precount clock source - internal
esrf_io (OSC_SUP["b_mon"],"DevCntInit",vct6_init)
esrf_io (OSC_SUP["b_mon"],"DevCntStart")
info "VCT6 beam monitor counter started"
}'
#%IU%
# %MDESC% Beam intensity measurement macro, called during the exposure after
# each half-oscillation.
def oscillation_read_diode '{
local b_mon_val
info "measuring beamcurrent"
b_mon_val = esrf_io (OSC_SUP["b_mon"],"DevCntRead")
info sprintf("Beam monitor intensity: %8d ", b_mon_val)
ID30_BMON_AVG += b_mon_val
}'
#%IU%
# %MDESC% Beam intensity measurement macro, called during the exposure after
# each half-oscillation.
def oscillation_log_diode(d_log_file) '{
local b_mon_val
b_mon_val = ID30_BMON_AVG/OSCILL["number"]/OSCILL["exposure_time"]
fprintf (d_log_file,"%8d ", b_mon_val)
info sprintf("Beam monitor intensity per second : %8d ", b_mon_val)
}'
#%IU%
# %MDESC% Enter the speed parameter with some value check.
#
def enter_speed_parameter '{
local max_speed
local new_speed
local expo_time
max_speed = motor_par (OSC_SUP["gonio_n"],"velocity")
expo_time = getval("Exposure time [s]",OSCILL["exposure_time"])+0
new_speed = calculate_speed(expo_time)
while(new_speed > max_speed)
{
printf("Warning: too short exposure time !!\n")
expo_time = getval("Exposure time [s]",OSCILL["exposure_time"])+0
new_speed = calculate_speed(expo_time)
}
OSCILL["exposure_time"] = expo_time
}'
#%IU%
# %MDESC% Calculate the motor speed according to the range parameter
# and the motor parameters.
#
def calculate_speed(exposure_time) '{
local range
local speed
local nr_oscillations
local steps
local steps_per_degree
if(exposure_time <= 0) exposure_time = 0.01;
range = OSCILL["range"]
nr_oscillations = 1 # Reset the number of oscillations to 1.
# Calculate the number of motor steps for one half-oscillation
steps_per_degree = motor_par (OSC_SUP["gonio_n"],"step_size")
info "motor needs "steps_per_degree" steps/deg"
steps = int (fabs (range) * steps_per_degree)
info "steps per oscillation = "steps
# Strange feature of the VCT6: if external start is used the count number
# must be not be even, otherwise a miscounting in the range -1 to -127 occurres
if (steps % 2 == 0) steps++
info "steps per oscillation set to "steps" (VCT6 odd feature)"
range = (range > 0 ? 1 : -1) * steps / steps_per_degree
info "oscillation range set to "range" deg"
# Calculate the stady-state speed needed of the rotation motor [steps/s]
speed = steps*nr_oscillations/exposure_time
info "required speed = "speed" steps/s"
return speed
}'
#%UU%
#%MDESC%Set image parameters, detector distance and wavelength
def MM_mar_imagepar(__ccd_u, start_angle, stop_angle, exp_time, range, n_phi_osc, d_s_dist, \
wavelength) '{
local _i
image_par(__ccd_u, "hw_par", \
sprintf("opt_phi_start=%f opt_phi_stop=%f opt_expo_time=%f osc_range=%f opt_phi_osci=%f opt_distance=%f opt_wavelength=%f", \
start_angle, stop_angle, exp_time, range, n_phi_osc, d_s_dist, wavelength))
}'
#%MACROS%
#%IMACROS%
#%AUTHOR% by Friedrich Schotte, Manu Perez, Holger Witsch with bits from L.
#Claustre and D. Fernandez last changes March 2001
|