esrf

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

#%TITLE% can9660.mac
#%NAME% 
#  Simple macros to control the "digital" DSP Canberra 9660.
#%CATEGORY% Detection, MCA
#%DESCRIPTION% 
#  Simple macros to control the "digital" DSP Canberra 9660.
#%BR% 
#%BR%
# All addresses and ID are explained in the document %LINK% http://www.canberra.com 
# Software Design
# Document for 2060/9660 Module %LINK% by Canberra Industries, Inc.
#
# registers really used are: 
#%PRE%
# 1 6 7 8 e f 10 11 12 13 14 15 16 17 18 19 1a
# 1b 1c 1d 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2f 30 31 32 33 34 35
# 36 37 38 39 3a 3b 3c 3d 3e 3f 46 54 55 56 57 58 
#%PRE%
# so unused ones are: 
#%PRE%
# 2 3 4 5 9 a b c d e f 2e 40 41 42 43 44 45 47 to 53
#%PRE%
#
#%BR% %BR%
# The macro name prefix AS refers to Andrea DAROCZYNE SOMOGYI
#%BR% %BR%
# Modifications history:
#%DL%
# %DT%Jan 2002   %DD% HW Creation
# %DT%Feb 2002   %DD% HW better input, saver calculations and more possibilities
# %DT%Feb 2002   %DD% HW more options
# %DT%2002/02/19 %DD% HW add save and restore configuration using files.
#                   use can9660save_config and can9660restore_config.
# %DT%2003/07    %DD% HW add Stabilizer setup.
# %DT%2003/11    %DD% HW change CAN9660 array structure, make the save and restore 
#                    stuff work. Bugfix of the AS_CAN9660_calc_gain() macro. 
#                    Values are now user friendly, they are not entered as
#                    integer, but in whatever their unit is. 
#%XDL%
#%LOG%
#   $Revision: 2.3 $
#%END%

#------------------------------------------------------------------------------
#%UU%
#%MDESC% menu macro to set some parameters in Canberra 9660 DSP
def can9660menu '{
  if (!(whatis("CAN9660") & 0x01000000)) {
    printf ("Please run can9660setup first!")
    exit
  }
  global MENU_OPTION
  MENU_OPTION["holger"] = ""
  # check for old and new version CAN9660 array
  if (CAN9660["version"] != 2) {
    local icb_addr, device
    device = CAN9660["dev"]
    icb_addr = CAN9660["icb_adr"]
    unglobal CAN9660  # delete the old associative array
    global CAN9660
    CAN9660["version"] = 2 # and put sth so it will work next time.
    CAN9660["dev"] = device
    CAN9660["icb_adr"] = icb_addr
    AS_CAN9660_preset()
  }
#	AS_CAN9660_read_interesting()  # is now done menu by menu
  AS_CAN9660_menu_helper()
}'

#-----------------------------------------------------------
#%IU%
#%MDESC% no description, needed as exitmacro value to the menu() function
def AS_CAN9660_menu_helper() '{
  menu(sprintf("%60s", "Canberra 9660 setup (Bliss/Holger)"), \
    "AS_CAN9660_display()", "", "")
}' # Ends macro AS_CAN9660_menu_helper


#------------------------------------------------------------------------------
#%IU%()
#%MDESC% menu driven macro function called by can9660menu. Don't use this
#macro directly.
def AS_CAN9660_display() '{
  
  if(!CAN9660["read for gain"]) {
    printf("   Reading parameters from DSP")
    CAN9660["AMP_CGAIN"]     = CAN9660["AMP_CGAIN"]["old"]  = \
    AS_CAN9660_readtableval(CAN9660["AMP_CGAIN"]["addr"])
    CAN9660["AMP_FGAIN"]     = CAN9660["AMP_FGAIN"]["old"]  = \
    AS_CAN9660_readtableval(CAN9660["AMP_FGAIN"]["addr"])  * 1.6  / 0x1000
    CAN9660["AMP_SFGAIN"]    = CAN9660["AMP_SFGAIN"]["old"] = \
    AS_CAN9660_readtableval(CAN9660["AMP_SFGAIN"]["addr"]) * 0.03 / 0x1000
    CAN9660["COINC"]         = CAN9660["COINC"]["old"]      = \
    AS_CAN9660_readtableval(CAN9660["COINC"]["addr"])
    CAN9660["read for gain"] = 1
    print
  }
  CAN9660["AMP_GAIN"]      = CAN9660["AMP_GAIN"]["old"]   = \
    AS_CAN9660_calc_gain(CAN9660["AMP_CGAIN"], CAN9660["AMP_FGAIN"], \
    CAN9660["AMP_SFGAIN"])  
  # calculate current gain
  CAN9660["CUR_GAIN"]      = CAN9660["AMP_GAIN"]                               
  if(CAN9660["warn"])
    menuwarning("New values need writing to the controller!")
  #---------------------------------------------------------------
  menuprint(-30, "\n", "Current gain:", CAN9660["CUR_GAIN"])

  menuoptbutton(0, "\n","Gain Setup Menu")
  menuaction ("menu(sprintf(\"%60s\", \"Canberra 9660 gain setup\"), \
    \"AS_CAN9660_gain_display()\", \"\", \"AS_CAN9660_menu_helper()\")")

  menuoptbutton(0, "\n","Fast Discriminator Setup Menu")
  menuaction ("menu(sprintf(\"%60s\", \"Canberra 9660 Fast Discriminator setup\"), \
    \"AS_CAN9660_fdisc_display()\", \"\", \"AS_CAN9660_menu_helper()\")")

  menuoptbutton(0, "\n","ADC Setup Menu")
  menuaction ("menu(sprintf(\"%60s\", \"Canberra 9660 ADC setup\"), \
    \"AS_CAN9660_ADC_display()\", \"\", \"AS_CAN9660_menu_helper()\")")

  menuoptbutton(0, "\n","Filter Setup Menu")
  menuaction ("menu(sprintf(\"%60s\", \"Canberra 9660 filter setup\"), \
    \"AS_CAN9660_filter_display()\", \"\", \"AS_CAN9660_menu_helper()\")")

  menuoptbutton(0, "\n","Stabilizer Setup Menu")
  menuaction ("menu(sprintf(\"%60s\", \"Canberra 9660 Stabilizer setup\"), \
    \"AS_CAN9660_Stabilizer_display1()\", \"\", \"AS_CAN9660_menu_helper()\")")

  menuoptbutton(0, "\n","Stabilizer Reset")
  menuaction ("AS_CAN9660_Stabilizer_reset()")

  if(CAN9660["warn"]) {
    menuprb(MENU_SEP)
    menuoptbutton(0, "\n","Write current values to the DSP", "W")
    menuaction ("print \"Writing to the DSP\";AS_CAN9660_write_interesting(); exit")
  }
}'

#-----------------------------------------------------------
#%IU%
#%MDESC% no description
def AS_CAN9660_gain_helper1() '{
  local gstring, n
  local float array tab[3]

  if (CAN9660["AMP_GAIN"] != CAN9660["AMP_GAIN"]["old"]) {
    tab = AS_CAN9660_find_gain(CAN9660["AMP_GAIN"])
    AS_CAN9660_info tab
    CAN9660["AMP_CGAIN"]  = tab[0]
    AS_CAN9660_info CAN9660["AMP_CGAIN"]
    CAN9660["AMP_FGAIN"]  = tab[1]
    AS_CAN9660_info CAN9660["AMP_FGAIN"]
    CAN9660["AMP_SFGAIN"] = tab[2]
    AS_CAN9660_info sprintf("gain parts is %d %f %f\n", \
      CAN9660["AMP_CGAIN"], CAN9660["AMP_FGAIN"], CAN9660["AMP_SFGAIN"])
    CAN9660["AMP_GAIN"]["old"] = CAN9660["AMP_GAIN"]
    CAN9660["warn"] = 1
  }
}' # Ends macro AS_CAN9660_gain_helper1

#-----------------------------------------------------------
#%IU%
#%MDESC% no description
def AS_CAN9660_gain_helper2() '{
  if (CAN9660["AMP_FGAIN"] < 0.4)   CAN9660["AMP_FGAIN"] = 0.4
  if (CAN9660["AMP_FGAIN"] > 1.6)   CAN9660["AMP_FGAIN"] = 1.6
  if (CAN9660["AMP_SFGAIN"] > 0.03) CAN9660["AMP_SFGAIN"] = 0.03
  if (CAN9660["AMP_SFGAIN"] < 0)    CAN9660["AMP_SFGAIN"] = 0
    CAN9660["AMP_GAIN"] = AS_CAN9660_calc_gain(CAN9660["AMP_CGAIN"], \
        CAN9660["AMP_FGAIN"], CAN9660["AMP_SFGAIN"])
}' # Ends macro AS_CAN9660_gain_helper2


#------------------------------------------------------------------------------
#%IU%()
#%MDESC% menu driven macro function called by can9660menu. Don't use this
#macro directly.
def AS_CAN9660_gain_display() '{
  local gstring, n, i, tab[]
  # CAN9660["AMP_CGAIN"] must contain 0 to 5
  # Next if is for keyboard entries on any value 

  if(!CAN9660["read for gain"]) {
    printf("   Reading parameters from DSP")
    CAN9660["AMP_CGAIN"]     = CAN9660["AMP_CGAIN"]["old"]  = \
      AS_CAN9660_readtableval(CAN9660["AMP_CGAIN"]["addr"])
    CAN9660["AMP_FGAIN"]     = CAN9660["AMP_FGAIN"]["old"]  = \
      AS_CAN9660_readtableval(CAN9660["AMP_FGAIN"]["addr"])  * 1.6  / 0x1000
    CAN9660["AMP_SFGAIN"]    = CAN9660["AMP_SFGAIN"]["old"] = \
      AS_CAN9660_readtableval(CAN9660["AMP_SFGAIN"]["addr"]) * 0.03 / 0x1000 
    CAN9660["COINC"]         = CAN9660["COINC"]["old"]      = \
      AS_CAN9660_readtableval(CAN9660["COINC"]["addr"])  
    CAN9660["read for gain"] = 1
    print
  }
  if(CAN9660["warn"])
    menuwarning("New values need writing to the controller!")

  CAN9660["AMP_GAIN"]      = CAN9660["AMP_GAIN"]["old"]   = \
    AS_CAN9660_calc_gain(CAN9660["AMP_CGAIN"], CAN9660["AMP_FGAIN"], \
    CAN9660["AMP_SFGAIN"])  
  # calculate current gain
  CAN9660["CUR_GAIN"]      = CAN9660["AMP_GAIN"]                               

  for (i = 0; i < 6; i++) {
    helpvar[i] = int(CAN9660["G"][i])
  }

  menuoptgroupselect1(0,"\n","   Change Amplifier Coarse Gain:", helpvar, \
    CAN9660["AMP_CGAIN"],6,"CAN9660[\"AMP_CGAIN\"]",4)

  # that are 6 options from 1 to 6. only add actions to those options
  for (i=MENU_OPT-5; i<=MENU_OPT; i++) # this is needed as we need to calculate the overall gain.
    MENU_ACTION[i]=sprintf("%s CAN9660[\"warn\"] = 1;AS_CAN9660_gain_helper2();", MENU_ACTION[i])

  print 
  menuoptval(0,"\n","Enter Amplifier Fine Gain", \
    CAN9660["AMP_FGAIN"])
  menuvargetv ("CAN9660[\"AMP_FGAIN\"]")
  menuaction("CAN9660[\"warn\"] = 1;AS_CAN9660_gain_helper2()")

  menuoptval(0,"\n","Enter Amplifier Super Fine Gain", \
    CAN9660["AMP_SFGAIN"])
  menuvargetv ("CAN9660[\"AMP_SFGAIN\"]")
  menuaction("CAN9660[\"warn\"] = 1;AS_CAN9660_gain_helper2()")

  menuprint(72, "\n", "   Calculated GAIN value", CAN9660["AMP_GAIN"])

  menuoptval(0,"\n","Enter Amplifier Gain Value", \
    CAN9660["AMP_GAIN"])
  menuvargetv ("CAN9660[\"AMP_GAIN\"]")
  menuaction("CAN9660[\"warn\"] = 1;AS_CAN9660_gain_helper1()")
  
  menuoptval(0,"\n","Coincidence Mode: Sets the devices gating mode", \
    CAN9660["COINC"]?"Coinc.":"AntiC.") 
  menuvartogg ("CAN9660[\"COINC\"]")
  menuaction("CAN9660[\"warn\"] = 1")
  #---------------------------------------------------------------
  menuprb(MENU_SEP)

  menuoptbutton(0, "\n","   Write current values to the DSP", "W")
  menuaction ("print \"Writing to the DSP\";AS_CAN9660_write_interesting(); exit")

}'


#------------------------------------------------------------------------------
#%IU%()
#%MDESC% menu driven macro function called by can9660menu. Don't use this
#macro directly.
def AS_CAN9660_fdisc_display() '{
  if(CAN9660["warn"])
    menuwarning("New values need writing to the controller!")
  if(!CAN9660["read for fdisc"]) {
    printf("   Reading parameters from DSP")
    CAN9660["MISC_FDM"]   = CAN9660["MISC_FDM"]["old"]    = \
    AS_CAN9660_readtableval(CAN9660["MISC_FDM"]["addr"])
    CAN9660["MISC_FD"]    = CAN9660["MISC_FD"]["old"]     = \
    AS_CAN9660_readtableval(CAN9660["MISC_FD"]["addr"]) / 10
    CAN9660["MISC_INPP"]  = CAN9660["MISC_INPP"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["MISC_INPP"]["addr"])
    CAN9660["MISC_INHP"]  = CAN9660["MISC_INHP"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["MISC_INHP"]["addr"])
    CAN9660["read for fdisc"] = 1
    print
  }
  menuoptval(3,"\n","Fast Discriminator Mode", \
    CAN9660["MISC_FDM"]?"Manual":"Auto")
  menuvartogg ("CAN9660[\"MISC_FDM\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  if (CAN9660["MISC_FDM"] == 1) {
    menuoptval(3,"%\n","Fast Discriminator Settings", \
      CAN9660["MISC_FD"])
    menuvargetv ("CAN9660[\"MISC_FD\"]")
    menuaction("if (CAN9660[\"MISC_FD\"] < 0) CAN9660[\"MISC_FD\"] = 0;\
      if (CAN9660[\"MISC_FD\"] > 100) CAN9660[\"MISC_FD\"] = 100;\
      CAN9660[\"warn\"] = 1")
  } else {
    menuoptskip (MENU_OPT)
    print
  }

  menuoptval(3,"\n","Input signal polarity", \
    CAN9660["MISC_INPP"]?"Negative":"Positive")
  menuvartogg ("CAN9660[\"MISC_INPP\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  menuoptval(3,"\n","Inhibit signal polarity", \
    CAN9660["MISC_INHP"]?"Negative":"Positive")
  menuvartogg ("CAN9660[\"MISC_INHP\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  #---------------------------------------------------------------
  menuprb(MENU_SEP)

  menuoptbutton(0, "\n","   Write current values to the DSP", "W")
  menuaction ("print \"Writing to the DSP\";AS_CAN9660_write_interesting(); exit")
}'


#------------------------------------------------------------------------------
#%IU%()
#%MDESC% menu driven macro function called by can9660menu. Don't use this
#macro directly.
def AS_CAN9660_ADC_display() '{

  if(CAN9660["warn"])
    menuwarning("New values need writing to the controller!")
  if(!CAN9660["read for ADC"]) {
    printf("   Reading parameters from DSP")
    CAN9660["ADC_LLD"]        = AS_CAN9660_readtableval(CAN9660["ADC_LLD"]["addr"])
      # make this a percentage
    CAN9660["ADC_LLD"]            = CAN9660["ADC_LLD"]["old"]     = CAN9660["ADC_LLD"] * 100 / 0x7FFF
    CAN9660["ADC_CGAIN"]          = CAN9660["ADC_CGAIN"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["ADC_CGAIN"]["addr"]) 
    CAN9660["ADC_OFFSET"]         = CAN9660["ADC_OFFSET"]["old"]  = \
    AS_CAN9660_readtableval(CAN9660["ADC_OFFSET"]["addr"]) * 128
    CAN9660["ADC_ZERO"]           = CAN9660["ADC_ZERO"]["old"]    = \
    (AS_CAN9660_readtableval(CAN9660["ADC_ZERO"]["addr"]) - 3125) / 1000
    CAN9660["read for ADC"]       = 1
    print
  }
  local helpvar
  helpvar[0] = 256; helpvar[1] = 512; helpvar[2] = 1024;  helpvar[3] = 2048
  helpvar[4] = 4096;  helpvar[5] = 8192;  helpvar[6] = 16384
  menuoptgroupselect1(0,"","   Choose ADC Conversion Gain, current:", helpvar, \
    CAN9660["ADC_CGAIN"],7,"CAN9660[\"ADC_CGAIN\"]",4) 
  # that are 3 options 
  for (i=MENU_OPT-6; i<=MENU_OPT; i++) # this is needed as we need to calculate the overall gain.
    MENU_ACTION[i]=sprintf("%s CAN9660[\"warn\"] = 1;", MENU_ACTION[i])
  print "\n\n"

  print "   The ADC Digital Offset in 128 channel increments."
  menuoptval(0,"\n","Choose Digital Offset value", \
    CAN9660["ADC_OFFSET"])
  menuvargetv ("CAN9660[\"ADC_OFFSET\"]")
  menuaction("if (CAN9660[\"ADC_OFFSET\"] < 0) CAN9660[\"ADC_OFFSET\"] = 0;\
    if (CAN9660[\"ADC_OFFSET\"] > 16126) CAN9660[\"ADC_OFFSET\"] = 16126;\
    CAN9660[\"warn\"] = 1")

  print
  print "   The ADC Low Level Discriminator can be chosen as a"
  local groupsize
  groupsize = mca_par("group_size")
  menuprint(0,"%\n", sprintf("   percentage of the of the device\'s full scale\
 (%d).", groupsize), CAN9660["ADC_LLD"])
  menuoptval(3,"%\n","Choose Low Level Discriminator Value", \
    CAN9660["ADC_LLD"])
  menuvargetv ("CAN9660[\"ADC_LLD\"]")
  menuaction("if (CAN9660[\"ADC_LLD\"] < 0) CAN9660[\"ADC_LLD\"] = 0;\
    if (CAN9660[\"ADC_LLD\"] > 100) CAN9660[\"ADC_LLD\"] = 100;\
    CAN9660[\"warn\"] = 1")
  print

  print "   The ADC Zero DAC value can be chosen between -3.125% to +3.125%."
  menuoptval(3,"\n","Choose ADC Zero DAC Value", CAN9660["ADC_ZERO"])
  menuvargetv ("CAN9660[\"ADC_ZERO\"]")
  menuaction("if (CAN9660[\"ADC_ZERO\"] < -3.125) CAN9660[\"ADC_ZERO\"] = -3.125;\
    if (CAN9660[\"ADC_ZERO\"] > 3.125) CAN9660[\"ADC_ZERO\"] = 3.125;\
    CAN9660[\"warn\"] = 1")
  #---------------------------------------------------------------
  menuprb(MENU_SEP)
  menuoptbutton(0, "\n","   Write current values to the DSP", "W")
  menuaction ("print \"Writing to the DSP\";AS_CAN9660_write_interesting(); exit")
}'


#------------------------------------------------------------------------------
#%IU%()
#%MDESC% menu driven macro function called by can9660menu. Don't use this
#macro directly.
def AS_CAN9660_filter_display() '{
  # CAN9660["FILTER_FT"] is the console entry value, multiply with 10 at write
  # time.
  if(CAN9660["warn"])
    menuwarning("New values need writing to the controller!")
  if(!CAN9660["read for filter"]) {
    printf("   Reading parameters from DSP")
    CAN9660["FILTER_FT"]        = \
    AS_CAN9660_readtableval(CAN9660["FILTER_FT"]["addr"]) / 10
    CAN9660["FILTER_FT"]["old"] = CAN9660["FILTER_FT"]
    # Rise time related stuff
    CAN9660["FILTER_RT"]        = \
    AS_CAN9660_readtableval(CAN9660["FILTER_RT"]["addr"]) / 10
    CAN9660["FILTER_RT"]["old"] = CAN9660["FILTER_RT"]
    CAN9660["FILTER_PZ"]        = \
    AS_CAN9660_readtableval(CAN9660["FILTER_PZ"]["addr"])
    if (CAN9660["FILTER_PZ"] != 0) {
      CAN9660["FILTER_PZ"] = CAN9660["FILTER_PZ"] * 40 / 0xfff + 1.7
    }
    CAN9660["FILTER_PZ"]["old"]  = CAN9660["FILTER_PZ"]
    CAN9660["FILTER_PZM"]        = \
    AS_CAN9660_readtableval(CAN9660["FILTER_PZM"]["addr"])
    CAN9660["FILTER_PZM"]["old"] = CAN9660["FILTER_PZM"]
    CAN9660["read for filter"]   = 1
    print
  }  
  print "   The Flat Top Time can be chosen between 0 to 3 usec."
  menuoptval(0,"usec\n","Choose Flat Top Time Value", \
    CAN9660["FILTER_FT"])
  menuvargetv ("CAN9660[\"FILTER_FT\"]")
  menuaction("if (CAN9660[\"FILTER_FT\"] > 3) CAN9660[\"FILTER_FT\"] = 3; \
    if (CAN9660[\"FILTER_FT\"] < 0) CAN9660[\"FILTER_FT\"] = 0; \
    CAN9660[\"warn\"] = 1")
  print
  print "   The Rise Time can be chosen between 0.4 to 28 usec."
  menuoptval(0,"usec\n","Choose Rise Time Value", \
    CAN9660["FILTER_RT"])
  menuvargetv ("CAN9660[\"FILTER_RT\"]")
  menuaction("if (CAN9660[\"FILTER_RT\"] > 28) CAN9660[\"FILTER_RT\"] = 28; \
    if (CAN9660[\"FILTER_RT\"] < 0.4) CAN9660[\"FILTER_RT\"] = 0.4; \
    CAN9660[\"warn\"] = 1")
  print 
  local helpvar
  helpvar[0] = "RC Auto"; helpvar[1] = "RC Manual"; helpvar[2] = "TRP"
  menuoptgroupselect1(0,"","   Preamp type:", helpvar, \
    CAN9660["FILTER_PZM"],3,"CAN9660[\"FILTER_PZM\"]",4)
  # that are 3 options 
  for (i=MENU_OPT-2; i<=MENU_OPT; i++) # this is needed as we need to calculate the overall gain.
    MENU_ACTION[i]=sprintf("%s CAN9660[\"warn\"] = 1;", MENU_ACTION[i])
  print "\n\n"

  if (CAN9660["FILTER_PZM"] == 1) {
    print "   The Pole Zero DAC value can be chosen between 1.7 to 40 usec. 0 is off."
    menuoptval(0,"usec\n","Choose Pole Zero DAC Value", \
      CAN9660["FILTER_PZ"]?CAN9660["FILTER_PZ"]:"off")
    menuvargetv ("CAN9660[\"FILTER_PZ\"]")
    menuaction("if (CAN9660[\"FILTER_PZ\"] != 0) { if (CAN9660[\"FILTER_PZ\"] > 40) CAN9660[\"FILTER_PZ\"] = 40; \
      if (CAN9660[\"FILTER_PZ\"] < 1.7) CAN9660[\"FILTER_PZ\"] = 1.7}; \
      CAN9660[\"warn\"] = 1")
  } else print "\n\n"
  #---------------------------------------------------------------
  menuprb(MENU_SEP)
  menuoptbutton(0, "\n","   Write current values to the DSP", "W")
  menuaction("print \"Writing to the DSP\";AS_CAN9660_write_interesting();exit")
}'


#------------------------------------------------------------------------------
#%IU%()
#%MDESC% menu driven macro function called by can9660menu. Don't use this
#macro directly.
def AS_CAN9660_Stabilizer_display1() '{

  if(CAN9660["warn"])
    menuwarning("New values need writing to the controller!")
  if(!CAN9660["read for stabilizer1"]) {
    printf("   Reading parameters from DSP")
    CAN9660["STABLZ_GCOR"] = AS_CAN9660["STABLZ_GCOR"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_GCOR"]["addr"])
    CAN9660["STABLZ_ZCOR"] = AS_CAN9660["STABLZ_ZCOR"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_ZCOR"]["addr"])
    CAN9660["STABLZ_GMOD"] = AS_CAN9660["STABLZ_GMOD"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_GMOD"]["addr"])
    CAN9660["STABLZ_GOVR"] = CAN9660["STABLZ_GMOD"] & 0x80
    CAN9660["STABLZ_GOVF"] = CAN9660["STABLZ_GMOD"] & 0x40
    CAN9660["STABLZ_GMOD"] = CAN9660["STABLZ_GMOD"] & 0x03
    CAN9660["STABLZ_ZMOD"] = AS_CAN9660["STABLZ_ZMOD"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_ZMOD"]["addr"])
    CAN9660["STABLZ_ZOVR"] = CAN9660["STABLZ_ZMOD"] & 0x80
    CAN9660["STABLZ_ZOVF"] = CAN9660["STABLZ_ZMOD"] & 0x40
    CAN9660["STABLZ_ZMOD"] = CAN9660["STABLZ_ZMOD"] & 0x03
    CAN9660["STABLZ_GDIV"] = AS_CAN9660["STABLZ_GDIV"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_GDIV"]["addr"])
    CAN9660["STABLZ_ZDIV"] = AS_CAN9660["STABLZ_ZDIV"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_ZDIV"]["addr"])
    CAN9660["STABLZ_GSPC"] = AS_CAN9660["STABLZ_GSPC"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_GSPC"]["addr"])
    CAN9660["STABLZ_ZSPC"] = AS_CAN9660["STABLZ_ZSPC"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_ZSPC"]["addr"])
    CAN9660["read for stabilizer1"] = 1
    print
  }

  menuoptval(0,"\n","Stabilizer Gain Correction Range: Ge (+/- 1%), Nal (+/- 10%)", \
    CAN9660["STABLZ_GCOR"]?"Nal":"Ge")
  menuvartogg ("CAN9660[\"STABLZ_GCOR\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  menuoptval(0,"\n","Stabilizer Zero Correction Range: Ge (+/- 1%), Nal (+/- 10%)", \
    CAN9660["STABLZ_ZCOR"]?"Nal":"Ge")
  menuvartogg ("CAN9660[\"STABLZ_ZCOR\"]")
  menuaction("CAN9660[\"warn\"] = 1")
  print

  local helpvar
  helpvar[0] = " OFF"; helpvar[1] = "ON"; helpvar[2] = "HOLD"
  menuoptgroupselect1(0,"","   Gain Correction Mode:", helpvar, \
    CAN9660["STABLZ_GMOD"],3,"CAN9660[\"STABLZ_GMOD\"]",4)
  # that are 3 options 
  for (i=MENU_OPT-2; i<=MENU_OPT; i++) # this is needed as we need to calculate the overall gain.
    MENU_ACTION[i]=sprintf("%s CAN9660[\"warn\"] = 1;", MENU_ACTION[i])
  print "\n"

  local helpvar
  helpvar[0] = " OFF"; helpvar[1] = "ON"; helpvar[2] = "HOLD"
  menuoptgroupselect1(0,"","   Zero Correction Mode:", helpvar, \
    CAN9660["STABLZ_ZMOD"],3,"CAN9660[\"STABLZ_ZMOD\"]",4)
  # that are 3 options 
  for (i=MENU_OPT-2; i<=MENU_OPT; i++) # this is needed as we need to calculate the overall gain.
    MENU_ACTION[i]=sprintf("%s CAN9660[\"warn\"] = 1;", MENU_ACTION[i])
  print "\n\n"

  if (CAN9660["STABLZ_GOVR"]) {
    menuwarning("Stabilizers gain range is in OverRange condition!")
    menuwarning("Gain mode was switched to HOLD!")
  }
  if (CAN9660["STABLZ_ZOVR"]) {
    menuwarning("Stabilizers zero correction range is in OverRange condition!")
    menuwarning("Zero correction mode was switched to HOLD!")
  }
  if (CAN9660["STABLZ_GOVF"]) {
    menuwarning("Stabilizers gain registers are in OverFlow condition,")
    menuwarning(" due to excessibley high input rate or window selection!")
  }
  if (CAN9660["STABLZ_ZOVF"]) {
    menuwarning("Stabilizers zero correction registers are in OverFlow condition,")
    menuwarning(" due to excessibley high input rate or window selection!")
  }

	printf("   %s\n","Gain and Zero Correction Divider: expressed as 2^N, ")
	printf("   %s\n","where N = 0 through 9, representing a divider factor of 1 through 512")
  menuoptval(9,"\n","Choose for Gain Correction", CAN9660["STABLZ_GDIV"])
  menuvargetv ("CAN9660[\"STABLZ_GDIV\"]")
  menuaction("CAN9660[\"warn\"] = 1")
  
  menuoptval(0,"\n","Choose for Zero correction", CAN9660["STABLZ_ZDIV"])
  menuvargetv ("CAN9660[\"STABLZ_ZDIV\"]")
  menuaction("CAN9660[\"warn\"] = 1")
  print

	printf("   %s\n","Gain and Zero Spacing expressed as 2 through 512 channels")
  menuoptval(0,"\n","Choose for Gain Spacing", CAN9660["STABLZ_GSPC"])
  menuvargetv ("CAN9660[\"STABLZ_GSPC\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  menuoptval(0,"\n","Choose for Zero Spacing", CAN9660["STABLZ_ZSPC"])
  menuvargetv ("CAN9660[\"STABLZ_ZSPC\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  #---------------------------------------------------------------
  menuprb(MENU_SEP)
  menuoptbutton(70, "\n","Continue to the next page!", "C")
  menuaction ("menu(sprintf(\"%60s\", \"Canberra 9660 Stabilizer setup\"), \
    \"AS_CAN9660_Stabilizer_display2()\", \"\", \"AS_CAN9660_menu_helper()\")")
  if(CAN9660["warn"]) {
    menuprb(MENU_SEP)
    menuoptbutton(70, "\n","Write current values to the DSP", "W")
    menuaction ("print \"Writing to the DSP\";AS_CAN9660_write_interesting(); exit")
  }
}'

#------------------------------------------------------------------------------
#%IU%()
#%MDESC% menu driven macro function called by can9660menu. Don't use this
#macro directly.
def AS_CAN9660_Stabilizer_display2() '{
  if(CAN9660["warn"])
    menuwarning("New values need writing to the controller!")
  if(!CAN9660["read for stabilizer2"]) {
    printf("   Reading parameters from DSP")
    CAN9660["STABLZ_GWIN"]       = AS_CAN9660["STABLZ_GWIN"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_GWIN"]["addr"])
    CAN9660["STABLZ_ZWIN"]       = AS_CAN9660["STABLZ_ZWIN"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_ZWIN"]["addr"])
    CAN9660["STABLZ_GCENT"]      = AS_CAN9660["STABLZ_GCENT"]["old"]  = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_GCENT"]["addr"])
    CAN9660["STABLZ_ZCENT"]      = AS_CAN9660["STABLZ_ZCENT"]["old"]  = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_ZCENT"]["addr"])
    CAN9660["STABLZ_GRAT"]       = AS_CAN9660["STABLZ_GRAT"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_GRAT"]["addr"])
    CAN9660["STABLZ_ZRAT"]       = AS_CAN9660["STABLZ_ZRAT"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_ZRAT"]["addr"])
    CAN9660["STABLZ_GAIN"]       = AS_CAN9660["STABLZ_GAIN"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_GAIN"]["addr"])
    CAN9660["STABLZ_ZERO"]       = AS_CAN9660["STABLZ_ZERO"]["old"]   = \
    AS_CAN9660_readtableval(CAN9660["STABLZ_ZERO"]["addr"])
    CAN9660["read for stabilizer2"] = 1
    print
  }

	printf("   %s\n","Gain and Zero Window expressed as 1 through 128 channels")
  menuoptval(0,"\n","Choose Gain Window", CAN9660["STABLZ_GWIN"])
  menuvargetv ("CAN9660[\"STABLZ_GWIN\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  menuoptval(0,"\n","Choose Zero Window", CAN9660["STABLZ_ZWIN"])
  menuvargetv ("CAN9660[\"STABLZ_ZWIN\"]")
  menuaction("CAN9660[\"warn\"] = 1")

	printf("   %s\n","Gain and Zero Centroid, ranging from 10 to 16376")
  menuoptval(0,"\n","Choose Gain Centroid", CAN9660["STABLZ_GCENT"])
  menuvargetv ("CAN9660[\"STABLZ_GCENT\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  menuoptval(0,"\n","Choose Zero Centroid", CAN9660["STABLZ_ZCENT"])
  menuaction("CAN9660[\"warn\"] = 1")

	printf("   %s\n","Gain and Zero Ratio Value. Ratio between upper and lower window counts,")
	printf("   %s\n","that stabilizer will attmept to maintain. Value will range from 1 to 10000,")
	printf("   %s\n","representing a ratio value of 0.01 to 100.00. Default will be at 100 for")
  printf("   %s\n","1.00. The ratio is caluculated from the upper and lower windows as follows:")
  printf("   %s\n","      ratio = upper / lower * 100")
  menuoptval(0,"\n","Choose Gain Ratio Value", CAN9660["STABLZ_GRAT"])
  menuvargetv ("CAN9660[\"STABLZ_GRAT\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  menuoptval(0,"\n","Choose Zero Ratio Value", CAN9660["STABLZ_ZRAT"])
  menuvargetv ("CAN9660[\"STABLZ_ZRAT\"]")
  menuaction("CAN9660[\"warn\"] = 1")

	printf("   %s\n","Actual Gain and Zero correction values")
  menuoptval(0,"\n","Choose Gain Correction Value (0 to 1FFFh)", CAN9660["STABLZ_GAIN"])
  menuvargetv ("CAN9660[\"STABLZ_GAIN\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  menuoptval(0,"\n","Choose Zero Correction Value (0 to FFFh", CAN9660["STABLZ_ZERO"])
  menuvargetv ("CAN9660[\"STABLZ_ZERO\"]")
  menuaction("CAN9660[\"warn\"] = 1")

  #---------------------------------------------------------------
  if(CAN9660["warn"]) {
    menuprb(MENU_SEP)
    menuoptbutton(70, "\n","Write current values to the DSP", "W")
    menuaction ("print \"Writing to the DSP\";AS_CAN9660_write_interesting(); exit")
  }

}'

#------------------------------------------------------------------------------
#%IU%()
#%MDESC% menu driven macro function called by can9660menu. Don't use this
#macro directly.
def AS_CAN9660_Stabilizer_reset() '{
  AS_CAN9660_writetableval(0x23, 1) # write 1 into, write only register
# AS_CAN9660_read_interesting()
}'

#-----------------------------------------------------------
#%IU%(gain)
#%MDESC% tries to find the closest settings for DSP to have the desired
# gain. This macro function returns a string with the three values
# cgain, fgain and sfgain. Please see description of macro
# AS_CAN9660_calc_gain() for detailed explaination on how to calculate
#the gain.
def AS_CAN9660_find_gain(gain) '{
  local i, cgain, fgain, sfgain
  local float array tab[3]
  for (i = 0; i <= 5; i ++) {
    if (gain < CAN9660["G"][i] * 1.6) {
      tab[0] = i
      AS_CAN9660_info sprintf("setting i %d, %d", i, CAN9660["G"][tab[0]])
      break
    }
  }
  AS_CAN9660_info sprintf("setting tab[0] to: %d\n", tab[0])
  tab[1] = gain / CAN9660["G"][tab[0]]

  AS_CAN9660_info sprintf("fgain %f", tab[1])
  if(tab[1] < 0.4 ) tab[1] = 0.4
  tab[1] = int(tab[1] * 0x1000 / 1.6) # calculate the value written to
  # the controller to use its possible precision, otherwise
  # the super fine gain will always be 0
  AS_CAN9660_info sprintf("fgain 0x%04x", tab[1])

  tab[2] = gain - CAN9660["G"][tab[0]] * (tab[1] * 1.6 / 0x1000)
  # now we have the difference between calculated and desired value.
  tab[2] = tab[2] / CAN9660["G"][tab[0]] # now the factor
  AS_CAN9660_info sprintf("sfgain %f", tab[2])
  if (tab[2] < 0) tab[2] = 0; if (tab[2] > 0.03) tab[2] = 0.03
  tab[2] = int(tab[2] * 0x1000 / 0.03) # calculate the value written to
  # the controller to use its possible precision
  
  AS_CAN9660_info sprintf("sfgain 0x%04x", tab[2])
  tab[1] = tab[1] * 1.6 / 0x1000 # and return trip
  tab[2] = tab[2] * 0.03 / 0x1000  # and return trip

  AS_CAN9660_info AS_CAN9660_calc_gain(tab[0], tab[1], tab[2])
  AS_CAN9660_info sprintf("returning array: %d %f %f", tab[0], tab[1], tab[2])
  return tab[]
}' # Ends macro AS_find_gain

#-----------------------------------------------------------
#%IU%(cgain, fgain, sfgain)
#%MDESC% calculates the real gain out of coarse, fine and super fine
# gain. This macro function returns the calculated gain.
#%BR%
# %BR%
# Formula: Gain = Coarse Gain * (Fine Gain + Super Fine Gain)
#%BR%
#Coarse Gain can be: 5, 15, 40, 120, 330, 960
#%BR%
#Fine gain is 0x100 to 0xfff representing *0.4 to *1.6
#%BR%
#Super fine gain is 0x000 to 0xfff representing 0 to 0.03
def AS_CAN9660_calc_gain(cgain, fgain, sfgain) '{
  local gain
  gain =   CAN9660["G"][cgain] * (fgain + sfgain)
  AS_CAN9660_info "AS_CAN9660_calc_gain", CAN9660["G"][cgain], fgain, sfgain, (fgain + sfgain), gain
  return gain
}' # Ends macro AS_CAN9660_calc_gain
def AS_CAN9660_int_calc_gain(cgain, fgain, sfgain) '{
  local gain
  p fgain
  fgain = fgain * 1.6 / 0x1000
  p fgain
  p sfgain
  sfgain = sfgain * 0.03 / 0x1000
  p sfgain
  gain =   CAN9660["G"][cgain] * (fgain + sfgain)
  return gain
}' # Ends macro AS_CAN9660_calc_gain

#-----------------------------------------------------------
#%UU% <mca device name> <icb address>
#%MDESC% Sets up a canberra 9660 DSP on ICB address <icb address>. The MCA
#device name is taken as unit 0 from SPEC.
def can9660setup '{
  local i, tmp
  if (!(whatis("CAN9660") & 0x01000000)) {
    printf ("\nFirst time running $0 !\n")
    global CAN9660
    if (CAN9660["config_dir"] == "") 
      CAN9660["config_dir"] = BLISSADM"/local/spec/userconf/dsp"
    CAN9660["version"] = 2
    AS_CAN9660_preset()
  }
  # check for old and new version CAN9660 array
  if (CAN9660["version"] != 2) {
    unglobal CAN9660  # delete the old associative array
    global CAN9660
    CAN9660["version"] = 2 # and put sth so it will work next time.
    AS_CAN9660_preset()
  }
  if ($# >= 1) {
    CAN9660["dev"] = mca_spar($1, "device_id")
    if ( tmp == "?" ) {
      eprint "Sorry, no MCA on number", $1, "available!"
      delete CAN9660["dev"]
      exit
    }
  } else {
    for (i=0; i < MCAS; i ++) {
      tmp =  mca_spar(i, "device_id")
      if (tmp != "?")
        if (yesno(sprintf("Do you want to use MCA %s", tmp),1)) {
          CAN9660["dev"] = tmp
          break
        }
    }
    if ( i == MCAS || CAN9660["dev"] == 0 ) {
      eprint "Sorry, no MCA available or none chosen!"
      delete CAN9660["dev"]
      exit
    }
  }

  print "\n\nUsing MCA device", CAN9660["dev"], "to talk to DSP9660"
  if ($# >= 2) {
    CAN9660["icb_adr"] = $2
  } else {
    CAN9660["icb_adr"] = getval("ICB address of DSP module",CAN9660["icb_adr"])
  }
  
  CAN9660["Verbose"]    = 1
  AS_CAN9660_verbose # force definition of info function :-)
  delete CAN9660["read for ADC"]
  delete CAN9660["read for fdisc"]
  delete CAN9660["read for gain"]
  delete CAN9660["read for stabilizer1"]
  delete CAN9660["read for stabilizer2"]
  delete CAN9660["read for filter"]
}'

#-----------------------------------------------------------
#%IU%()
#%MDESC% Don't use this macro directly. Presets the data structure used 
#by the macros for the canberra 9660 DSP
def AS_CAN9660_preset() '{
  # no access to the device here, as device and icb are not defined yet.
  # icb address might be changed in setup.
  if (!(whatis("CAN9660") & 0x04000000)) {
    printf ("Please run can9660setup first")
    exit
  }
  CAN9660["Verbose"]  = 1                          
  CAN9660["warn"]     = 0                          
# CAN9660["dev"]      = mca_par("device_id")       
  CAN9660["icb_adr"]  = 2 # probably on ID18F      
  CAN9660["G"][0] = 5
  CAN9660["G"][1] = 15
  CAN9660["G"][2] = 40
  CAN9660["G"][3] = 120
  CAN9660["G"][4] = 330
  CAN9660["G"][5] = 960
  # put the addresses here
  CAN9660["AMP_CGAIN"]["addr"]    = 1
  CAN9660["AMP_FGAIN"]["addr"]    = 0x2a
  CAN9660["AMP_SFGAIN"]["addr"]   = 0x2b
  CAN9660["COINC"]["addr"]        = 0x11
  CAN9660["MISC_FDM"]["addr"]     = 0x13
  CAN9660["MISC_FD"]["addr"]      = 0x34
  CAN9660["MISC_INPP"]["addr"]    = 0x0e
  CAN9660["MISC_INHP"]["addr"]    = 0x0f
  CAN9660["FILTER_FT"]["addr"]    = 0x36
  CAN9660["FILTER_RT"]["addr"]    = 0x35
  CAN9660["FILTER_PZ"]["addr"]    = 0x07
  CAN9660["FILTER_PZM"]["addr"]   = 0x08
  CAN9660["ADC_LLD"]["addr"]      = 0x30
  CAN9660["ADC_CGAIN"]["addr"]    = 0x31
  CAN9660["ADC_OFFSET"]["addr"]   = 0x33
  CAN9660["ADC_ZERO"]["addr"]     = 0x2c
  CAN9660["STABLZ_GCOR"]["addr"]  = 0x22
  CAN9660["STABLZ_ZCOR"]["addr"]  = 0x2d
  CAN9660["STABLZ_GMOD"]["addr"]  = 0x20
  CAN9660["STABLZ_ZMOD"]["addr"]  = 0x21
  CAN9660["STABLZ_GDIV"]["addr"]  = 0x37
  CAN9660["STABLZ_ZDIV"]["addr"]  = 0x38
  CAN9660["STABLZ_GSPC"]["addr"]  = 0x1c
  CAN9660["STABLZ_ZSPC"]["addr"]  = 0x1d
  CAN9660["STABLZ_GWIN"]["addr"]  = 0x1a
  CAN9660["STABLZ_ZWIN"]["addr"]  = 0x1b
  CAN9660["STABLZ_GCENT"]["addr"] = 0x18
  CAN9660["STABLZ_ZCENT"]["addr"] = 0x19
  CAN9660["STABLZ_GRAT"]["addr"]  = 0x28
  CAN9660["STABLZ_ZRAT"]["addr"]  = 0x29
  CAN9660["STABLZ_GAIN"]["addr"]  = 0x3e
  CAN9660["STABLZ_ZERO"]["addr"]  = 0x3f
  # put the old values found by AS here in the field "init"
  CAN9660["ADC_CGAIN"]["init"]    = 5
  CAN9660["ADC_LLD"]["init"]      = 99
  CAN9660["ADC_OFFSET"]["init"]   = 0
  CAN9660["ADC_ZERO"]["init"]     = 3125
  CAN9660["AMP_CGAIN"]["init"]    = 1
  CAN9660["AMP_FGAIN"]["init"]    = 2583
  CAN9660["AMP_SFGAIN"]["init"]   = 3250
  CAN9660["COINC"]["init"]        = 0
  CAN9660["FILTER_FT"]["init"]    = 8
  CAN9660["FILTER_PZ"]["init"]    = 0
  CAN9660["FILTER_PZM"]["init"]   = 0
  CAN9660["FILTER_RT"]["init"]    = 56
  CAN9660["MISC_FD"]["init"]      = 10
  CAN9660["MISC_FDM"]["init"]     = 0
  CAN9660["MISC_INHP"]["init"]    = 0
  CAN9660["MISC_INPP"]["init"]    = 0
  CAN9660["STABLZ_GAIN"]["init"]  = 4096
  CAN9660["STABLZ_GCENT"]["init"] = 8323
  CAN9660["STABLZ_GCOR"]["init"]  = 0
  CAN9660["STABLZ_GDIV"]["init"]  = 0
  CAN9660["STABLZ_GMOD"]["init"]  = 0
  CAN9660["STABLZ_GRAT"]["init"]  = 100
  CAN9660["STABLZ_GSPC"]["init"]  = 64
  CAN9660["STABLZ_GWIN"]["init"]  = 8
  CAN9660["STABLZ_ZCENT"]["init"] = 512
  CAN9660["STABLZ_ZCOR"]["init"]  = 0
  CAN9660["STABLZ_ZDIV"]["init"]  = 0
  CAN9660["STABLZ_ZERO"]["init"]  = 0
  CAN9660["STABLZ_ZMOD"]["init"]  = 0
  CAN9660["STABLZ_ZRAT"]["init"]  = 100
  CAN9660["STABLZ_ZSPC"]["init"]  = 64
  CAN9660["STABLZ_ZWIN"]["init"]  = 8
}'

#%IU%()
#%MDESC% Writes all the interesting values to the controller. Don't use
#this macro directly. Presets the data structure used by the macros for
#the canberra 9660 DSP 
def AS_CAN9660_write_interesting() '{
  if (!(whatis("CAN9660") & 0x04000000)) {
    printf ("Please run can9660setup first")
    exit
  }
  printf("Writing parameters back to DSP   ")
  # Gain related stuff
  if (CAN9660["AMP_CGAIN"]["old"] != CAN9660["AMP_CGAIN"])
    AS_CAN9660_writetableval(CAN9660["AMP_CGAIN"]["addr"], CAN9660["AMP_CGAIN"])
  if (CAN9660["AMP_FGAIN"]["old"] != CAN9660["AMP_FGAIN"])
    AS_CAN9660_writetableval(CAN9660["AMP_FGAIN"]["addr"], CAN9660["AMP_FGAIN"] * 0x1000 / 1.6)
  if (CAN9660["AMP_SFGAIN"]["old"] != CAN9660["AMP_SFGAIN"])
    AS_CAN9660_writetableval(CAN9660["AMP_SFGAIN"]["addr"], CAN9660["AMP_SFGAIN"] * 0x1000 / 0.03)
  if (CAN9660["COINC"]["old"] != CAN9660["COINC"])
    AS_CAN9660_writetableval(CAN9660["COINC"]["addr"], CAN9660["COINC"])
  # fast discriminator stuff
  if (CAN9660["MISC_FDM"]["old"] != CAN9660["MISC_FDM"])
    AS_CAN9660_writetableval(CAN9660["MISC_FDM"]["addr"], CAN9660["MISC_FDM"])
  if (CAN9660["MISC_FD"]["old"] != CAN9660["MISC_FD"])
    AS_CAN9660_writetableval(CAN9660["MISC_FD"]["addr"], CAN9660["MISC_FD"])
  if (CAN9660["MISC_INPP"]["old"] != CAN9660["MISC_INPP"])
    AS_CAN9660_writetableval(CAN9660["MISC_INPP"]["addr"], CAN9660["MISC_INPP"])
  if (CAN9660["MISC_INHP"]["old"] != CAN9660["MISC_INHP"])
    AS_CAN9660_writetableval(CAN9660["MISC_INHP"]["addr"], CAN9660["MISC_INHP"])
  # Filter related stuff
  if (CAN9660["FILTER_FT"]["old"] != CAN9660["FILTER_FT"])
    AS_CAN9660_writetableval(CAN9660["FILTER_FT"]["addr"], int(CAN9660["FILTER_FT"] * 10))
  if (CAN9660["FILTER_RT"]["old"] != CAN9660["FILTER_RT"])
    AS_CAN9660_writetableval(CAN9660["FILTER_RT"]["addr"], int(CAN9660["FILTER_RT"] * 10))
  if (CAN9660["FILTER_PZM"]["old"] != CAN9660["FILTER_PZM"])
    AS_CAN9660_writetableval(CAN9660["FILTER_PZM"]["addr"], CAN9660["FILTER_PZM"])
  if (CAN9660["FILTER_PZ"]["old"] != CAN9660["FILTER_PZ"]) {
    local x
    if (CAN9660["FILTER_PZ"] != 0) {
      x = int((CAN9660["FILTER_PZ"] - 1.7) * 0xfff / 40)
    } else x = 0
    AS_CAN9660_writetableval(CAN9660["FILTER_PZ"]["addr"], x)
  }
  # ADC related stuff
  if (CAN9660["ADC_LLD"]["old"] != CAN9660["ADC_LLD"])
    AS_CAN9660_writetableval(CAN9660["ADC_LLD"]["addr"], CAN9660["ADC_LLD"] * 0x7FFF / 100)
  if (CAN9660["ADC_CGAIN"]["old"] != CAN9660["ADC_CGAIN"])
    AS_CAN9660_writetableval(CAN9660["ADC_CGAIN"]["addr"], CAN9660["ADC_CGAIN"])
  if (CAN9660["ADC_OFFSET"]["old"] != CAN9660["ADC_OFFSET"])
    AS_CAN9660_writetableval(CAN9660["ADC_OFFSET"]["addr"], CAN9660["ADC_OFFSET"] / 128)
  if (CAN9660["ADC_ZERO"]["old"] != CAN9660["ADC_ZERO"])
    AS_CAN9660_writetableval(CAN9660["ADC_ZERO"]["addr"], CAN9660["ADC_ZERO"] * 1000 + 3125)
  # Stabilizer setup
  if (CAN9660["STABLZ_GCOR"]["old"] != CAN9660["STABLZ_GCOR"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_GCOR"]["addr"], CAN9660["STABLZ_GCOR"])
  if (CAN9660["STABLZ_ZCOR"]["old"] != CAN9660["STABLZ_ZCOR"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_ZCOR"]["addr"], CAN9660["STABLZ_ZCOR"])
  if (CAN9660["STABLZ_GMOD"]["old"] != CAN9660["STABLZ_GMOD"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_GMOD"]["addr"], CAN9660["STABLZ_GMOD"])
  if (CAN9660["STABLZ_ZMOD"]["old"] != CAN9660["STABLZ_ZMOD"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_ZMOD"]["addr"], CAN9660["STABLZ_ZMOD"])
  if (CAN9660["STABLZ_GDIV"]["old"] != CAN9660["STABLZ_GDIV"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_GDIV"]["addr"], CAN9660["STABLZ_GDIV"])
  if (CAN9660["STABLZ_ZDIV"]["old"] != CAN9660["STABLZ_ZDIV"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_ZDIV"]["addr"], CAN9660["STABLZ_ZDIV"])
  if (CAN9660["STABLZ_GSPC"]["old"] != CAN9660["STABLZ_GSPC"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_GSPC"]["addr"], CAN9660["STABLZ_GSPC"])
  if (CAN9660["STABLZ_ZSPC"]["old"] != CAN9660["STABLZ_ZSPC"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_ZSPC"]["addr"], CAN9660["STABLZ_ZSPC"])
  if (CAN9660["STABLZ_GWIN"]["old"] != CAN9660["STABLZ_GWIN"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_GWIN"]["addr"], CAN9660["STABLZ_GWIN"])
  if (CAN9660["STABLZ_ZWIN"]["old"] != CAN9660["STABLZ_ZWIN"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_ZWIN"]["addr"], CAN9660["STABLZ_ZWIN"])
  if (CAN9660["STABLZ_GCENT"]["old"] != CAN9660["STABLZ_GCENT"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_GCENT"]["addr"], CAN9660["STABLZ_GCENT"])
  if (CAN9660["STABLZ_ZCENT"]["old"] != CAN9660["STABLZ_ZCENT"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_ZCENT"]["addr"], CAN9660["STABLZ_ZCENT"])
  if (CAN9660["STABLZ_GRAT"]["old"] != CAN9660["STABLZ_GRAT"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_GRAT"]["addr"], CAN9660["STABLZ_GRAT"])
  if (CAN9660["STABLZ_ZRAT"]["old"] != CAN9660["STABLZ_ZRAT"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_ZRAT"]["addr"], CAN9660["STABLZ_ZRAT"])
  if (CAN9660["STABLZ_GAIN"]["old"] != CAN9660["STABLZ_GAIN"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_GAIN"]["addr"], CAN9660["STABLZ_GAIN"])
  if (CAN9660["STABLZ_ZERO"]["old"] != CAN9660["STABLZ_ZERO"])
    AS_CAN9660_writetableval(CAN9660["STABLZ_ZERO"]["addr"], CAN9660["STABLZ_ZERO"])
  delete CAN9660["read for ADC"]
  delete CAN9660["read for fdisc"]
  delete CAN9660["read for gain"]
  delete CAN9660["read for stabilizer1"]
  delete CAN9660["read for stabilizer2"]
  delete CAN9660["read for filter"]
  CAN9660["warn"] = 0
  
}'

#%IU%()
#%MDESC% Writes all the values to the controller. Presets the data structure 
#used by the macros for the canberra 9660 DSP 
def AS_CAN9660_write_force() '{
  local x
  
  if (!(whatis("CAN9660") & 0x04000000)) {
    printf ("Please run can9660setup first")
    exit
  }
  # Gain related stuff
  AS_CAN9660_writetableval(CAN9660["AMP_CGAIN"]["addr"], CAN9660["AMP_CGAIN"])
  AS_CAN9660_writetableval(CAN9660["AMP_FGAIN"]["addr"], CAN9660["AMP_FGAIN"] * 0x1000 / 1.6)
  AS_CAN9660_writetableval(CAN9660["AMP_SFGAIN"]["addr"], CAN9660["AMP_SFGAIN"] * 0x1000 / 0.03)
  AS_CAN9660_writetableval(CAN9660["COINC"]["addr"], CAN9660["COINC"])
  # fast discriminator stuff
  AS_CAN9660_writetableval(CAN9660["MISC_FDM"]["addr"], CAN9660["MISC_FDM"])
  AS_CAN9660_writetableval(CAN9660["MISC_FD"]["addr"], CAN9660["MISC_FD"])
  AS_CAN9660_writetableval(CAN9660["MISC_INPP"]["addr"], CAN9660["MISC_INPP"])
  AS_CAN9660_writetableval(CAN9660["MISC_INHP"]["addr"], CAN9660["MISC_INHP"])
  # Filter related stuff
  AS_CAN9660_writetableval(CAN9660["FILTER_FT"]["addr"], int(CAN9660["FILTER_FT"] * 10))
  AS_CAN9660_writetableval(CAN9660["FILTER_RT"]["addr"], int(CAN9660["FILTER_RT"] * 10))
  AS_CAN9660_writetableval(CAN9660["FILTER_PZM"]["addr"], CAN9660["FILTER_PZM"])
  if (CAN9660["FILTER_PZ"] != 0) {
    x = int((CAN9660["FILTER_PZ"] - 1.7) * 0xfff / 40)
  } else {
    x = 0
  }
  AS_CAN9660_writetableval(CAN9660["FILTER_PZ"]["addr"], x)
  # ADC related stuff
  AS_CAN9660_writetableval(CAN9660["ADC_LLD"]["addr"], CAN9660["ADC_LLD"] * 0x7FFF / 100)
  AS_CAN9660_writetableval(CAN9660["ADC_CGAIN"]["addr"], CAN9660["ADC_CGAIN"])
  AS_CAN9660_writetableval(CAN9660["ADC_OFFSET"]["addr"], CAN9660["ADC_OFFSET"] / 128)
  AS_CAN9660_writetableval(CAN9660["ADC_ZERO"]["addr"], CAN9660["ADC_ZERO"] * 1000 + 3125)
  # Stabilizer setup
  AS_CAN9660_writetableval(CAN9660["STABLZ_GCOR"]["addr"], CAN9660["STABLZ_GCOR"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_ZCOR"]["addr"], CAN9660["STABLZ_ZCOR"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_GMOD"]["addr"], CAN9660["STABLZ_GMOD"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_ZMOD"]["addr"], CAN9660["STABLZ_ZMOD"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_GDIV"]["addr"], CAN9660["STABLZ_GDIV"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_ZDIV"]["addr"], CAN9660["STABLZ_ZDIV"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_GSPC"]["addr"], CAN9660["STABLZ_GSPC"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_ZSPC"]["addr"], CAN9660["STABLZ_ZSPC"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_GWIN"]["addr"], CAN9660["STABLZ_GWIN"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_ZWIN"]["addr"], CAN9660["STABLZ_ZWIN"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_GCENT"]["addr"], CAN9660["STABLZ_GCENT"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_ZCENT"]["addr"], CAN9660["STABLZ_ZCENT"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_GRAT"]["addr"], CAN9660["STABLZ_GRAT"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_ZRAT"]["addr"], CAN9660["STABLZ_ZRAT"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_GAIN"]["addr"], CAN9660["STABLZ_GAIN"])
  AS_CAN9660_writetableval(CAN9660["STABLZ_ZERO"]["addr"], CAN9660["STABLZ_ZERO"])
  delete CAN9660["read for ADC"]
  delete CAN9660["read for fdisc"]
  delete CAN9660["read for gain"]
  delete CAN9660["read for stabilizer1"]
  delete CAN9660["read for stabilizer2"]
  delete CAN9660["read for filter"]
  CAN9660["warn"] = 0
}'

#%IU%
#%MDESC% Only for test purposes. Sets the verbose option used 
#by the macros for the canberra 9660 DSP
def AS_CAN9660_verbose '{
  if (!(whatis("CAN9660") & 0x01000000)) {
    printf ("Please run can9660setup first")
    exit
  }
  CAN9660["Verbose"] = CAN9660["Verbose"] ? 0 : 1
  if (CAN9660["Verbose"]) rdef AS_CAN9660_info "print"
  else                    rdef AS_CAN9660_info "\#\$\*"
}'

#%IU%
#%MDESC% Documented to send a reset
def AS_CAN9660_reset '{
  icb_write(CAN9660["dev"], CAN9660["icb_adr"], 0, 0x08)
  icb_write(CAN9660["dev"], CAN9660["icb_adr"], 0, 0x04)
}'

#%IU%
#%MDESC% Documented to send a device ready
def AS_CAN9660_ready '{
  icb_write(CAN9660["dev"], CAN9660["icb_adr"], 0, 0x10)
}'

#%IU%
#%MDESC% Send a device ready and restore the values initially 
# set by Andrea S.
def AS_CAN9660_init '{
  can9660_reset
  can9660_ready
  for (key in CAN9660){
    split(key, tab, "\034")
    if (tab[1] == "addr")
      CAN9660[tab[0]]["addr"] = CAN9660[tab[0]]["init"]
  }
}'

#-----------------------------------------------------------
#%IU%
#%MDESC% read register 6 of the DSP module and decode the various bits.
def AS_CAN9660_modulestatus '{
  local numregs
  numregs = 7
  local ubyte array CAN9660regs[numregs]
  icb_mread (CAN9660["dev"], CAN9660["icb_adr"], 6, 6, CAN9660regs)
  # find the contents of reg 6 in CAN9660regs[6]
  AS_CAN9660_info sprintf("register 6 contains 0x%02x\n", CAN9660regs[6])
  if (CAN9660regs[6] & (1<<7)) {
    print "DSP module: DXIP (data transfer to the module inf progress)."
  }
  if (CAN9660regs[6] & (1<<6)) {
    print "DSP module: RDAV/WDONE double function bit:"
    print "   RDAV : Register data available for reading."
    print "   WDONE: Write operation hwas been completed."
    print "This bit will be cleared after reading."
  }
  if (CAN9660regs[6] & (1<<3)) {
    print "DSP module: ABUSY (Process is busy. Currently performing \
lengthy task)."
  }
  if (CAN9660regs[6] & (1<<2)) {
    print "DSP module: MBUSY (Module is busy. Currently executing \
command)."
  }
  if (CAN9660regs[6] & (1<<1)) {
    print "DSP module: MERR (Module error. Will be set by one of the \
following:"
    print "   Stabilizer Gain-Mode was ON and overrange condition \
occured."
    print "   Stabilizer Zero-Mode was ON and overrange condition \
occured."
  }  
  if (CAN9660regs[6] & 1) {
    print "DSP module: FAIL (Internal hardware failure occurred)."
  }
}'

##def icb_read (device, addr, reg) must be taken from can9635.mac
if (!(whatis("icb_read") & 2)) {
  print "***\nicb_read is not defined, please use can9635.mac for \
that!\n***"
}
##def icb_write (device, addr, reg, value) must be taken
# from can9635.mac
if (!(whatis("icb_write") & 2)) {
  print "***\nicb_write is not defined, please use can9635.mac for \
that!\n***"
}

##def icb_mread (device, addr, from_reg, to_reg, outarr) must be taken
# from can9635.mac
if (!(whatis("icb_mread") & 2)) {
  print "***\nicb_mread is not defined, please use can9635.mac for \
that!\n***"
}

##def icb_mwrite (device, addr, from_reg, to_reg, inarr) must be taken
# from can9635.mac
if (!(whatis("icb_mwrite") & 2)) {
  print "***\nicb_mwrite is not defined, please use can9635.mac for \
that!\n***"
}

#-----------------------------------------------------------
#%IU%(data table index)
#%MDESC% read a value from the DSP data space. 
# To read such a value, you must write registers 2 to 5 (5 always last )
# with the appropriate contents, then check R6 for the RDAV bit and the
# re-read the R2-5 to find the result in the same registers. The RDAV
# bit is self clearing, one read will clear it.
def AS_CAN9660_readtableval(tablenum) '{
  local numregs, i, value
  numregs = 7; value = 0
  local ubyte array CAN9660regs[numregs+1]
  if (!(whatis("CAN9660") & 0x01000000)) {
    printf ("Please run can9660setup first")
    exit
  }

  CAN9660regs[2] = 0x08 # bit #3 indicates read operation
  CAN9660regs[3] = tablenum
  CAN9660regs[4] = 0
  CAN9660regs[5] = 0
  AS_CAN9660_info sprintf("registers 2 to 5: 0x%02x 0x%02x 0x%02x 0x%02x \n", \
    CAN9660regs[2], CAN9660regs[3], CAN9660regs[4], CAN9660regs[5])
  # now send registers 2 to 5.
  icb_mwrite(CAN9660["dev"], CAN9660["icb_adr"], 2, 5, CAN9660regs)
  AS_CAN9660_info "wrote reg 2 to 5"
  # read register 6 
  icb_mread (CAN9660["dev"], CAN9660["icb_adr"], 6, 6, CAN9660regs)
  if(set_sim(-1)) CAN9660regs[6] = 0x40
  AS_CAN9660_info sprintf("read reg 6 0x%02x\n", CAN9660regs[6])
  i = 1
  # check for the RDAV bit.
  while(!(CAN9660regs[6] & 0x40)) {
    icb_mread (CAN9660["dev"], CAN9660["icb_adr"], 6, 6, CAN9660regs)
    AS_CAN9660_info sprintf("read reg 6 0x%02x\n", CAN9660regs[6])
    sleep(0.1) # wait for 100ms
    i ++ # increment the counter to see how many times all this takes.
  }
  AS_CAN9660_info sprintf("needed %d times to read the RDAV bit.\n", i)

  icb_mread (CAN9660["dev"], CAN9660["icb_adr"], 0, 6, CAN9660regs)
  AS_CAN9660_info "read reg 0 to 6"
  # now the contents of table, the value should be register 5 plus
  # R4 << 8 plus R3 << 16 plus R2 << 24
  # only 28 bits are however significant !
  AS_CAN9660_info sprintf("registers 0 to 6: 0x%02x 0x%02x 0x%02x \
0x%02x 0x%02x 0x%02x 0x%02x \n", CAN9660regs[0], CAN9660regs[1], \
    CAN9660regs[2], CAN9660regs[3], CAN9660regs[4], CAN9660regs[5], \
    CAN9660regs[6])
  value = CAN9660regs[5] + (CAN9660regs[4] <<8) + \
          (CAN9660regs[3] <<16) + (CAN9660regs[2] <<24)
  value = value & 0x1FFFFFFF
  AS_CAN9660_info value
  printf(".")
  return(value)
  
}' # Ends macro AS_CAN9660_readtableval


#-----------------------------------------------------------
#%IU%(data table index, value to write)
#%MDESC% write a value from the DSP data space. 
# To write such a value, you must write registers 2 to 5 (5 always last )
# with the appropriate contents, then check R6 for the WDONE bit. The
# RDAV bit is self clearing, one read will clear it.
def AS_CAN9660_writetableval(tablenum, value) '{
  local numregs, i
  numregs = 7
  local ubyte array CAN9660regs[numregs+1]
  if (!(whatis("CAN9660") & 0x01000000)) {
    printf ("Please run can9660setup first")
    exit
  }

  CAN9660regs[2] = 0  # absence of bit #3 indicates write operation
  CAN9660regs[3] = tablenum
  CAN9660regs[4] = (value & 0xffff) >> 8
  CAN9660regs[5] = value & 0xff
  AS_CAN9660_info sprintf("registers 2 to 5: 0x%02x 0x%02x 0x%02x 0x%02x \n", \
  CAN9660regs[2], CAN9660regs[3], CAN9660regs[4], CAN9660regs[5])
  # now send registers 2 to 5.
  AS_CAN9660_info sprintf("value to write is 0x%08x\n", value)
  icb_mwrite(CAN9660["dev"], CAN9660["icb_adr"], 2, 5, CAN9660regs)
  AS_CAN9660_info "wrote reg 2 to 5"
  # read register 6 
  icb_mread (CAN9660["dev"], CAN9660["icb_adr"], 6, 6, CAN9660regs)
  AS_CAN9660_info sprintf("read reg 6 0x%02x\n", CAN9660regs[6])
  i = 1
  if(set_sim(-1)) CAN9660regs[6] = 0x40
  # check for the WDONE bit.
  while(!(CAN9660regs[6] & 0x40)) {
    icb_mread (CAN9660["dev"], CAN9660["icb_adr"], 6, 6, CAN9660regs)
    AS_CAN9660_info sprintf("read reg 6 0x%02x\n", CAN9660regs[6])
    sleep(0.1) # wait for 100ms
    i ++ # increment the counter to see how many times all this takes.
  }
  AS_CAN9660_info sprintf("needed %d times to read the WDONE bit.\n", i)
  printf(".")
}' # Ends macro AS_CAN9660_writetableval

#%UU% [filename] [overwrite]
#%MDESC% Save the DSP parameters to an
#ASCII file, so they can be reloaded later by the "can9660restore_config" macro.
#The file is in the directory ~blissadm/local/spec/userconf/dsp and has the
#name, that you choose.
#%BR%
#If no name is specified as first argument, the user is prompted for
#one. If the file exists, the user will be prompted to confirm overwriting,
#unless a second argument is present.
def can9660save_config '{
  local dir file fullname command overwrite answer
  if (!(whatis("CAN9660") & 0x01000000)) {
    printf ("Please run can9660setup first")
    exit
  }

  dir = CAN9660["config_dir"]
  if ($# == 0) {
    print "Save CAN9660 DSP values."
    print "Already used filenames are:"
    unix(command = "ls "dir)
    file = getval("Enter filename","")
  } else
    file = "$1"

  if($# == 2) overwrite=1
  else if (yesno(sprintf("Overwrite file %s", file), 1))
    overwrite=1
  else overwrite=0

  if (file_info(dir,"isdir") != 1) unix (command="mkdir -p "dir)

  fullname = sprintf("%s/%s", dir, file)

  if (file_info(fullname, "-e")) {
    if (overwrite)
      unix (command = "rm -f "fullname)
    else {
      print "WARNING: File", file, "not written!"
      return 1
    }
  }

  on(fullname); offt
  for (i in CAN9660) {
    local x[]
    split(i, x, "\034")
    
    if (x[1] == "addr") print "CAN9660[\"" x[0] "\"] =",CAN9660[x[0]]
  }
  close (fullname); ont
 
  print "CAN9660 DSP values stored in:"
  print fullname
}'

#%UU% [filename]
#%MDESC% Restore the parameters stored in the file given by the only argument
#or entered by the user on request.
#%BR%
#The values will not immediately written to the controller, but will replace the
#values shown in the menus at the next call of can9660menu. If you choose not to
#write the values to the controller, they will persist, until you either write
#them to the controller or perform a new can9660setup. This will not destroy the
#existing data, but force the reading of the controller, which will provide you 
# again with the life values.
def can9660restore_config '{
  local file fullname dir
  if (!(whatis("CAN9660") & 0x01000000)) {
    printf ("Please run can9660setup first")
    exit
  }
  dir = CAN9660["config_dir"]

  if ($# == 0) {
    print "Restore CAN9660 DSP values."
    print "Available files are:"
    unix(command = "ls "dir)
    file = getval("Enter filename","")
  } else
    file = "$1"

  if (file == "-1")
    file = CAN9660["config_file"]
    
  if (file != "" ) {
    fullname = sprintf("%s/%s", dir, file)

    if (file_info(fullname,"-e") == 1) {
      global CAN9660
      qdofile(fullname)
      CAN9660["read for ADC"] = 1
      CAN9660["read for fdisc"] = 1
      CAN9660["read for gain"] = 1
      CAN9660["read for stabilizer1"] = 1
      CAN9660["read for stabilizer2"] = 1
      CAN9660["read for filter"] = 1
      eprint "DSP parameters have been restored. Use can9660menu to adjust"
      eprint "them to your needs!"
    } else
      eprint "No such file", fullname ". Can\'t restore CAN9660 DSP values"
  } else
    eprint "No filename given ---> no action!"
}'

#%MACROS%
#%IMACROS%
#%TOC%
#%AUTHOR% Holger