Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
current_amplifiers.py 11.20 KiB
import numpy as np
from time import sleep

from epics import caget, caput
from .userCalcs import userStringSeq_pvs, userStringSeq_clear
from .VLS_PGM import mono_energy_get


def ca_detector_list(branch):
    """
    returns the list of keithley current amplifiers based on key
    key: 'ARPES' / 'Staff'
    """
    ca_list = list(ca_dictionary()[branch].keys())
    
    return ca_list

def ca_dictionary():
    """
    dictionary of connected Kiethlys and thier names

    Previously: CA_Name
    """
    ca={}
    ca["b"] = {
        1: 'W-mesh',
        2: 'H-wire',
        3: 'V-wire',
        4: 'Slit1A',
        5: 'Slit1A',
        9: 'D2B',
        10: 'B-branch',
        12: '',
        13: 'Slit3C',
        14: 'MeshD',
        15: 'DiodeC',
        }

    ca["c"] = {
        1:'TEY',
        }
    return ca

##############################################################################################################
################################            Keithley             ##############################
##############################################################################################################
def Keithley_pv(ca_ioc, ca_num):
    return "29id"+ca_ioc+":ca"+str(ca_num)+":"

class Keithley:
    """
    class for Keithley current amplifiers
    """
    def __init__(self,ca_ioc, ca_num):
        self._pv = Keithley_pv(ca_ioc, ca_num)
        try:
            self.name = ca_dictionary()[ca_ioc][ca_num]
        except:
            self.name = 'CA'+ca_ioc+str(ca_num)

        self.current
        self.rate
        self.filter_num
        self.range

    def get(self):
        """
        reads the current SRS and corresponding scaler values
        """
        self.current = caget(self.pv+"read")
        self.range = 'Autoscale' if caget(self.pv+"rangeAuto",as_string=True)=='On' else caget(self.pv+"range",as_string=True)
        self.rate = caget(self.pv+"rate")
        digital_filter = (self.pv+'digitalFilter')
        self.filter_num = 1 if digital_filter == 'On' else (self.pv+'digitalFilterCount')


    def reset(self,rate="Slow"):
        """
        
        Previously: Reset_CA
        """

        pv = self.pv
        caput(pv+":reset.PROC",1)
        caput(pv+":digitalFilterSet","Off")
        caput(pv+":medianFilterSet","Off")
        caput(pv+":zeroCheckSet",0)
        caput(pv+":rangeAuto",1)
        caput(pv+":rateSet",rate)
        caput(pv+":rangeAutoUlimit","20mA")
        caput(pv+":read.SCAN",".5 second")

    def autoscale(self,On_Off='On',gain=7):
        """
        ca_ioc = 'b' / 'c'
        ca_num = 2

        On_Off= 'On' => Turns On the Autoscale; gain is irrelevant.
        On_Off= 'Off' => Turns Off the Autoscale with gain below:
                0 = 2nA
                1 = 20nA
                2 = 200nA
                3 = 2uA
                4 = 20uA
                5 = 200uA
                6 = 2mA
                7 = 20mA

        Previously: CA_Autoscale
        """
        pv = self.pv
        caput(pv+":rangeAutoSet",On_Off)
        sleep(0.5)
        caput(pv+":rangeSet",gain)
        print(pv,"Autoscale",On_Off)

        if On_Off == 'Off':
                sleep(1)
                print("Gain set to:",caget(pv+":range",as_string=True))



    def avg(self,num_averages,rate='Slow',verbose=True):
        """
        Turns on the filtering for the Keithlys for average during a scan

        rate: Keithley sampling rate
            'Slow' / 'Medium' / 'Fast'

        num_averages: number of points to average

        returns the settling_time

        Previously: CA_Filter
        """

        pv = self.pv
        name=self.name
        t=0.1
        if rate == "Slow":
            t=6/60.0
        elif rate == "Medium":
            t=1/60.0
        elif rate == "Fast":
            t=0.1/60.0

        settling_time=round(max(0.15,num_averages*t+0.1),2)

        if num_averages  <= 1: # no averaging
            self.reset(rate)    
            settling_time = None
            if verbose:
                print("Average disabled: "+name+" - "+pv)

        else:
            caput(pv+"read.SCAN","Passive",wait=True,timeout=500)
            caput(pv+"rateSet",rate)
            sleep(1)
            caput(pv+"digitalFilterCountSet",num_averages,wait=True,timeout=500)
            caput(pv+"digitalFilterControlSet","Repeat",wait=True,timeout=500)
            caput(pv+"digitalFilterSet","On",wait=True,timeout=500)
            
            if verbose:
                print("Average enabled: "+name+" - "+pv)
                print("Detector settling time: "+str(settling_time)+"s")

        return settling_time


    
def ca_reset_all(rate="Slow"):
    """"
    
    Previously: Reset_CA_all
    """
    ca_dict = ca_dictionary()
    for ca_ioc in ca_dict.keys():
        for ca_num in ca_dict[ca_ioc].keys(): 
            CA = Keithley(ca_ioc,ca_num)
            CA.reset()

    caput("29idb:ca5:read.SCAN","Passive")    # CA5 in passive
    print("\nAll the current amplifiers have been reset; ca5 set to passive.")

def ca_live_sequence(ioc,seq_num,detector_list):    
    """
    creates a string sequence to put keithleys back in live mode

    ca_list = list of ca to go in live

    Previously: CA_Live_StrSeq
    """   
    userStringSeq_pv, userStringSeq_proc = userStringSeq_pvs(ioc, seq_num)       # do we need to add 29idb:ca5 ???
 
    userStringSeq_clear(ioc,seq_num)

    caput(userStringSeq_pv+".DESC","CA Live "+ioc)

    for (i,ca) in enumerate(detector_list):
        pv = "29id"+ca[0]+":ca"+str(ca[1])+":"
        ca_read_pv = pv+'read.SCAN CA NMS'
        ca_avg_pv = pv+'digitalFilterSet PP NMS'

        caput(userStringSeq_pv+".LNK"+str(i+1),ca_avg_pv)
        caput(userStringSeq_pv+".STR" +str(i+1),"Off")

        n=len(detector_list)
        if n+1+i < 10:
            caput(userStringSeq_pv+".LNK" +str(n+1+i),ca_read_pv)
            caput(userStringSeq_pv+".STR" +str(n+1+i),".5 second")
            caput(userStringSeq_pv+".WAIT"+str(n+1+i),"After"+str(n))
        elif n+1+i == 10:
            caput(userStringSeq_pv+".LNKA",ca_read_pv)
            caput(userStringSeq_pv+".STRA",".5 second")
            caput(userStringSeq_pv+".WAITA","After"+str(n))

    return userStringSeq_proc

def ca_average(avg_pts,ca_list,rate="Slow",verbose=True):
    """
    Average reading of the relevant current amplifiers for the current scanIOC/branch.

    Previously: CA_average
    """
    print("\nAverage set to:   "+str(max(avg_pts,1)))
    for i in range(0,len(ca_list)):
        ca = Keithley(ca_list[i][0],ca_list[i][1])
        ca.avg(avg_pts,rate=rate,verbose=verbose)


def load_responsivity_curve():
    """
    Loads the responsivity curve for the AUX100 photo diode, used to convert 
    current to flux at a given photon energy

    Previously: loadResponsivityCurve
    """
    FilePath='/home/beams/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/'
    FileName="DiodeResponsivityCurve"
    data = np.loadtxt(FilePath+FileName, delimiter=' ', skiprows=1)
    return data

def current2flux(current,hv=None,verbose=True):
    """
    Converts the current to flux for of an AUX 100 diode using the responisity curves

    hv = None; gets the current mono energy; otherwise you will need to specify

    Previously CA2flux
    """
    try: 
        curve=load_responsivity_curve()
    except:
        print('responsivity curve not loaded')
        return
    responsivity=curve[:,0]
    energy=curve[:,1]
    charge = 1.602e-19
    if hv is None:
        try: 
            hv = mono_energy_get()
        except:
            print('specify hv')
            return
    eff=np.interp(hv,energy,responsivity)
    flux = current/(eff*hv*charge)
    if verbose:
        print("\nCalculating flux for:")
        print("   hv = %.1f eV" % hv)
        print("   current = %.3e Amp" % current)
        print("Flux = %.3e ph/s\n" % flux)
    return flux


def flux2current(flux,hv=None,verbose=True):
    """
    Converts the specified flux to a current for of an AUX 100 diode using the responisity curves

    hv = None; gets the current mono energy; otherwise you will need to specify
    """
    try:
        curve=load_responsivity_curve()
    except:
        print('responsivity curve not loaded')
        return
    responsivity=curve[:,0]
    energy=curve[:,1]
    charge = 1.602e-19
    if hv is None:
        try:
            hv = mono_energy_get()
        except:
            print('specify hv')
            return
    eff=np.interp(hv,energy,responsivity)
    current = flux*(eff*hv*charge)
    if verbose:
        print("\nCalculating current for:")
        print("   hv = %.1f eV" % hv)
        print("   flux = %.3e ph/s" % flux)
        print("Current = %.3e Amp/n" % current)
    return current

    

##############################################################################################################
################################            SRS             ##############################
##############################################################################################################
class SRS:
    """
    SRS current amplifier and corresponding scalers
    """

    def __init__(self,scaler_pv,srs_pv):
        self._scaler_pv = scaler_pv
        self._srs_pv = srs_pv
        
        self.scaler_value 
        self.gain
        self.unit
        self.current
        self.get()

    def get(self):
        """
        reads the current SRS and corresponding scaler values
        """
        self.scaler_value = caget(self._scaler_pv)
        self.gain = float(caget(self._scaler_pv+'sens_num.VAL',as_string=True))
        self.unit = caget(self._scaler_pv+'sens_unit.VAL',as_string=True)
        CA = {'pA':1e-12, 'nA':1e-9, 'uA':1e-6, 'mA':1e-3}
        self.current = self.scaler_value * self.gain * CA[self.unit]

    def setgain(self,gain,unit):
        """
        gain = 1,2,5,10,20,50,100,200,500
        unit = 'pA, 'nA', 'uA', 'mA'
        """
        caput(self._srs_pv+'sens_num.VAL',str(gain))
        caput(self._srs_pv+'sens_unit.VAL',str(unit))
  

    def srs_print_all(self, long=False):
        """
        prints  SRS setting
        """

        invert=caget(self._srs_pv+'invert_on.VAL',as_string=True)
        currentUnit=caget(self._srs_pv+'sens_unit.VAL',as_string=True)
        currentValue=caget(self._srs_pv+'sens_num.VAL',as_string=True)
        offsetValue=caget(self._srs_pv+"offset_num.VAL",as_string=True)
        offsetUnit=caget(self._srs_pv+"offset_unit.VAL",as_string=True)
        offsetSign=caget(self._srs_pv+"offset_sign.VAL",as_string=True)
        offsetFactor=caget(self._srs_pv+"off_u_put.VAL",as_string=True)
        print('Gain: '+currentValue+' '+currentUnit+'  (invert '+invert+')')
        print('Baseline: '+offsetSign+' '+offsetFactor+' x '+offsetValue+" "+offsetUnit)
        if long:
            filterType=caget(self._srs_pv+'filter_type.VAL',as_string=True)
            filterLow=caget(self._srs_pv+'low_freq.VAL',as_string=True)
            filterHigh=caget(self._srs_pv+'high_freq.VAL',as_string=True)
            blank=caget(self._srs_pv+'blank_on.VAL',as_string=True)
            biasOnOff=caget(self._srs_pv+'bias_on.VAL',as_string=True)
            biasValue=caget(self._srs_pv+'bias_put.VAL',as_string=True)
            print('Filter: '+filterType+' - Low/High: '+filterLow+'  -'+filterHigh)
            print('Bias: '+biasOnOff+'- '+biasValue)
            print('Blank: '+blank)