esrf

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

#%TITLE% ADC.MAC
#%NAME%
#  Macros to work with an ADAS ICV 150 ADC from SPEC
#%CATEGORY%  Generic I/O
#%DESCRIPTION%
#  This macro set provides an easier interface to work with the ADAS ADC
#  card. You can configure the ADC as a counter or monitor and use the
#  standard SPEC macros to work with this card. %BR%
#  The new version of these macros will read the ADC during counting
#  and calculate the average value during this time.
#
#  You need to define a timer therefore (use the UNIX software timer
#  if you don't have another one)
#%EXAMPLE%
#%DL%
#%DT%adcwatch%DD% (Read the ADC and plot the results on the screen)
#%DT%  ct 3 %DD%(Read the adc during three seconds and display the average voltage)
#%DT%  dscan motor1 0 10 10 2 %DD%(Do a scan with motor1, but use the ADC to
#        get at each point a reading in mV)
#%DT%  adcsetup "ID9/icvadcID92/0" 2 1 0 50 %DD%(This is normally done in
#        idxxstartup.mac to set the parameters for the adc)
#%XDL%        
#%SETUP%
#%UL%
#%LI% You have to define counters with mnemonics adc1, adc2, and so on
#  with spec's configuration editor to use the adc. If adcsetup has
#  been done , the counter adc<x> will hold the adc readings. If you
#  undefine the counter adc<x> again, it will automatically realize
#  that you do not want to read the adc for the moment. 
#%LI% The parameters for the adc and the definition of the pseudo counters
#  are done with adcsetup. 
#%XUL%
#%END%

global ADCMACLOADED


def adc_allprecount 'adc_mreadstart'

def adc_precount 'ADC_DACTIVE[$3]=1;'

def adc_allgetcounts 'adc_mread'

def adc_getcounts 'S[$1]=counter_par($1,"scale")*ADC_AVER[$2]*ADC_SCALE/ ((ADC_NOREADS[$3]==0)?1:ADC_NOREADS[$3]);'



# Will read all the configured ADCs and sum them up in ADC_AVER
def adc_mreadstart '
{ 
  local i
  for (i=0;i<ADC_NODEV;i++) { ADC_DACTIVE[i]=0; ADC_NOREADS[i]=0}
  for (i=0;i<ADC_NOCHANNEL;i++) { ADC_AVER[i]=0 }
} '

def adc_mread '
global ADC_AVER ADC_NOREADS
{
local i j maxi channel values
global ADC_LASTETIME
for (i=0;i<ADC_NODEV;i++) {
  if (ADC_DACTIVE[i]) {
    channel[0]=ADC_MINC[i] ; channel[1]=ADC_MAXC[i] ; 
    ESRF_ERR = -1
    esrf_io(ADC_DDEV[i],ADC_TRIG?"DevTrigReadValues":"DevReadValues",\
	channel,values)
    if (!ESRF_ERR) {
      for (j=0;j<ADC_NOCHANNEL;j++) \
        if (ADC_IDX[j] == i) ADC_AVER[j]+=values[ADC_CHA[j]-channel[0]]
    ADC_NOREADS[i]++;
    } else if (ESRF_ERR == 151) {
# Data stale/data overflow
      if (time() - ADC_LASTETIME > 300) {
        print "WARNING: READ ADC value is out of range"
        print "         (This message will not reappear for 5 minutes)"
        ADC_LASTETIME = time()
      } 
    } else if (ESRF_ERR == 58) {
# command not allowed in this device state
      print "WARNING: SPEC uses triggered read but adc card is switched on"
    }  
  }
}
} '

#%UU%  [s]: Like adcmread but the results will be plotted
#%MDESC%

def adcwatch '
global ADC_AVER ADC_CHI2 
{
local i j maxi 
global ADCDATA
ADCDATA=255
maxi = ($1+0) ? $1 : 1
data_grp(ADCDATA,maxi,ADC_NOCHANNEL+1)
for (i=0;i<ADC_NODEV;i++) ADC_DACTIVE[i]=1;
for (j=0;j<maxi;j++) {
  for (i=0;i<ADC_NOCHANNEL;i++) ADC_AVER[i]=0;
  adc_mread
  data_put(ADCDATA,j,0,j)
  for (i=0;i<ADC_NOCHANNEL;i++) {
    data_put(ADCDATA,j,i+1,ADC_AVER[i]*ADC_SCALE)
  }
}

plot_cntl("filter2")
if (PLOT_MODE&128) {
  plot_cntl(sprintf("colors=%s",splot_col))
  plot_cntl("-ylog -ebars lines dots")
  plot_cntl("open")
  }
plot_cntl("erase")
plot_range("auto","auto","auto","auto")
data_plot(ADCDATA,0,0,"all")

plot_move(0,1,sprintf("ADC in mV"))
for (i=0;i<ADC_NOCHANNEL;i++) {
  ADC_AVER[i]=data_anal(ADCDATA,0,0,0,i+1,"sum")/maxi
  if (ADC_NOCHANNEL<4) {
    plot_move(-60,i,sprintf("ADC Channel %d: Average: %f ",ADC_CHA[i],ADC_AVER[i]))
    }
  else {
    printf ("ADC Channel %d: Average: %f\n",ADC_CHA[i],ADC_AVER[i])
    }
  }
plot_cntl("filter1")
}
'

#%UU%  [Channel] 
#%MDESC%

# reads from Channel or DEFAULT channel given in
#                       adcsetup 
def adcread '
{
  local chann devn
  if ($# > 2) print "Usage:  adcread [channel] [device number]"
  if ($# > 0) { chan =$1 } else { chan = ADC_CHA[0] }
  if ($# > 1) { dev = ADC_DEV[$2] } else { dev = ADC_DEV[0] }
  print esrf_io(dev,ADC_TRIG?"DevTrigReadChannel":"DevReadChannel",\
	channel) " V"
} '

#%UU% [scale]
#%MDESC% Defines a global scale factor, every adc has to be multiplied with
# Default is 1000 to give mV output
def adcscale '
if ($#) {
  ADC_SCALE=$1
} else {
  ADC_SCALE=getval("Enter global adc scale factor",ADC_SCALE)
}
'

#%UU%  [mne device channel] [mne device channel] ...
#%MDESC%
# The new version allows multiple devices to be used. The input format
# had to be changed therefore, but the macros still accept the old
# input format: %BR%
# %B% [device name] [channel] [No. of channels] [gain] [No. reads] %B%
#%PRE%
#         Parameters: device name : the name of your device (ex.: ID112Adc)
#		      channel : the channel to read from (0-n)
#                     No. of channels : number of channels to be read from
#		            the ADC card.
#                     gain : the gain is given in power of 2. If the
#                            range of your card is -10 to 10 V then:
#				gain 0 : -10 -> 10 V
#				gain 1 : -5 -> 5 V
#				gain 2 : -2.5 -> 2.5 V
#				gain 3 : -1.25 -> 1.25 V
#				gain 4 : -0.63 -> 0.63 V
#                            Whatever gain you will set, the value you
#                            read are always in Volt
#                     No. reads : the number of reads for the multiple
#                                 read command 
#%PRE%
# The questions asked if you do not supply input parameters on the
# command line : %BR%
#%PRE%
# How many ADC pseudo counters do you want to define
#   Counter mnemonic (the counter mnemonic)
#   Device name for pseudo counter (the ESRF device server)
#   Channel on this ADC card (channel starts with 0) 
#%PRE%
#
def adcsetup ' {
global ADC_DEV ADC_CHA ADC_MNE ADC_IDX ADC_NOCHANNEL 
global ADC_DDEV ADC_MINC ADC_MAXC ADC_NODEV ADC_TRIG
global ADC_NOREADS ADC_GAIN ADC_MINV ADC_MAXV ADC_DACTIVE ADC_SCALE
local _pp ii
_pp[0]="$1" ; _pp[1]="$2" ; _pp[2]="$3" ; _pp[3]="$4" ; _pp[4]="$5" ; 
_pp[5]="$6" ; _pp[6]="$7" ; _pp[7]="$8" ; _pp[8]="$9" ; _pp[9]="$10" ; 
_pp[10]="$11" ; _pp[11]="$12" ; _pp[12]="$13" ; _pp[13]="$14" ; _pp[14]="$15" 

ADC_TRIG = 0 # We want to read continuously

adcoff
 
if (!$#) {
  ADC_NOCHANNEL = getval("How many ADC pseudo counters do you want to define",\
	ADC_NOCHANNEL)
  for (ii=0;ii<ADC_NOCHANNEL;ii++) {
    ADC_MNE[ii] = getval(sprintf("%d. Counter mnemonic",ii+1),ADC_MNE[ii])
    ADC_DEV[ii] = getval(sprintf("Device name for pseudo counter %s ",ADC_MNE[ii]),ADC_DEV[ii])
    ADC_CHA[ii] = getval("Channel on this ADC card",ADC_CHA[ii])
  }
} else {
  if (index("$2","/") !=0 ) {
    # New format : adcsetup mne device channel mne device channel ...
    ADC_NOCHANNEL = int($#/3)
    for (ii=0;ii<ADC_NOCHANNEL;ii++) {
      ADC_MNE[ii] = _pp[ii*3]
      ADC_DEV[ii] = _pp[ii*3+1]
      ADC_CHA[ii] = _pp[ii*3+2]
    }
  } else {
    # Old format :  adcsetup [device] [channel] [No. of ch] [gain] [No. reads]
    ADC_NOCHANNEL = $3
    for (ii=0;ii<ADC_NOCHANNEL;ii++) {
      ADC_MNE[ii] = sprintf("adc%d",ii+1)
      ADC_DEV[ii] = _pp[0]
      ADC_CHA[ii] = _pp[1]+ii
    }
  }
}

#Add some default values which can not be changed with setup for the moment
for (ii=0;ii<ADC_NOCHANNEL;ii++) {
  ADC_GAIN[ii]=0;
  ADC_MINV[ii]=-10;
  ADC_MAXV[ii]=10;
} 

if (ADC_SCALE == 0) ADC_SCALE=1000;

#Prepare device information
for (i=0,ADC_NODEV=0; i<ADC_NOCHANNEL; i++) {
  for (j=0,indx = -1; j < ADC_NODEV; j++ ) {
    if (ADC_DEV[i] == ADC_DDEV[j]) {
      indx = j
      break
    }
  }
  if (indx == -1) {
    ADC_DDEV[indx= ADC_NODEV++] = ADC_DEV[i]
    ADC_MINC[indx] = ADC_CHA[i] ; ADC_MAXC[indx] = ADC_CHA[i] ;
  } else {
    if (ADC_MINC[indx] > ADC_CHA[i]) ADC_MINC[indx] = ADC_CHA[i] ;
    if (ADC_MAXC[indx] < ADC_CHA[i]) ADC_MAXC[indx] = ADC_CHA[i] ;
  }
  ADC_IDX[i]=indx ; 
}

adcon
# p "Doing an adc init - look in adc.mac if this fails and help y....."
adc_init
} '

#%UU% 
#%MDESC% Switches off all adc related in/output

def adcoff '
# undef all the old definitions first
for (ii=0;ii<ADC_NOCHANNEL;ii++) {
  cpseudosdel ADC_MNE[ii]
  if ((idx = cnt_num(ADC_MNE[ii])) != -1)
    counter_par(idx,"disable",1)
}
cpseudodel "_adc"
'

#%UU% 
#%MDESC% Switches on all adc related in/output (after adcoff)
def adcon '
#cpseudodef countername precount postcount getcounts cleanup config user1 user2
{
  local ppstring
  for (i=0; i<ADC_NOCHANNEL; i++) { 
    ppstring = sprintf("%s adc_precount none adc_getcounts none none %d %d",\
	ADC_MNE[i],i,ADC_IDX[i])
    cpseudosdef ppstring
    if ((idx = cnt_num(ADC_MNE[i])) != -1)
      counter_par(idx,"disable",0)
  }
  if (ADC_NOCHANNEL) {
    cdef("user_prepcount","adc_allprecount; ","_adc")
    cdef("user_getcounts","adc_allgetcounts; ","_adc")
    cdef("user_pollcounts","adc_allgetcounts; ","_adc")
  }
}'


#%IU%
#%MDESC% Sends the global values for adc min and max to the device.
def adc_init '
# They deleted my stop command
#  adcstop
# If you want to change the gain complain with Antonia . She promised
# it for: August 1996 (10% Salary reduction otherwise)  
#  adcgain
# Another victim of the deleted command syndrom
#  adcsetlast
# Do not set min max because we do not know if current or voltage
#  adcminmax
if (ADC_TRIG ) {
  adcstop
} else {
# I dont need it but you never know. Keep it for a while.
  adcstart
}
  '
 
#adcstart [dev]
#%UU%  : starts the Adc for cont. reading
#%MDESC%

def adcstart '  
  if (!$#) {
    local i
    for (i=0;i<ADC_NODEV;i++) esrf_io(ADC_DEV[i],"DevStartConversion")
  } else {
    local dev
    dev = "$1" ; esrf_io(dev,"DevStartConversion")
  }
'
#adcsetlast [dev lastchannel]
def adcsetlast '
  if (!$#) {
    local i
#   for (i=0;i<ADC_NODEV;i++) esrf_io(ADC_DEV[i],"DevSetNum",ADC_MAXC[i])
# Set the last channel to 32 to be sure that we do not disturb other
# servers on the same card
    for (i=0;i<ADC_NODEV;i++) esrf_io(ADC_DEV[i],"DevSetNum",32)
  } else {
    local dev
    dev = "$1" ; esrf_io(dev,"DevSetNum",$2+0)
  }
'
#adcstop [dev]
#%UU%   : stops the Adc
#%MDESC%

def adcstop '  
  if (!$#) {
    local i
    for (i=0;i<ADC_NODEV;i++) esrf_io(ADC_DEV[i],"DevStopConversion")
  } else {
    local dev
    dev = "$1" ; esrf_io(dev,"DevStopConversion")
  }
'

#adcgain [dev chan gain]
def adcgain '{  
  local i inp
  if (!$#) {
    for (i=0;i<ADC_NOCHANNEL;i++) {
     inp[0]=ADC_CHA[i]; inp[1]=ADC_GAIN[i]
       esrf_io(ADC_DEV[i],"DevSetChanGain",inp)
    }
  } else {
    inp[0]=$2+0; inp[1]=$3+0
    dev = "$1" ; esrf_io(dev,"DevSetChanGain",inp)
  }
}   
'

#adcminmax [dev chan min max]
def adcminmax '{  
  local i inp
  if (!$#) {
    for (i=0;i<ADC_NOCHANNEL;i++) {
      inp[0]=ADC_CHA[i]; inp[1]=ADC_MINV[i] ; inp[2]=ADC_MAXV[i]
      esrf_io(ADC_DEV[i],"DevSetMinMax",inp)
    }
  } else {
    inp[0]=$2+0; inp[1]=$3+0; inp[2]=$4+0
    dev = "$1" ; esrf_io(dev,"DevSetMinMax",inp)
  }
}   
'

#%MACROS%
#%IMACROS%
#%ATTENTION% 
#%UL%
#%LI% The macros are written for the new version of the adc device
# server. There are still some beam lines where this version is not 
# installed yet. You will get some error messages in the init phase,
# but should otherwise have no problems
#%LI% Due to the way the adc is read during counting, you not only
# need a timer configured, but you might have problems with pseudo
# counters which block spec during counting (like the canberrra).
#  
#%LI% The gain can just be changed when the adc is stopped. You will
#    get an error message from the server otherwise. 
#%LI% The result from adcmread is in mV, the result from adcread in Volt.
#%LI% The ADC is setup to read at the moment you asked it for ( Triggered
#   reading) not continiously
#%XUL% 
#%DEPENDENCIES%
#%UL% 
#%LI% 
# The file adc.mac has to be read in                !done by: startup script
#    (this file needs: stchanges.mac cpseudo.mac) 
#%LI% Some of the parameters for the adc have to be set 
# !done by: startup script
#    with adcsetup 
#%XUL%
#%AUTHOR%
#  ADC.MAC Jorg Klora 15.2.93
#%TOC%