示例#1
0
def moveGuider(x1, y1, x2, y2, nomove=False):
    tvxoff = ktl.cache('dcs', 'tvxoff')
    tvyoff = ktl.cache('dcs', 'tvyoff')
    rel2curr = ktl.cache('dcs', 'rel2curr')
    binning = ktl.cache('magiq', 'binning')

    deltax = x1 - x2
    deltay = y2 - y1

    # this is the scale in arcsec/pix on the guider
    scale = 0.208
    # usually guiders are used in 2x2 binning

    scale = scale * float(binning.read())

    # convert delta in arcsec
    deltatvx = deltax * scale
    deltatvy = deltay * scale

    say("Requested moves: %3.1f %3.1f arcsecs" % (deltatvx, deltatvy))

    # move
    if (nomove is False):
        say("Applying moves...")
        tvxoff.write(deltatvx)
        tvyoff.write(deltatvy)
        rel2curr.write(True)
    else:
        say("No moves applied")
示例#2
0
def kcwiPowerStatus():
    """ 
   Provides access to the power supplies status
   """
    kp1s = ktl.cache('kp1s')
    kp2s = ktl.cache('kp2s')
    kp3s = ktl.cache('kp3s')

    plugs = [1, 2, 3, 4, 5, 6, 7, 8]
    p1 = {}
    p2 = {}
    p3 = {}

    for plug in plugs:
        p1[plug] = [
            kp1s['pwname' + str(plug)], kp1s['pwstat' + str(plug)], kp1s
        ]
        p2[plug] = [
            kp2s['pwname' + str(plug)], kp2s['pwstat' + str(plug)], kp2s
        ]
        p3[plug] = [
            kp3s['pwname' + str(plug)], kp3s['pwstat' + str(plug)], kp3s
        ]

    say(datetime.now().strftime("%c"))

    format = "%1s %-14s %1s\t%-14s %1s\t%-14s %1s"
    say(format % ('P', 'kp1s', 'on', 'kp2s', 'on', 'kp3s', 'on'))

    for plug in plugs:
        say(format % (str(plug),p1[plug][0].read(),p1[plug][1].read(),\
                      p2[plug][0].read(),p2[plug][1].read(),\
                      p3[plug][0].read(),p3[plug][1].read()))

    return p1, p2, p3
示例#3
0
def get_nextfile(channel):
    """
   Return 1+ the frameno of the last saved image
   """

    if channel == 'blue':
        koutdir = ktl.cache('kbds', 'outdir')
        host = 'kcwiserver'
    #if channel == 'red':
    #   koutdir = ktl.cache('krds','outdir')
    if channel == 'fpc':
        koutdir = ktl.cache('kfcs', 'outdir')
        host = 'kcwiserver'

    outdir = koutdir.read()

    if exists_and_is_writable(outdir):
        files = file_list(outdir, host).split('\n')
    else:
        sys.stdout.write("Specified outdir %s does not exist.\n" % (outdir))
        sys.exit()

    pattern_start = frameroot(channel)
    pattern_end = '.fits'
    nextfile = 1
    if len(files) > 0:
        selected_files = [x for x in files if x.startswith(str(pattern_start))]
        selected_numbers = [
            int((x.split("_")[1]).split(".")[0]) for x in selected_files
        ]
        if len(selected_numbers) > 0:
            nextfile = sorted(selected_numbers)[-1] + 1

    #say("returning next file = %s" % (nextfile))
    return nextfile
    def interrupt(error_code, error_message):

        """
        There is no need to update the control
        variables on interrupt, because the
        script is terminated.
        """
        
        fcssta = ktl.cache('deifcs', 'FCSSTA')
        fcsstate = ktl.cache('deifcs', 'FCSSTATE')
        fcstrack = ktl.cache('deifcs', 'FCSTRACK')
        fcstask = ktl.cache('deifcs', 'FCSTASK')
        fcssta.write('passive', wait=True)
        fcsstate.write('idle', wait=True)
        fcstask.write('Idle', wait=True)
        fcstrack.write('not correcting', wait=True)

        fcsreffi = ktl.cache('deifcs', 'FCSREFFI')
        fcsimgfi = ktl.cache('deifcs', 'FCSIMGFI')
        fcslogfi = ktl.cache('deifcs', 'FCSLOGFI')
        fcsreffi.write('', wait=True)
        fcsimgfi.write('', wait=True)
        fcslogfi.write('', wait=True)

        fcsintxm = ktl.cache('deifcs', 'FCSINTXM')
        fcsintym = ktl.cache('deifcs', 'FCSINTYM')
        fcsintxm.write(0.0, wait=True)
        fcsintym.write(0.0, wait=True)

        flamps = ktl.cache('deifcs', 'FLAMPS')
        flamps.write('off', wait=True)

        logErrorMessage(error_code, error_message)
        
        sys.exit(error_code)
def lamp_shutter(lamp=None, action=None):
    """
    Open/Close or query status of calibration lamps shutter

    Parameters
    ----------
    lamp : string
        Lamp name. Valid values are "thar","fear","continuum|led", and "all"
        Abbreviated and capitalized names are ok.

    action: open/close/status
        Open
        Close
        If action is missing, the status is returned.

    Examples
    --------

    Open the Iron Argon lamp shutter:

    >>> Calibration.lamp_shutter("fear","open")

    """
    server = 'kcas'

    lamp0shstat = ktl.cache(server, 'LAMP0SHSTAT')
    lamp1shstat = ktl.cache(server, 'LAMP1SHSTAT')

    monitoredKeywords = (lamp0shstat, lamp1shstat)

    # set wait = False if you can accept undefined keywords (for simulation, for example)
    setupMonitoring(monitoredKeywords, wait=True)

    # capitalize

    lamp = lamp.upper()
    action = action.upper()

    # decide which keywords to use

    if lamp in ("THAR", "THA", "TH", "T", "1"):
        statusKeyword = lamp1shstat
    if lamp in ("FEAR", "FEA", "FE", "F", "0"):
        statusKeyword = lamp0shstat

    if action != "":
        time.sleep(3)

    if action == "OPEN":
        statusKeyword.write(1)

    if action == "CLOSE":
        statusKeyword.write(0)

    # get status

    if action == "":

        status = statusKeyword.read()
        return status
示例#6
0
def moveFpc(x1, y1, x2, y2, nomove=False):

    instxoff = ktl.cache('dcs', 'instxoff')
    instyoff = ktl.cache('dcs', 'instyoff')
    rel2curr = ktl.cache('dcs', 'rel2curr')

    deltax = x2 - x1
    deltay = y2 - y1

    # this is the scale in arcsec/pix on the focal plane, unbinned
    scale = 0.00756
    # binning
    binning = 4
    scale = scale * binning

    # convert delta in arcsec
    deltainstx = deltax * scale
    deltainsty = deltay * scale

    say("Requested moves: %3.1f %3.1f arcsecs" % (deltainstx, deltainsty))

    # move
    if (nomove is False):
        say("Applying moves...")
        instxoff.write(deltainstx)
        instyoff.write(deltainsty)
        rel2curr.write(True)
    else:
        say("No moves applied")
示例#7
0
def moveSlicer(direction, number, nomove=False):
    slicer = ktl.cache('kcas', 'ifuname')
    tvxoff = ktl.cache('dcs', 'tvxoff')
    rel2curr = ktl.cache('dcs', 'rel2curr')

    slicer.monitor()
    say("Slicer is %s" % (slicer))
    platescale = 1.36
    if slicer == 'Medium':
        sliceSize = platescale / 2.0
    elif slicer == 'Small':
        sliceSize = platescale / 4.0
    elif slicer == 'Large':
        sliceSize = platescale
    else:
        sliceSize = 0

    if direction == 'left':
        sign = +1
    elif direction == 'right':
        sign = -1
    else:
        sign = 0

    if (nomove is False):
        moveSize = sliceSize * number * sign
        say("Applying moves (%s %f) ...\n" % (direction, moveSize))
        tvxoff.write(sliceSize * number * sign)
        rel2curr.write(True)
    else:
        say("No moves applied")
def hatch(status=None):
    """
    Open or close the instrument hatch

    Parameters
    ----------
    status : string
        open or close

    Examples
    --------
    Open the instrument hatch

    >>> Calibration.hatch(status="open")


    """
    server = 'kcas'
    hatchstatus = ktl.cache(server, 'HATCHSTATUS')
    hatchpos = ktl.cache(server, "HATCHPOS")

    monitoredKeywords = (hatchstatus, hatchpos)

    # set wait = False if you can accept undefined keywords (for simulation, for example)
    setupMonitoring(monitoredKeywords, wait=True)
    #hatchstatus.monitor()

    # if called with an empty string, return the current slicer
    if status == None:
        return hatchstatus.read()

    # initiate the move
    if status in ["open", "OPEN", 1, "1", "Open"]:
        requested = 1
    if status in ["close", "CLOSE", 0, "0", "Closed"]:
        requested = 0

    # if the requested position is the same as the current, do not move
    if requested == int(hatchstatus.ascii):
        say("Hatch: Target is the same as requested. No move needed.")

    # check if move is possible
    # probably not necessary for the hatch
    #checkIfMoveIsPossible(ifustatus)

    hatchstatus.write(requested)
    say("Setting Hatch to %s" % (status))

    target_reached = '$kcas.hatchstatus == ' + str(requested)

    target_reached = ktl.Expression(target_reached)

    # wait for target reached
    target_reached.wait(timeout=10)

    return hatchstatus
示例#9
0
def newdir(disk=None):

    # DISK

    if disk == None:
        # read the list of available disks
        disklist = ktl.cache('kbds', 'disklist')
        datadisk = disklist.read()

        disk = str(datadisk.split(':')[0])

    # UTDATE

    uttime = float(datetime.utcnow().strftime("%H"))
    if uttime > 18.0:
        delta = 1
    else:
        delta = 0
    utdate = str((datetime.utcnow() +
                  timedelta(days=delta)).strftime("%Y%b%d")).lower()

    # USER

    user = os.environ['USER']

    # HOST

    host = os.environ['HOST']

    # use the builtin feature of the kbds server to create the directory

    new_outdir = disk + "/" + user + "/" + utdate
    say("Attempting to change outdir to %s" % new_outdir)

    outdir(new_outdir)
示例#10
0
def autoshutb(mode=None):
    """
    Reads or sets the blue channel science camera autoshutter

    Parameters
    ----------
    autoshutb : int
        Desired autoshutb mode (1 or 0)

    Examples
    --------
    Prints the current autoshut mode

    >>> Blue.autoshutb(mode=1)

    """
    autoshutKeyword = ktl.cache('kbds','AUTOSHUT')
    availableModes = [0,1]
    current = autoshutKeyword.read()
    if mode == None:
        return current

    elif int(mode) in availableModes:
        if int(mode) != int(current):
            autoshutKeyword.write(mode)
            say ('AUTOSHUT set to %s' % (mode))
        else:
            say("AUTOSHUT: Target equals current, no change needed.")

        current = autoshutKeyword.read()
        return current
    else:
        raise ValueError('AUTOSHUT %s is not supported' % (mode))
def new_reference(gpos):
    """
    Recenter the grating tilt for sliders 3 and 4
    """

    gr_tlt_off = 'G' + str(gpos) + 'TLTOFF'
    
    grtiltoff = ktl.cache('deimot', gr_tlt_off)
    try:
        grtltoff.monitor()
    except ktl.ktlError:
        raise fcs_exceptions.AbortOnDeimotCommunicationFailure(grtltoff)

    if grtltoff != 0:
        
        message = str('Slider %d tilt offset %s is not centered in its range' % (gpos, gtltoff))
        fcs_auxliary.logMessage(message)
        message = str('About to recenter the slider %d tilt by setting offset = 0' % gpos)
        fcs_auxliary.logMessage(message)

        try:
            grtltoff.write(0)
        except:
            raise fcs_exceptions.AbortOnCenteringSliderTiltOffset(gpos)

    else:
        
        message = str('Slider %d tilt is already centered in its range' % gpos)
        fcs_auxliary.logMessage(message)
示例#12
0
def binningfpc(binning=None):
    """
    Reads or sets the focal plane camera binninb

    Parameters
    ----------
    binning : string
        Desired binning mode. Available values are 1,2,4 and so on

    Examples
    --------
    Prints the current binning mode

    >>> Blue.binningfpc()

    Set the binning mode

    >>> Blue.binningfpc(binning='2')

    """
    
    binningKeyword = ktl.cache('kfcs','BINNING')
    current = binningKeyword.read()
    if binning==None:
        return current
        
    if binning != current:
        binningKeyword.write(binning)
        say ('BINNING set to %s' % (binning))
    else:
        say("Binning: Target equals current, no change needed.")

    current = binningKeyword.read()
    return current
示例#13
0
def tintb(exptime=None):
    """
    Reads or sets the blue channel science camera exposure time

    Parameters
    ----------
    exptime : float
        Desired exposure time. 

    Examples
    --------
    Prints the current exposure time

    >>> Blue.tintb()

    Set the exposure time

    >>> Blue.tintb(exptime=10)

    """
    
    exptimeKeyword = ktl.cache('kbds','TTIME')

    if exptime!=None:
        exptimeKeyword.write(exptime)
        say ('Exposure time set to %s' % (str(exptime)))


    result = exptimeKeyword.read()
    return result
def logErrorMessage(error_code, error_message):

    """
    Prints error code and message on the terminal.
    Updates FCSERR and FCSMSG keywords.
    """

    fcserr = ktl.cache('deifcs', 'FCSERR')
    fcsmsg = ktl.cache('deifcs', 'FCSMSG')

    if fcserr != error_code:
        fcserr.write(error_code, wait=True)
    if fcsmsg != error_message:
        fcsmsg.write(error_message, wait=True)
        
    print(str(dt.datetime.now()) + ' --> ' + error_message)
示例#15
0
def tintfc(exptime=None):
    """
    Reads or sets the focal plane camera exposure time

    Parameters
    ----------
    exptime : float
        Desired exposure time. 

    Examples
    --------
    Prints the current exposure time

    >>> Blue.tintfc()

    Set the exposure time

    >>> Blue.tintfc(exptime=10)

    """
    exptime = float(exptime)
    exptimeKeyword = ktl.cache('kfcs','EXPTIME')

    if (exptime == 0):
        say('The exposure time for the focal plane camera cannot be zero')
        exptime = 0.01

    if exptime!=None:
        exptimeKeyword.write(exptime)
        say ('Exposure time set to %s' % (str(exptime)))

    result = exptimeKeyword.read()
    return result
示例#16
0
def triggerfc(trigger_time=None):
    """
    Reads or sets the focal plane camera trigger time

    Parameters
    ----------
    trigger_time : int
        Desired trigger time (interval between exposures)

    Examples
    --------
    Prints the current trigger time

    >>> Blue.triggerfc()

    Set the triger time

    >>> Blue.triggerfc(5)

    """
    
    keyword = ktl.cache('kfcs','TRIGTIME')

    if trigger_time!=None:
        keyword.write(trigtime)
        say ('Trigger time set to %s' % (str(trigtime)))


    result = keyword.read()
    return result
示例#17
0
def sequencefc(sequence=None):
    """
    Reads or sets the focal plane camera sequence (number of images, 0 = continuous)

    Parameters
    ----------
    sequence : int
        Desired number of exposures

    Examples
    --------
    Prints the current number of exposures

    >>> Blue.sequencefc()

    Set the number of the exposures

    >>> Blue.sequencefc(5)

    """
    
    sequenceKeyword = ktl.cache('kfcs','SEQUENCE')
    sequenceKeyword.read()

    seqCurrent = sequenceKeyword.binary

    if sequence is not None and sequence != seqCurrent:
        sequenceKeyword.write(sequence)
        say ('Sequence set to %s' % (str(sequence)))

    result = sequenceKeyword.read()
    return result
示例#18
0
def pupil_rotator_ok():
    '''Commonly used pre- and post- condition to check whether there are errors
    in the pupil rotator status.
    '''
    mmprs_statuskw = ktl.cache(keyword='STATUS', service='mmprs')
    pupil_status = mmprs_statuskw.read()
    if pupil_status not in ['OK', 'Tracking']:
        raise FailedCondition(f'Pupil rotator status is {pupil_status}')
示例#19
0
def frame(channel, number=None):
    """
   if number is fiven, reset frameno to that number.
   If not, returns the current frameno
   """
    if channel == 'blue':
        kframeno = ktl.cache('kbds', 'frameno')
    if channel == 'red':
        kframeno = ktl.cache('krds', 'frameno')
    if channel == 'fpc':
        kframeno = ktl.cache('kfcs', 'frameno')

    if number == None:
        frame = kframeno.read()
        return frame
    else:
        kframeno.write(number)
示例#20
0
def modify_keyword(server, keyword):
    mykeyword = ktl.cache(server, keyword)
    print("Is it json:" + str(request.is_json))
    content = request.get_json()
    print("Request data: %s" % (str(content)))
    mykeyword.write(content['value'])
    new_value = mykeyword.read()
    return json_dump(new_value)
示例#21
0
 def show(self, req, qstr):
     self._getParameters(qstr)
     mykeyword = ktl.cache(self._http_server, self._http_keyword)
     value = mykeyword.read()
     return json.dumps({
         'server': self._http_server,
         'keyword': self._http_keyword,
         'value': value
     }), self.jsonText
示例#22
0
def kcwiTempStatus():
    """ 
   Provides access to the temperature status
   """
    kt1s = None
    kt2s = None

    if isServerUp('kt1s'):
        kt1s = ktl.cache('kt1s')
    else:
        say("Cannot establish connection to KT1S")
    if isServerUp('kt2s'):
        kt2s = ktl.cache('kt2s')
    else:
        say("Warning: Cannot establish connection to KT2S")

    sensors = [1, 2, 3, 4, 5, 6, 7, 8]
    t1 = {}
    t2 = {}
    say(datetime.now().strftime("%c"))

    format = "%1s %-20s %10.3f %10.3f"
    say("%1s %-20s %7s %7s" % ('S', 'Location', 'C', 'K'))

    for sensor in sensors:
        if kt1s is not None:
            t1[sensor] = [
                kt1s['tmploc' + str(sensor)], kt1s['tmp' + str(sensor)], kt1s
            ]
            T1 = float(t1[sensor][1].read()) - 273.15
            if T1 + 273.15 > 0:
                say(format %
                    (str(sensor), t1[sensor][0].read(), T1, T1 + 273.15))
    for sensor in sensors:
        if kt2s is not None:
            t2[sensor] = [
                kt2s['tmploc' + str(sensor)], kt2s['tmp' + str(sensor)], kt2s
            ]
            T2 = float(t2[sensor][1].read()) - 273.15
            if T2 + 273.15 > 0:
                say(format %
                    (str(sensor), t2[sensor][0].read(), T2, T2 + 273.15))

    return t1, t2
示例#23
0
def set_nextframe(channel, number=None):
    """
   if number is given, reset frameno to that number. If not, 
   reset to the last frame available on disk + 1
   """
    if channel == 'blue':
        kframeno = ktl.cache('kbds', 'frameno')
        if number == None:
            number = get_nextfile(channel)
    #if channel == 'red':
    #   kframeno = ktl.cache('krds','frameno')
    #   if number == None:
    #      nextfile = get_nextfile(channel)
    if channel == 'fpc':
        kframeno = ktl.cache('kfcs', 'counter')
        if number == None:
            number = get_nextfile(channel)

    kframeno.write(int(number))
示例#24
0
def outdir(outdir=None):
    """ 
   With no arguments, prints the name of the current data directory. 
   With one argument, change the outdir keyword to the named directory, creating it if requested.
   If files already exist in the outputdirectory, frameno is reset to be 1 higher than the highest
   existing image number.
   """

    if outdir == None:
        koutdir = ktl.cache('kbds', 'outdir')
        return koutdir.read()

    say("Setting outdir to %s for Blue Camera" % (outdir))
    koutdirb = ktl.cache('kbds', 'outdir')
    koutdirb.write(outdir)
    #koutdirr = ktl.cache('krds','outdir')
    #koutdirr.write(outdir)
    say("Setting outdir to %s for Focal Plane Camera" % (outdir))
    koutdir_fpc = ktl.cache('kfcs', 'outdir')
    koutdir_fpc.write(outdir)
    exists_and_is_writable(outdir)
    if exists_and_is_writable(outdir):
        pass
    else:
        raise RuntimeError("Could not set outdir to " + outdir)

    # if there are files in the directory
    # set the frame to the next frame
    kframeno = ktl.cache('kbds', 'frameno')
    nextFileBlue = get_nextfile("blue")
    sys.stdout.write("Setting next file number to %s for blue camera.\n" %
                     (nextFileBlue))
    kframeno.write(nextFileBlue)
    #nextFileRed = get_lastfile("red")
    #sys.stdout.write("Setting next file number to %s for red camera.\n" % (nextFileRed))
    # kframeno.write(nextFileRed)
    kframeno_fpc = ktl.cache('kfcs', 'counter')
    nextFile = get_nextfile("fpc")
    sys.stdout.write(
        "Setting next file number to %s for focal plane camera.\n" %
        (nextFile))
    kframeno_fpc.write(nextFile)
示例#25
0
def is_in_filling_position():
    kbms = ktl.cache('kbms')
    
    artfilling = kbms['artfilling']
    artlocked = kbms['artlocked']

    status = False

    if artfilling.read() == "1" or artlocked.read() == "1":
        status = True

    return status
def logMessage(message):

    """
    Print message on the terminal.
    Update FCSMSG keyword.
    """

    fcsmsg = ktl.cache('deifcs', 'FCSMSG')

    if fcsmsg != message:
        fcsmsg.write(message, wait=True)
        
    print(str(dt.datetime.now())' --> ' message)
示例#27
0
def checkCommunications(ctrl_var):
    
    """
    Check communications with keyword services
    """

    # Check communications with the deifcs service.
    
    fcstask = ktl.cache('deifcs', 'FCSTASK')
    try:
        fcstask.monitor()
    except ktl.ktlError:
        ctrl_var = fcs_exceptions.DeifcsCommunicationFailure('FCSTASK', ctrl_var)

    # Check communications with the deiccd service.

    ampmode = ktl.cache('deiccd', 'AMPMODE')
    try:
        ampmode.monitor()
    except ktl.ktlError:
        ctrl_var = fcs_exceptions.DeiccdCommunicationFailure('AMPMODE', ctrl_var)

    # Check communications with the deimot service.
    
    gratenam = ktl.cache('deimot', 'GRATENAM')
    try:
        gratenam.monitor()
    except ktl.ktlError:
        ctrl_var = fcs_exceptions.DeimotCommunicationFailure('GRATENAM', ctrl_var)

    # Check communications with the deirot service.

    rotatval = ktl.cache('deirot', 'ROTATVAL')
    try:
        rotatval.monitor()
    except ktl.ktlError:
        ctrl_var = fcs_exceptions.DeirotCommunicationFailure('ROTATVAL', ctrl_var)

    return ctrl_var
示例#28
0
def object(object=None):
    """ 
   Returns or sets the object
   """
    kobjectb = ktl.cache('kbds', 'object')
    #kobjectr = ktl.cache('krds','object')
    if object == None:

        object = kobjectb.read()
        return object

    else:
        kobjectb.write(object)
示例#29
0
def imtype(imtype=None):
    """ 
   Returns or sets the image type
   """
    kimtype = ktl.cache('kbds', 'imtype')
    #kobjectr = ktl.cache('krds','object')
    if imtype == None:

        imtype = kimtype.read()
        return imtype

    else:
        kimtype.write(imtype)
示例#30
0
def observer(observer=None):
    """ 
   Returns or sets the observer
   """
    kobserverb = ktl.cache('kbds', 'observer')
    #kobserverr = ktl.cache('krds','observer')

    if observer == None:

        observer = kobserverb.read()
        return observer

    else:
        kobserverb.write(observer)
示例#31
0
SUNEL_HOR = -3.2
DEWARMAX = 8600
DEWARMIN = 8400

#ScriptDir = '@LROOT@/bin/robot/'
ScriptDir = '/usr/local/lick/bin/robot/'

deckscale = {'M': 1.0, 'W':1.0, 'N': 3.0, 'B': 0.5, 'S':2.0, 'P':1.0}


# Aquire the ktl services and associated keywords
tel        = ktl.Service('eostele')
sunelServ  = tel('SUNEL')
apfmet     = ktl.Service('apfmet')
checkapf   = ktl.Service('checkapf')
ok2open    = ktl.cache('checkapf','OPEN_OK')
dmtimer    = ktl.cache('checkapf','DMTIME')
wx         = ktl.cache('apfmet','M5WIND')

robot      = ktl.Service('apftask')
vmag       = robot['scriptobs_vmag']

ucam       = ktl.Service('apfucam')
apfteq     = ktl.Service('apfteq')
teqmode    = apfteq['MODE']
guide      = ktl.Service('apfguide')
counts     = ktl.cache('apfguide','COUNTS')
kcountrate     = ktl.cache('apfguide','COUNTRATE')
thresh     = guide['xpose_thresh']
elapsed    = ktl.cache('apfucam','ELAPSED')
motor      = ktl.Service('apfmot')