esrf

Beamline Instrument Software Support
SPEC Macro documentation: [ Macro Index | BCU Home ]

#%TITLE% TURBOSCAN.MAC
#%NAME%
#  Turbo scan macros
#%DESCRIPTION%
# This macro cntset uses a scan server allowing simultaneous 
# multiple read (encoders and vct6 channels at the same time).%BR%
#%BR%
# With minimum network load, fastest read time is around 40 msec.%BR%
#%BR%

#%XDL%
#%SETUP%
#%UL%%B%turbosetup%B% [<Scanserv-devname> <0|1> <Vct6-devname>] %BR%
# First parameters is scan server device name. %BR%
# Second parameter is nevether you want to save or not (1=save). %BR%
# Third parameter is vct6 device name. %BR%
# Fourth parameter has to be set to 1 until program advises to change it.%BR%
#%BR%
#
#%OVERVIEW%
# The following macros are provided:
#%DL%
#%DT%turboscan%DD% one motor continuous scan.%BR%
#%DT%turboscan2%DD% two motors continuous scan. %BR%
#%DT%turbosplot%DD% turboscan special splot. %BR%
#%BR%
#
#%EXAMPLE%
#%DL%
#%DT% turboscan om 0 1 1 50 %BR%
# this will scan at 1deg/s speed between position 0 and 1 %BR%
# with a readcnt every 50 msec %BR%
#%DT% turboscan2 om 0 1 2 theta 5 9 60 %BR%
# motor om drives  from 0 to 1 at 2 deg/min %BR%
# theta goes at the same time from 5 to 9. %BR%
# theta's speed is adapted for that the 2 motors reach %BR%
# their final point together %BR%
# reads are performed every 60 msec %BR%
#
#%END%
#%X%


#%UU% [<Scanserv-devname> <0|1> <Vct6-devname> <Sleep-time>] 
#%MDESC%
# 
def turbosetup '{
local i 

constant TS_TIMEMIN 100
constant TS_SECURITY 9096 # on bm16; 2048 on id12

global TS_SETUPCHECK TS_SAVE
global TS_TIME TS_SPEED TS_REALTIME TS_REALSPEED TS_MAXTIME
global TS_VELO TS_OLDVELO TS_OLDBASE TS_OLDACC
global TS_DEVNAME TS_VCT6SERV TS_NBCHA TS_MASTER TS_ELEM TS_NBREAD
global TS_FIRST 
global TS_NPTS TS_IDX
global TS_SLEEP TS_ONEMOTOR TS_ONEMNE
global TS_FPRNT # used in _turbo_save_point



TS_TIME       = TS_TIMEMIN
TS_MAXTIME    = pow(2,31) / counter_par(sec,"scale") # vct6 is 32 bit counter
TS_SETUPCHECK = 1
TS_MASTER     = (counter_par(sec,"unit")+1) * 100 + counter_par(sec,"channel")
# I need here one mnemonic of one of the motors to scan. Otherwise we have
# all kind of errors in cntconfig. This is used in ts_cntconfig.
  if ($#) {
    TS_DEVNAME  = "$1"
    TS_VCT6SERV = "$2"
    TS_SLEEP    = $3
    TS_NBCHA    = $4
    TS_ONEMNE   = $5
  } else {
    TS_DEVNAME  = getval("Scan server device name",TS_DEVNAME)
    TS_VCT6SERV = getval("Vct6 device name (ie D16/VCT6_1)",TS_VCT6SERV)
    TS_SLEEP    = getval("Time interval SPEC reads from VME buffer",\
				TS_SLEEP ? TS_SLEEP : 1)
    TS_NBCHA    = getval("How many Vct6 channel",TS_NBCHA )
    TS_ONEMNE   = getval("Enter the mnemonic of one posible motor to scan",TS_ONEMNE )
  }

#  TS_NBREAD = int (4096 / (2 + 2*TS_NBCHA + 2)) # Maximum 2 motors (last +2)
                                                # Now it could be more, 4096
                                                # was limit for data_group
  TS_NBREAD=2048

  if (motor_num(TS_ONEMNE) != -1) 
    TS_ONEMOTOR   = motor_par(motor_num(TS_ONEMNE),"channel")+1
  else
    p "Warning, motor mne not defined:",TS_ONEMNE

  cdef("config_mac","_turbo_oldspeed ; _turbo_cntconfig; ","turbo")
  _turbo_oldspeed
  _turbo_cntconfig
}'


#%UU% <motor mne> <from> <to> <deg/min> [ms read]
#%MDESC%  one motor continuous scan.
#
def turboscan '{
  if (!TS_SETUPCHECK) { print "Error: run turbosetup first."; exit }

  if (($# != 4) && ($# != 5)) {
     p "Usage: $0 <motor mne> <from> <to> <deg/min> [ms read]"
     exit
  } else { 
    _m[0]=motor_num("$1"); _s[0]=$2; _f[0]=$3; TS_SPEED[0]=$4; _nm = 1
    if ($5) {
      TS_TIME=$5 
    } else {
      TS_TIME=TS_TIMEMIN 
    } 
  }

  cdef("user_cleanup2","_turbo_cleanup; ", "turbo")
  HEADING = sprintf("turboscan $*")
# GBMV Put the date here, took it out of turboscan...
  DATE = date()

  _turbo_check
  _turbo_init
  _turbo_prepfprintf
  _turboscan
}'
 

#%UU% <motor1> <from> <to> <deg/min> <motor2> <from> <to> [ms read]
#%MDESC% two motor continuous scan
#
def turboscan2 '{
  if (!TS_SETUPCHECK) { print "Error: run turbosetup first."; exit }

  if (($# != 7) && ($# != 8)) {
     p "Usage: $0 <mot1> <from> <to> <deg/min> <mot2> <from> <to> [ms read]"
     p "   speed for motor2 is calculated by the macro"
     exit
  } else { 
    _m[0]=motor_num("$1"); _s[0]=$2; _f[0]=$3; TS_SPEED[0]=$4; 
    _m[1]=motor_num("$5"); _s[1]=$6; _f[1]=$7; _nm = 2
    if ($8) {
      TS_TIME=$8 
    } else {
      TS_TIME=TS_TIMEMIN 
    } 
  }

  cdef("user_cleanup2","_turbo_cleanup; ", "turbo")
  HEADING = sprintf("turboscan2 $*")
# GBMV put it here
  DATE = date()

  _turbo_check
  _turbo_init
  _turbo_prepfprintf
  _turboscan

}'


#%IU%
#%MDESC% Parameter check for the macros %B%turbo%B%
#
def _turbo_check '{
   local mot
   for (mot = 0; mot < _nm; mot ++) {
      if (_m[0] == -1) {
        p "Invalid motor name"
        exit
      } 
      _bad_lim = 0
      _chk_lim _m[mot] _s[mot]
      _chk_lim _m[mot] _f[mot]
      if (_bad_lim) exit
  }
}'


#%IU%
#%MDESC% Initialisations for the macros %B%turbo%B%
#
def _turbo_init '{
  local mot_list i_mot
# GBMV
  local mot_command

  mot_list = motor_mne(_m[0])
  for (i_mot = 1; i_mot < _nm; i_mot++) {
     mot_list = sprintf("%s, %s", mot_list, motor_mne(_m[i_mot]))
  }
  printf("Moving %s to the start position..\n", mot_list)
# GBMV thinks update1 + 2 need now motor mne instead of number or something
# just do this:
  for (i_mot = 0; i_mot < _nm; i_mot++) {
     A[_m[i_mot]] = _s[i_mot]  
     mot_command = sprintf("%s %s %f", \
		mot_command, motor_mne(_m[i_mot]), A[_m[i_mot]] ) 
  } 
  _mmv(0x02, mot_command, A[_m[0]] ) 
#insted of this: 
# waitmove; get_angles; 
# for (i_mot = 0; i_mot < _nm; i_mot++) { 
#    A[_m[i_mot]] = _s[i_mot] 
# } 
# move_em  ;  
# if (_nm == 1)	{ 
#   _update1 _m[0] 
# } else { 
#   _update2 _m[0] _m[1] 
# } 
 
  _turbo_set_speed 
  _turbo_init_group 
  _turbo_save_header 
  turborplot   
}'


#%IU%
#%MDESC% Saves the old speed for the continuous scan.
#
def _turbo_oldspeed '{
  local i_mot

  for (i_mot = 0; i_mot < _nm; i_mot++) {
    TS_OLDVELO[i_mot] = motor_par(_m[i_mot],"velocity")
    TS_OLDBASE[i_mot] = motor_par(_m[i_mot],"base_rate")
    TS_OLDACC[i_mot]  = motor_par(_m[i_mot],"acceleration")
  }
}'


#%IU%
#%MDESC% Sets the new speed for the continuous scan.
#
def _turbo_set_speed '{
  local i_mot

    if (_s[0] != _f[0])
      TS_SPEED[1]=fabs(_s[1]-_f[1]) / fabs(_s[0]-_f[0]) * TS_SPEED[0]

    for (mm=0; mm < _nm; mm++) {
      TS_VELO[mm] = TS_SPEED[mm] * fabs(motor_par(_m[mm],"step_size")) / 60
      motor_par(_m[mm],"velocity",TS_VELO[mm])
      if (motor_par(_m[mm],"base_rate") > TS_VELO[mm])
        motor_par(_m[mm],"base_rate",TS_VELO[mm]/2)

      motor_par(_m[mm],"get_pars")
      if (motor_par(_m[mm],"base_rate") > TS_VELO[mm])
        p "Warning: Base rate is: ",motor_par(_m[mm],"base_rate"),"steps/s"

      TS_REALSPEED[mm] = fabs(60 * motor_par(_m[mm],"velocity") \
			/	 motor_par(_m[mm],"step_size"))

      printf("Motor %s will drive at %7.4f deg/min \n",motor_mne(_m[mm]), \
                       TS_REALSPEED[mm])   
    }

    if (TS_REALSPEED[0]) 
      TS_REALTIME = fabs((_f[0]-_s[0]) / TS_REALSPEED[0] * 60)

}'


#%IU%
#%MDESC%
#
def _turbo_init_group '{
  local ts_size esttime
  TS_ELEM = 1 + TS_NBCHA*2 + _nm
  if ((esttime = int (TS_TIME/10) - 1) == 0)
    esttime = 1
  
  TS_SIZE = TS_REALTIME/esttime*100 + TS_SECURITY
  array TS_ARRAY[TS_SIZE][1+TS_NBCHA+_nm]
  array TS_ARRBUFF[TS_NBREAD][TS_ELEM]
  array TS_LASTPT[1][100]
}'


#%IU%
#%MDESC%
#
def _turboscan '{
  local ts_units ts_label ts_readval ts_line ts_cnt ts_runpars i j k
  local ts_sleept ts_stime ts_stop

  if (TS_REALTIME > 60) { ts_units = 60; ts_label="min." }
  else if (TS_REALTIME > 1) { ts_units = 1; ts_label="sec." } 
    else { ts_units = 0.001; ts_label="msec." }
  printf("Get ready for scan %d of %10.2f %s\n",SCAN_N,\
			TS_REALTIME/ts_units, ts_label)
  ts_stop = 0
# GBMV commented this out and put it in turbo_scan to hopefully correct 
# previous scan date thing
#  DATE = date()
  _ctime = TS_TIME
  T_L = "Scan" SCAN_N
  X_L = motor_name(_m[0])
  Y_L = cnt_name(DET)

  esrf_io(TS_DEVNAME,"tcp")

  ts_runpars[0] = TS_TIME
  ts_runpars[1] = TS_MASTER
  for (k=0; k<_nm;k++) {
    ts_runpars[2+k] = motor_par(_m[k],"channel")+1
  }
  
  ts_cnt = 0
  get_angles
  for (i = 0; i < _nm; i++) {
    A[_m[i]] = _f[i]
  }

  count_em TS_MAXTIME
  esrf_io(TS_DEVNAME,"DevRun",ts_runpars)
  sleep(1)
  esrf_io(TS_DEVNAME,"DevReadValues",TS_NBREAD,TS_ARRBUFF)

  move_em

  TS_NPTS  = 0
  TS_FIRST = 1
  TS_EPOCH = 0        # or use time() think later
   
  while (chk_move) {
    ts_stime = time()
    ts_readval = esrf_io(TS_DEVNAME,"DevReadValues",TS_NBREAD,TS_ARRBUFF)
    if (ts_readval > 0) {
      _turbo_fastfill (ts_readval/TS_ELEM, TS_ARRAY)
      _turbo_fastsave (ts_readval/TS_ELEM)
      plot_cntl("addpoint")
      _turbo_ppoint
    }
    ts_sleept = TS_SLEEP - (time() - ts_stime)

    if (ts_sleept > 0) 
      sleep (ts_sleept)
    printf("\r%5d %s: %10.4f t: %10.4f", \
	TS_ARRAY[TS_NPTS-1][0], motor_mne(_m[0]), \
	TS_ARRAY[TS_NPTS-1][TS_NBCHA+1], TS_ARRAY[TS_NPTS-1][1] )
  }

  _turbo_end

  if (TS_ARRAY[TS_NPTS-1][0] - TS_ARRAY[0][0] + 1 == TS_NPTS) { 
	p "Just worked great"
  } else { 
	p " Lost a couple " 
  }

}'


#%IU%
#%MDESC% optimized version
#
def _turbo_fastfill(noval,arr) '{
  local i n k ts_line val errors serrors thisval 
  global TS_LASTSEQ

  for (i=0; i < noval; i++) {
    val = TS_ARRBUFF[i][0]
    if (!val) {
      errors ++
      continue
    }
    if (!TS_FIRST) {
      if ( val <= TS_LASTSEQ) {
        serrors++    # Sequence must be in order 
        continue
      } 
      arr[TS_NPTS][0] = val # Seq. number

      arr[TS_NPTS][1] = TS_ARRBUFF[i][2] / counter_par(sec,"scale") # epoch
      if (arr[TS_NPTS][1] < arr[TS_NPTS - 1][1]) {
        serrors++     #Negative time, strange
        continue
      }

      for (k=2; k < TS_NBCHA+1; k++) {  # Counters
        arr[TS_NPTS][k] = TS_ARRBUFF[i][2*k] - TS_LASTPT[0][k]
        TS_LASTPT[0][k]   = TS_ARRBUFF[i][2*k]
      }

      for (k=0; k<_nm; k++) { # Motors
        arr[TS_NPTS][TS_NBCHA+1+k] = TS_ARRBUFF[i][2*TS_NBCHA+1+k]\
                                        / motor_par(_m[k],"encoder_step_size")
      }
      TS_NPTS++
    }
    else {
      TS_FIRST = 0
      for (k=2; k < TS_NBCHA+1; k++) {
        TS_LASTPT[0][k] =  TS_ARRBUFF[i][2*k]
      }
    }
    TS_LASTSEQ=val
  }
  if (errors)
    printf ("\n%d wrong value(s) sent, try slower speed (now %d [ms])\n",\
                errors,TS_TIME)
  if (serrors) {
      printf ("\n%d times points arrived out of order\n",serrors)
  }
}'


#%IU%
#%MDESC%
#
def _turbo_cleanup '{
  fprintf(DATAFILE, "# turboscan aborted with Ctrl-C\n")
  _turbo_end
}'


#%IU%
#%MDESC%
#
def _turbo_end '{
  local i_mot

  stop(2)

  p "Setting motor speed back .."
  for (i_mot = 0; i_mot < _nm; i_mot++) {
    motor_par(_m[i_mot],"velocity",TS_OLDVELO[i_mot])
    motor_par(_m[i_mot],"base_rate",TS_OLDBASE[i_mot])
    motor_par(_m[i_mot],"acceleration",TS_OLDACC[i_mot])
  }

  _turbo_stop 
  turbosplot
  cdef("user_cleanup2","","turbo","delete")
}'


def _turbo_stop '
  esrf_io(TS_DEVNAME,"DevStop")
'

#%IU%
#%MDESC% Called by _turbo_init
#
def _turbo_save_header '{
  local i
  FPRNT=""
  for (i=0;i<_nm;i++) {
    FPRNT=sprintf("%s%s  ",FPRNT,motor_name(_m[i]))
  }
  FPRNT=sprintf("%s%s  ",FPRNT,_hkl_sym1)
  SCAN_N = savestdheader(DATAFILE,3,SCAN_N)
  _cols= _nm+_hkl_col
  offt; savecntheader(DATAFILE); ont;
}'


#%IU%
#%MDESC% Called in turbosetup and config_mac
# prepares macro _turbo_fprintf 
#
def _turbo_prepfprintf '{
  local i str1 str2 pos str3
  
  str1 = ""
  pos = $1
  for (i=0; i<_nm; i++) {
#   str1 = sprintf("%s %%.11g",str1)
#   to remove the blank as first character  
    if (i == 0){
       str1 = sprintf("%%.11g")
    }
    else {
       str1 = sprintf("%s %%.11g",str1)
    }
    str2 = sprintf("%s,TS_ARRAY[pos][%d]",str2,TS_NBCHA+1+i)
  }

  str1 = sprintf("%s %s %s",str1,_hkl_val,Fout)

  _turbo_prepfprintflocal1 sec

  str1 = sprintf("%s %%g",str1)
  str2 = sprintf("%s,val",str2)

  for (i=0; i<COUNTERS; i++) {
    if (i != sec && i != DET && i != MON && cnt_name(i) != "unused") {
      _turbo_prepfprintflocal1 i
    }
  }

  _turbo_prepfprintflocal1 MON
  _turbo_prepfprintflocal1 DET

  str3=sprintf("fprintf(file,\"%s\n\"%s)",str1,str2)
  rdef _turbo_fprintf str3
}'


#%IU%
#%MDESC%  reduces code size of _turbo_prepfprintf
# warning : uses local variable of _turbo_prepfprintf
#
def _turbo_prepfprintflocal1 '
  if (TS_IDX[$1] != -1) {
    str1 = sprintf("%s %%g",str1)
    str2 = sprintf("%s,TS_ARRAY[pos][%d]",str2,TS_IDX[$1]+1)
  }
  else {
    str1 = sprintf("%s %%g",str1)
    str2 = sprintf("%s, S[%d]",str2,$1)
  }
'
#%IU%
#%MDESC% Writes the scan data of one point to the data file.
#        optimized version
#
def _turbo_fastsave(no_val) '{
  local i n val pos file str
  local array s[COUNTERS]

  if (!(whatis("_turbo_fprintf")&2)) {
    p "Error: _turbo_fprintf is not defined"
    exit
  }
  file = DATAFILE
  if (TS_NPTS && file != "" && file != "/dev/null") {
    for (i=0; i<no_val; i++) {
      if ((pos = TS_NPTS - no_val +i) < 0)
        continue

      ts_user_measure 

      if (pos) {
        val = TS_ARRAY[pos][TS_IDX[sec]+1] - TS_ARRAY[pos-1][TS_IDX[sec]+1]
      } else {
        val = 0
      }
      _turbo_fprintf
    }
    close(DATAFILE)
  }
}'


#%IU%
#%MDESC% Called by turbosetup
#
def _turbo_cntconfig '{
  local ts_stateres i j ts_idx ts_idx2 ts_runpars ts_res
  array readarr[1][30], ts_res[30] 
  ts_stateres[0]=0
  ts_runpars[0] = 100
  ts_runpars[1] = TS_MASTER
  ts_runpars[2] = TS_ONEMOTOR
  
  for (j=0; j<COUNTERS; j++) {
    TS_IDX[j]=-1  
  }
  esrf_io(TS_DEVNAME,"DevRun",ts_runpars)  
  sleep(1)
  esrf_io(TS_DEVNAME,"DevReadValues",1,readarr)
  esrf_io(TS_DEVNAME,"DevStop")

  for (j=0; j<COUNTERS; j++) {
    if (counter_par(j,"controller") == "VCT6") {
# I guess the Vct6 server name here.
      sname = sprintf("%s/%d",TS_VCT6SERV, counter_par(j,"channel"))
      esrf_io(sname,"DevCntState",ts_res)
      ts_idx2 = ts_res[12] * 100 + ts_res[7]
      for (i=0; i< TS_NBCHA; i++) {
        ts_idx = readarr[0][2*i+1]
        if (ts_idx == ts_idx2)
           TS_IDX[j]=i  
      }
    }
  }     
}'


#%UU%
#%MDESC% rplot (plot during acquisition) designed for turboscan
#
def turborplot '
  plot_cntl("filter1")
  plot_cntl("open")
  plot_cntl(sprintf("colors=%s",rplot_col))
  plot_move(0,1,T_L)
  plot_move(0,2,Y_L)
  plot_move(0,-1,sprintf("%.8s", X_L))
  plot_cntl("title=TurboScan")
  plot_cntl("erase")
  plot_range(_s[0],_f[0],"auto","auto")
'

#%UU%
#%MDESC% splot designed for turboscan
#
def  turbosplot '{
  if (PLOT_MODE&128) {
    plot_cntl(sprintf("colors=%s",splot_col))
    plot_cntl("open")
  }
  plot_cntl("erase")
  plot_range("auto","auto",YMIN,"auto")
  plot_move(0,1,T_L)
  plot_move(0,2,Y_L)
  plot_move(0,-1,sprintf("%.8s", X_L))
  _turbo_ppoint
  plot_move(0,3)
}'


#%IU%
#%MDESC% Called by _turboscan and turbosplot
#
def _turbo_ppoint '{
  local i ts_idx ts_str _X
  ts_str = sprintf("%d",TS_NBCHA+1)
  for (i=0; i<PLOT_NO; i++) {
    if ((ts_idx=TS_IDX[PLOT_LIST[i]]) != -1)
      ts_str = sprintf("%s,%d",ts_str,ts_idx+1) 
  }
  data_plot(TS_ARRAY[:TS_NPTS-1][ts_str])

  _X = TS_NBCHA+1
  plot_move(-50,0,ts_res1)
  plot_move(-50,1,ts_res2)
}'

def ts_res1 'sprintf("Peak at %.5g%s is %.5g.  COM at %.5g%s.   ",\                                            TS_xMAX,UL,TS_MAX,TS_COM,UL)'

def ts_res2 'sprintf("FWHM is %.5g%s at %.5g%s.   ",\
                              TS_FWHM,UL,TS_CFWHM,UL)'

def TS_FWHM  'array_op("fwhm",    TS_ARRAY[:TS_NPTS-1][_X],                                              TS_ARRAY[:TS_NPTS-1][ts_idx+1])'

def TS_CFWHM 'array_op("cfwhm",    TS_ARRAY[:TS_NPTS-1][_X],                                              TS_ARRAY[:TS_NPTS-1][ts_idx+1])'

def TS_xMAX 'array_op("x_at_max",    TS_ARRAY[:TS_NPTS-1][_X],                                              TS_ARRAY[:TS_NPTS-1][ts_idx+1])'

def TS_MAX 'array_op("max",    TS_ARRAY[:TS_NPTS-1][ts_idx+1])'

def TS_COM 'array_op("com",    TS_ARRAY[:TS_NPTS-1][_X],                                              TS_ARRAY[:TS_NPTS-1][ts_idx+1])'



def ts_define_empty_stubs '
if (!(whatis("ts_user_measure")&2)) rdef ts_user_measure ""
'
ts_define_empty_stubs

#%MACROS%
#%IMACROS%
#%AUTHOR%
#  TURBOSCAN.MAC J.K - R.P 9-96
#%TOC%