def DMZero(self): try: if self.checkapf['DMTIME'].read(binary=True) < 1: APFLib.write(self.checkapf['DMTIME'], -1,timeout=10) except Exception, e: ostr = "Error: cannot touch DM Timer: %s " %( e) apflog(ostr,level='error',echo=True)
def getObserved(filename): """ getObserved parses a file to find the object names and times names, times = getObserved(filename) names - list of names, must be first column of file called filename times - times either as a timestamp in second column or a (hour,minute) tuple from a scriptobs line """ obs = [] times = [] nobs = dict() try: f = open(filename, 'r') except IOError: apflog( "Couldn't open %s" % filename,level="warn",echo=True) return obs, times else: for line in f: line = line.strip() if len(line) > 0: if line[0] == '#' or line == "": pass else: ls = line.split() obs.append(ls[0]) if len(ls) > 15: times.append( (int(ls[14].split('=')[1]), int(ls[15].split('=')[1])) ) else: times.append(float(ls[1])) obs.reverse() times.reverse() return obs, times
def calibrate(self, script, time): s_calibrate = os.path.join(ScriptDir,"calibrate") if self.test: print "Test Mode: calibrate %s %s." % (script, time) APFTask.waitFor(self.task, True, timeout=10) return True if time == 'pre' or 'post': try: APFLib.write("apfmot.DEWARFOCRAW",ktl.read("apftask","FOCUSINSTR_LASTFOCUS",binary=True)) except: apflog("Cannot read the last best fitting focus value or write the dewar focus value", level='error') if self.dewarfoc > 8600 or self.dewarfoc < 8400: apflog("Warning: The dewar focus is currently %d. This is outside the typical range of acceptable values." % (self.dewarfoc), level = "error", echo=True) return False apflog("Running calibrate %s %s" % (script, time), level = 'info') owner = self.apfschedule('OWNRHINT').read() self.apfschedule('OWNRHINT').write('public') cmd = '%s %s %s' % (s_calibrate,script, time) result, code = cmdexec(cmd,debug=True,cwd=os.getcwd()) if not result: apflog("%s %s failed with return code %d" % (s_calibrate, script, code),echo=True) expression="($apftask.CALIBRATE_STATUS != 0) and ($apftask.CALIBRATE_STATUS != 1) " if not APFTask.waitFor(self.task,True,expression=expression,timeout=30): apflog("%s %s failed to exit" % (s_calibrate,script),echo=True) self.apfschedule('OWNRHINT').write(owner) return result else: print "Couldn't understand argument %s, nothing was done." % time
def run_focustel(self): """Runs the telescope focus routine.""" el = self.tel['EL'].read(binary=True) cfspos = self.fspos.read(binary=True) crspos = self.rspos.read(binary=True) if abs(el - cfspos) < 2.5 or abs(el - crspos) < 2.5: apflog("Cannot focus, telescope too close to shutter", level="warn", echo=True) return False if self.test: APFTask.waitFor(self.task, True, timeout=10) apflog("Test Mode: Would be running focus_telescope.",echo=True) return True else: apflog("Running focus_telescope routine.",echo=True) cmdpath = '/usr/local/lick/bin/robot/' cmd = os.path.join(cmdpath,'focus_telescope') result, code = cmdexec(cmd,cwd=os.path.curdir) if not result: apflog("focustel failed with code %d" % code, echo=True) expression="($apftask.FOCUSINSTR_STATUS != 0) and ($apftask.FOCUSINSTR_STATUS != 1) " if not APFTask.waitFor(self.task,True,expression=expression,timeout=30): apflog("focus_telescope failed to exit" ,echo=True) return result return True
def findColumns(col_names, req_cols, opt_cols=[]): """findColumns finds the indices for the column names in the list of required columns indices = findColumns(col_names, req_cols) indices - a dictionary of indices, each index maps to where in col_names the column is found col_names - list of column names to be searched req_cols - list of names that should be in the first list """ idx = [] didx = dict() for r in req_cols: if r in col_names: didx[r] = col_names.index(r) else: apflog("%s Not found in column names from google spreadsheet" % (r), level="Warn", echo=True) for r in opt_cols: if r in col_names: didx[r] = col_names.index(r) # hack to handle an error if req_cols[0] == "Star Name" and req_cols[0] not in didx.keys(): didx[req_cols[0]] = 0 apflog("Pasting 'Star Name' into column 0 of google spreadsheet", level="Warn", echo=True) return didx
def enoughTimeTemplates(star_table, stars, idx, apf_obs, dt): count = numTemplateExp(star_table['Vmag'][idx]) tot_time = count * 1200 tot_time += 210 + ( 2 * 40 + 40 * (star_table['nexp'][idx] - 1) ) + 2400 # two B star exposures + three 70 second acquisitions and the actual observation readout times vis, star_elevations, fin_els, scaled_els = Visible.visible( apf_obs, [stars[idx]], [tot_time]) time_left_before_sunrise = computeSunrise(dt, horizon='-9') try: apflog( "enoughTimeTemplates(): time for obs= %.1f time until sunrise= %.1f " % (tot_time, time_left_before_sunrise), echo=True) except: apflog("enoughTimeTemplates(): cannot log times!?!", echo=True) if tot_time < time_left_before_sunrise and vis and time_left_before_sunrise < 14 * 3600.: return True else: return False
def run_centerup(self): cmdpath = '/usr/local/lick/bin/robot/' cmd = os.path.join(cmdpath,'centerup') result, code = cmdexec(cmd,cwd=os.path.curdir) if not result: apflog("centerup failed with code %d" % code, echo=True) return result
def parseStarlist(starlist): """ Parse a scriptobs-compatible starlist for the scheduler. names, star_table, lines, stars = parseStarlist(starlist) starlist - a filename names - a list of stars in the starlist star_table - a numpy array lines - a list of strings that can be used for scriptobs input stars - a list of pyEphem objects """ names = [] lines = [] stars = [] star_table = [] try: f = open(starlist, 'r') except IOError: apflog("Warning: Could not open %s. No target can be selected." % starlist, echo=True) return None else: for line in f: if not re.search("\A\#", line): ls = line.split() names.append(ls[0]) row = [] # RA value in radians row.append(getRARad(ls[1], ls[2], ls[3])) # Dec value in radians row.append(getDECRad(ls[4], ls[5], ls[6])) # PM RA row.append(float(ls[8].split('=')[-1])) # PM Dec row.append(float(ls[9].split('=')[-1])) # V mag row.append(float(ls[10].split('=')[-1])) # Exposure time row.append(float(ls[11].split('=')[-1])) # Desired Counts row.append(float(ls[16].split('=')[-1])) # Filler not used here row.append(0.) row.append(0.) # Number of exposures row.append(int(ls[19].split('=')[-1])) star_table.append(row) # Save the scriptobs line for later lines.append(line) # Generate a pyEphem object for this target star = ephem.FixedBody() star._ra = ephem.hours(":".join([ls[1], ls[2], ls[3]])) star._dec = ephem.degrees(":".join([ls[4], ls[5], ls[6]])) stars.append(star) return names, np.array(star_table), lines, stars
def evening_star(self): """Aim the APF at the desired target. This calls prep-obs, slewlock, and focus-telescope. A workaround to relying on scriptobs.""" if self.isOpen()[0] == False: apflog("APF is not open. Can't target a star while closed.",level='error',echo=True) return self.DMReset() # Call prep-obs apflog("Calling prep-obs.",echo=True) result, ret_code = cmdexec('prep-obs') if result == False: apflog("Prep-obs returned error code %d. Targeting object has failed." % (ret_code),level='error',echo=True) return self.DMReset() apflog("Slewing to lower el",echo=True) result, ret_code = cmdexec('slew -e 75') if result == False: apflog("Slew returned error code %d. Targeting object has failed." % (ret_code),level='error',echo=True) return # Slew to the specified RA and DEC, set guide camera settings, and centerup( Slewlock ) # Focus the telescope? self.DMReset() if self.focusTel(): return True else: return False
def findColumns(col_names,req_cols,opt_cols=[]): """ findColumns finds the indices for the column names in the list of required columns indices = findColumns(col_names, req_cols) indices - a list of indices, each index maps to where in col_names the column is found and in the order of req_cols col_names - list of column names to be searched req_cols - list of names that should be in the first list """ idx = [] didx = dict() for r in req_cols: if r in col_names: didx[r] = col_names.index(r) else: apflog("%s Not found in column names from google spreadsheet" % (r) , level="Alert",echo=True) for r in opt_cols: if r in col_names: didx[r] = col_names.index(r) # hack to handle an error if req_cols[0] == "Star Name" and req_cols[0] not in didx.keys(): didx[req_cols[0]] = 0 apflog("Pasting 'Star Name' into column 0 of google spreadsheet" , level="Error",echo=True) return didx
def getObserved(filename): """ getObserved parses a file to find the object names and times names, times = getObserved(filename) names - list of names, must be first column of file called filename times - times either as a timestamp in second column or a (hour,minute) tuple from a scriptobs line """ obs = [] times = [] try: f = open(filename, 'r') except IOError: apflog("Couldn't open %s" % filename, level="warn", echo=True) return obs, times else: for line in f: line = line.strip() if len(line) > 0: if line[0] == '#' or line == "": pass else: ls = line.split() obs.append(ls[0]) if len(ls) > 15: times.append((int(ls[14].split('=')[1]), int(ls[15].split('=')[1]))) else: times.append(float(ls[1])) obs.reverse() times.reverse() return obs, times
def parseRankTable(sheet_table_name='2021B_ranks', certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json'): apflog("Starting parse of %s" % (sheet_table_name), echo=True) sheetns = [] rank = [] worksheet = getSpreadsheet(sheetn=sheet_table_name, certificate=certificate) if worksheet: cur_codex = worksheet.get_all_values() if len(cur_codex) <= 0: apflog("Worksheet %s exists but is empty, skipping" % (sheet_table_name), level='error', echo=True) return None, None for row in cur_codex[1:]: sheetns.append(row[0]) crank = floatDefault(row[1]) crank = int(round(crank)) rank.append(crank) time_left = timeLeft() if time_left is not None: for ky in time_left.keys(): if time_left[ky] <= 0: if ky in sheetns: sindx = sheetns.index(ky) rank[sindx] = -1000 return sheetns, rank
def templateConditions(moon, seeing, slowdown): """ istrue = conditionCuts(moon, seeing, slowdown) Checks to see if moon, seeing and slowdown factor are within template conditions istrue - a simple Boolean moon - phase value from pyephem, ranges from 0 to 100 (a percentage) seeing - size in pixels slowdown - relative to clear """ if seeing < SEEING_TEMP and slowdown < SLOWDOWN_TEMP: apflog("moon.phase=%.2f moon.alt=%.2f" % (moon.phase, moon.alt), echo=True, level='debug') if moon.phase < 50 and float(moon.alt) < 0: return True elif moon.phase < 25 and float(moon.alt) < 0.7: return True else: return False else: return False
def dmtimemon(dmtime): if dmtime['populated'] == False: return try: APF.dmtime = dmtime except Exception, e: apflog("Exception in dmtimemon: %s" % (e), level='error')
def makeRankTable(sheet_table_name, outfn='rank_table', outdir=None): if not outdir: outdir = os.getcwd() outfn = os.path.join(outdir, outfn) if os.path.exists(outfn): rank_table = astropy.table.Table.read(outfn, format='ascii') else: sheetns, ranks = ParseUCOSched.parseRankTable( sheet_table_name=sheet_table_name) if sheetns is None or len(sheetns) == 0: return None rank_table = astropy.table.Table([sheetns, ranks], names=['sheetn', 'rank']) try: rank_table.write(outfn, format='ascii') except Exception as e: apflog("Cannot write table %s: %s" % (outfn, e), level='error', echo=True) return rank_table
def parseStarlist(starlist): """ Parse a scriptobs-compatible starlist for the scheduler. names, star_table, lines, stars = parseStarlist(starlist) starlist - a filename names - a list of stars in the starlist star_table - a numpy array lines - a list of strings that can be used for scriptobs input stars - a list of pyEphem objects """ names = [] lines = [] stars = [] star_table = [] try: f = open(starlist,'r') except IOError: apflog("Warning: Could not open %s. No target can be selected." % starlist,echo=True) return None else: for line in f: if not re.search("\A\#",line): ls = line.split() names.append(ls[0]) row = [] # RA value in radians row.append(getRARad(ls[1], ls[2], ls[3])) # Dec value in radians row.append(getDECRad(ls[4], ls[5], ls[6])) # PM RA row.append(float(ls[8].split('=')[-1])) # PM Dec row.append(float(ls[9].split('=')[-1])) # V mag row.append(float(ls[10].split('=')[-1])) # Exposure time row.append(float(ls[11].split('=')[-1])) # Desired Counts row.append(float(ls[16].split('=')[-1])) # Filler not used here row.append(0.) row.append(0.) # Number of exposures row.append(int(ls[19].split('=')[-1])) star_table.append(row) # Save the scriptobs line for later lines.append(line) # Generate a pyEphem object for this target star = ephem.FixedBody() star.name = ls[0] star._ra = ephem.hours(":".join([ls[1], ls[2], ls[3]])) star._dec = ephem.degrees(":".join([ls[4], ls[5], ls[6]])) stars.append(star) return names, np.array(star_table), lines, stars
def windmon(wx): if wx['populated'] == False: return try: wvel = float(wx) except Exception, e: apflog("Exception in windmon: %s" % (e), level='error') return
def altwindmon(wx): if wx['populated'] == False: return try: downval = APF.down.read(binary=True) except Exception, e: apflog("Exception in altwindmon: %s" % (e), level='error') return
def okmon(ok2open): if ok2open['populated'] == False: return try: ok = ok2open # historical except Exception, e: apflog("Exception in okmon for checkapf.OPEN_OK: %s" % (e), level='error') return
def okmon(ok2open): ok = ok2open.read(binary=True) if not checkapf['MOVE_PERM'].read(binary=False): ok = False if APF.wvel > windlim: apflog("Too windy!") ok = False # Also need to check for cloud cover. This could require moving this call below the condition checking code. APF.openOK = ok
def write_as_table(full_codex, outfn): hdr = full_codex[0] success = False try: ascii.write(full_codex[1:], outfn, names=full_codex[0], delimiter=",") success = True except: apflog("Cannot write %s" % (outfn), level="warn") return success
def behindMoon(moon, ras, decs): md = TARGET_MOON_DIST_MAX - TARGET_MOON_DIST_MIN minMoonDist = ((moon.phase / 100.) * md) + TARGET_MOON_DIST_MIN moonDist = np.degrees(np.sqrt((moon.ra - ras)**2 + (moon.dec - decs)**2)) apflog("behindMoon(): Culling stars behind the moon", echo=True) moon_check = moonDist > minMoonDist return moon_check
def setTeqMode(self, mode): apflog("Setting TEQMode to %s" % mode) if self.test: print "Would be setting TEQMode to %s" % mode return self.teqmode.write(mode,wait=False) result = self.teqmode.waitfor('== %s' % mode, timeout=60) if not result: apflog("Error setting the TEQMODE.") raise RuntimeError, "Couldn't set TEQ mode"
def DMReset(self): try: APFLib.write(self.checkapf['ROBOSTATE'], "master operating",timeout=10) except Exception, e: try: ukind = self.checkapf['USERKIND'].read() except: ukind = "Unknown" ostr = "Error: Cannot write to ROBOSTATE, USERKIND = %s, reason: %s" % (ukind,e) apflog(ostr,level='error',echo=True)
def instr_permit(): instr_perm = ktl.read("checkapf","INSTR_PERM",binary=True) userkind = ktl.read("checkapf","USERKIND",binary=True) while not instr_perm or userkind != 3: apflog("Waiting for instrument permission to be true and userkind to be robotic") APFTask.waitfor(parent,True,expression="$checkapf.INSTR_PERM = true",timeout=600) APFTask.waitfor(parent,True,expression="$checkapf.USERKIND = robotic",timeout=600) instr_perm = ktl.read("checkapf","INSTR_PERM",binary=True) userkind = ktl.read("checkapf","USERKIND",binary=True) return True
def run_autoexposure(self,ind=5): cmdpath = '/usr/local/lick/bin/robot/' cmd = os.path.join(cmdpath,'autoexposure') istr = "%d" % (ind) cmdargs = cmd # cmdargs = cmd + " -i " + istr result, code = cmdexec(cmdargs,cwd=os.path.curdir) # result, code = cmdexec([cmd,istr],cwd=os.path.curdir) if not result: apflog("autoexposure failed with code %d" % code, echo=True) return result
def templateConditions(moon, seeing, slowdown): if seeing < 20 and slowdown < 0.7: apflog("moon.phase=%.2f moon.alt=%.2f" % (moon.phase, moon.alt), echo=True, level='debug') if moon.phase < 50 and float(moon.alt) < 0.7: return True else: return False else: return False
def focusTel(self): star = self.find_star() if not star: apflog("Cannot find star near current position!?",level='error',echo=True) return False apflog("Targeting telescope on %s" % star[0], echo=True) if self.slew(star): if self.run_autoexposure(ind=1): if self.run_centerup(): return self.run_focustel() return False
def update_googledex_lastobs(filename, sheetns=["2018B"],ctime=None,certificate='UCSC Dynamic Scheduler-5b98d1283a95.json'): """ Update the online googledex lastobs column assuming things in filename have been observed. update_googledex_lastobs(filename, sheetn="The Googledex",time=None,certificate='UCSC Dynamic Scheduler-5b98d1283a95.json') filename - where the observations are logged """ names, times = ObservedLog.getObserved(filename) if len(names) == 0: return if ctime is None: ctime = datetime.utcfromtimestamp(int(time.time())) for sheetn in sheetns: ws = get_spreadsheet(sheetn=sheetn,certificate=certificate) if ws: vals = ws.get_all_values() else: next col = vals[0].index("lastobs") nobscol = vals[0].index("Nobs") for i, v in enumerate(vals): # Did we observe this target tonight? if v[0] in names: # We observed this target, so update the cell in the worksheet # update_cell(row, col, val) - col and row are 1 indexed otime = times[names.index(v[0])] if isinstance(otime,float): t = datetime.utcfromtimestamp(otime) else: hr, mn = otime t = datetime(ctime.year, ctime.month, ctime.day, hr, mn) jd = float(ephem.julian_date(t)) try: pastdate = float(v[col]) try: n = int(v[nobscol]) except: n = 0 if jd > pastdate: ws.update_cell(i+1, col+1, round(jd, 2) ) ws.update_cell(i+1, nobscol+1, n + 1 ) except: print (v[0], v[col]) ws.update_cell(i+1, col+1, round(jd,2) ) apflog( "Updated %s" % (sheetn),echo=True) return
def countratemon(kcountrate): if kcountrate['populated'] == False: return try: ctr = float(kcountrate['binary']) except: apflog("Cannot read apfguide.countrate",level='warn',echo=True) return APF.countrate *= (1.0*APF.ncountrate)/(APF.ncountrate+1) APF.countrate += ctr/(APF.ncountrate+1) APF.ncountrate += 1 return
def update_local_googledex(intime,googledex_file="googledex.dat", observed_file="observed_targets"): """ Update the local copy of the googledex with the last observed star time. update_local_googledex(time,googledex_file="googledex.dat", observed_file="observed_targets") opens googledex_file and inputs date of last observation from observed_file in principle can use timestamps as well as scriptobs uth and utm values """ names, times = getObserved(observed_file) try: g = open(googledex_file, 'r') except IOError: apflog("googledex file did not exist, so can't be updated",echo=True) return names,times full_codex = pickle.load(g) g.close() codex_cols = full_codex[0] starNameIdx = codex_cols.index("Star Name") lastObsIdx = codex_cols.index("lastobs") for i in range(1, len(full_codex)): row = full_codex[i] if row[starNameIdx] in names: # We have observed this star, so lets update the last obs field obstime = times[names.index(row[starNameIdx])] if isinstance(obstime,float): t = datetime.utcfromtimestamp(obstime) else: hr, min = obstime if type(intime) != datetime: ctime = datetime.now() td = timedelta(0,3600.*7) intime = ctime + td t = datetime(intime.year, intime.month, intime.day, hr, min) # This keeps the JD precision to one decimal point. There is no real reason for this other than # the googledex currently only stores the lastObs field to one decimal precision. Legacy styles FTW. jd = round(float(ephem.julian_date(t)), 2) apflog( "Updating local googledex star %s from time %s to %s" % (row[starNameIdx], row[lastObsIdx], str(jd)),echo=True) row[lastObsIdx] = str(jd) full_codex[i] = row with open(googledex_file, 'w') as f: pickle.dump(full_codex, f) f.close() return names, times
def ucam_powercycle(self, fake=False): if fake: apflog("would have executed @LROOT/bin/robot/robot_power_cycle_ucam") return True else: val = subprocess.call("/usr/local/lick/bin/robot/robot_power_cycle_ucam") if val > 0: apflog("power cycle of UCAM failed",level='alert') return False return True return True
def testBias(self): if self.test: apflog( "Would have taken a single bias frame using APFControl.testBias()", echo=True) else: result = self.apf.testBias() if result == None: apflog("Focusinstr or calibrate or scriptobs are running?!", level='Error', echo=True) if result == False: # this is a UCAM problem rv = self.apf.ucamRestart() if rv == False: apflog("Failure in UCAM status and restart!", level='Alert', echo=True) result = self.apf.ucamStatus() if result is False: apflog("Failure in UCAM status and restart!", level='Alert', echo=True) return result
def run(self): now = time.time() timeout = int(self.stime - now) if now < self.stime: apflog("Waiting until %s to begin" % datetime.fromtimestamp(now), echo=True) APFTask.wait(self.task, True, timeout=timeout) if self.phase_index == 1: bias_result = self.testBias() if bias_result is False: return start = self.phase_index end = self.phase_index + 1 for pi in (start, end): self.phase_index = pi cur_phase = self.possible_phases[pi] APFTask.phase(self.task, self.possible_phases[pi]) apflog("Phase now %s %d" % (cur_phase, pi), echo=True) if cur_phase[0:3] == 'Cal': result = self.calibrate(cur_phase) else: result = self.focusInstr() if result: apflog("Phase %s is complete" % cur_phase, echo=True) else: apflog("Phase %s failed" % cur_phase, echo=True) return self.stop() return
def focus(self,flags="-b"): """Runs the focus routine appropriate for the user.""" if self.test: APFTask.waitFor(self.task, True, timeout=10) print "Test Mode: Would be running focusinstr." return True else: supplies = ('PS1_48V_ENA', 'PS2_48V_ENA') for keyword in supplies: value = motor[keyword].read(binary=True) if value != 1: motor[keyword].write('Enabled', wait=False) apflog("Running focusinstr routine.",echo=True) cmdpath = '/usr/local/lick/bin/robot/' execstr = " ".join(['focusinstr',flags]) cmd = os.path.join(cmdpath,execstr) result, code = cmdexec(cmd,debug=True,cwd=os.getcwd()) if not result: apflog("focusinstr failed with code %d" % code, echo=True) result = False expression="($apftask.FOCUSINSTR_STATUS == 3)" if not APFTask.waitFor(self.task,True,expression=expression,timeout=30): apflog("focusinstr failed" ,echo=True, level="error") result = False expression="($apftask.FOCUSINSTR_LASTFOCUS > 0)" if not APFTask.waitFor(self.task,True,expression=expression,timeout=30): apflog("focusinstr failed to find an adequate focus" ,echo=True, level="error") result = False return result
def calibrate(self, script, time): if self.test: print "Test Mode: calibrate %s %s." % (script, time) APFTask.waitFor(self.task, True, timeout=10) return True if time == 'pre' or 'post': apflog("Running calibrate %s %s" % (script, time), level = 'info') cmd = '/usr/local/lick/bin/robot/calibrate %s %s' % (script, time) result, code = cmdexec(cmd) if not result: apflog("Calibrate %s %s failed with return code %d" % (script, time, code),echo=True) return result else: print "Couldn't understand argument %s, nothing was done." % time
def isOpen(self): """Returns the state of checkapf.WHATSOPN as a tuple (bool, str).""" try: whatstr = str(self.whatsopn) what = whatstr.split() except: apflog("checkapf.WHATSOPN returned a value that str.split cannot split",level='warn',echo=True) return False, '' if hasattr(what,'__iter__'): if "DomeShutter" in what or "MirrorCover" in what or "Vents" in what: return True, what else: return False, '' else: return False, ''
def focusInstr(self): if self.test: apflog("Would have run APFControl.focusinstr", echo=True) result = True else: result = self.apf.focusinstr() apflog("Focus has finished.", echo=True) if not self.test: APFTask.set(self.task, suffix="LAST_OBS_UCSC", value=self.apf.ucam["OBSNUM"].read()) return result
def focus(self, user='******'): """Runs the focus routine appropriate for the style string.""" if user == 'ucsc': if self.test: APFTask.waitFor(self.task, True, timeout=10) print "Test Mode: Would be running Focus cube." return True else: apflog("Running FocusCube routine.",echo=True) cmd = '/u/user/devel_scripts/ucscapf/auto_focuscube.sh pre t' result, code = cmdexec(cmd,cwd='/u/user/devel_scripts/ucscapf') if not result: apflog("Focuscube failed with code %d" % code, echo=True) return result else: print "Don't recognize user %s. Nothing was done." % style
def clearestop(self): if self.test: return True if self.mv_perm.binary == False: apflog("Waiting for permission to move...", echo=True) chk_move = "$checkapf.MOVE_PERM == true" result = APFTask.waitFor(self.task, False, chk_move, timeout=600) if not result: apflog("Can't open. No move permission.",echo=True) return False cmd = '/usr/local/lick/bin/robot/clear_estop' result, code = cmdexec(cmd,debug=True,cwd=os.getcwd()) if result: return True else: return False
def lastAttempted(observed): failed_obs = None try: lastresult = ktl.read("apftask", "SCRIPTOBS_LINE") lastobj = lastresult.split()[0] except: return None if lastobj not in observed.names: apflog("lastAttempted(): Last objects attempted %s" % (lastobj), echo=True) failed_obs = lastobj return failed_obs
def updateHourTable(hour_table, observed, dt, outfn='hour_table', outdir=None): ''' updateHourTableobserved_logs,outfn='hour_table') Updates hour_table with history of observations. ''' if not outdir: outdir = os.getcwd() outfn = os.path.join(outdir, outfn) hours = dict() # observed objects have lists as attributes # in reverse time order, so most recent target observed is first. nobj = len(observed.names) for i in range(0, nobj): own = observed.owners[i] if own not in hours.keys(): hours[own] = 0.0 cur = dt for i in range(0, nobj): hr, mn = observed.times[i] prev = datetime(dt.year, dt.month, dt.day, hr, mn) diff = cur - prev hourdiff = (diff.days * 24 + diff.seconds / 3600.) if hourdiff > 0: hours[observed.owners[i]] += hourdiff cur = prev for ky in hours.keys(): if ky == 'public': hour_table['cur'][hour_table['sheetn'] == 'RECUR_A100'] = hours[ky] else: hour_table['cur'][hour_table['sheetn'] == ky] = hours[ky] try: hour_table.write(outfn, format='ascii', overwrite=True) except Exception as e: apflog("Cannot write table %s: %s" % (outfn, e), level='error', echo=True) return hour_table
def killRobot(self, now=False): """ In case during an exposure there is a need to stop the robot and close up.""" apflog("Terminating Robot.csh") if now: apflog("Abort exposure, terminating robot now.") else: if not ucam['EVENT_STR'].read() == "ControllerReady": apflog("Waiting for current exposure to finish.") ucam['EVENT_STR'].waitfor(" = ReadoutBegin", timeout=1200) apflog("Killing Robot.") ripd, running = self.findRobot() if running: try: APFLib.write(self.robot['scriptobs_control'], "abort") except Exception, e: errstr = "Cannot abort scriptobs: %s" % (e) apflog(errstr,level="Warn",echo=True)
def power_cycle_fousb(self): self.apfmot['FOUSB_POWER'].write(0) rv = APFTask.waitfor(self.task, True, expression="$apfmot.FOUSB_POWER == Off", timeout=10) if rv: APFTask.wait(self.task,True,timeout=1) else: apflog("Cannot power cycle FOUSB",level='error',echo=True) return False self.apfmot['FOUSB_POWER'].write(1) rv = APFTask.waitfor(self.task, True, expression="$apfmot.FOUSB_POWER == On",timeout=10) if rv: APFTask.wait(self.task,True,timeout=1) else: apflog("Cannot power cycle FOUSB",level='error',echo=True) return False return rv
def computePriorities(star_table, cur_dt, observed=None, hour_table=None, rank_table=None): # make this a function, have it return the current priorities, than change references to the star_table below into references to the current priority list new_pri = np.zeros_like(star_table['pri']) # new priorities will be new_pri[star_table['pri'] == 1] += 0 new_pri[star_table['pri'] == 2] -= 20 new_pri[star_table['pri'] == 3] -= 40 cadence_check = (ephem.julian_date(cur_dt) - star_table['lastobs']) good_cadence = cadence_check > star_table['cad'] bad_cadence = np.logical_not(good_cadence) cadence_check /= star_table['cad'] if hour_table is not None: too_much = hour_table['cur'] > hour_table['tot'] done_sheets = hour_table['sheetn'][too_much] else: done_sheets = [] if done_sheets != []: apflog("The following sheets are finished for the night: %s" % (" ".join(done_sheets)), echo=True) bad_pri = np.floor(cadence_check * 100) bad_pri = np.int_(bad_pri) if rank_table is not None: for sheetn in rank_table['sheetn']: if sheetn not in done_sheets: cur = star_table['sheetn'] == sheetn new_pri[cur & good_cadence] += rank_table['rank'][ rank_table['sheetn'] == sheetn] new_pri[cur & bad_cadence] += bad_pri[cur & bad_cadence] else: cur = star_table['sheetn'] == sheetn new_pri[cur & good_cadence] += 100 new_pri[cur & bad_cadence] += bad_pri[cur & bad_cadence] return new_pri
def set_obs_defaults(opt): if opt.name is None or opt.name == "ucsc": opt.owner = 'Vogt' opt.name = 'ucsc' if opt.obsnum == None: apflog("Figuring out what the observation number should be.",echo=False) opt.obsnum = findObsNum(apf) else: opt.obsnum = int(opt.obsnum) elif opt.name == "ucb": apflog("Figuring out what the observation name should be.",echo=False) opt.owner = 'Howard' opt.name = "ucb-" + getnightcode() opt.obsnum=100 else: if opt.owner == None: opt.owner = opt.name return opt
def reboot_warsaw(self): if self.ucam_status.read(binary=True) == 1: rv = self.stop_ucam_software() if rv is False: apflog("UCAM software did not stop running, rebooting anyway",level='Error',echo=True) self.ucam_command.write(2) rv = APFTask.waitfor(self.task, True, expression="$apftask.UCAMLAUNCHER_STATUS == Running", timeout=120) if rv: # yay! self.ucam_command.write(1) rv = self.power_cycle_fousb() return rv else: # this is bad apflog("UCAM host not re-booted",level='Alert',echo=True) return False
def update_googledex_lastobs( filename, sheetn="FakeGoogledex", time=None, certificate='UCSC Dynamic Scheduler-5b98d1283a95.json'): """ Update the online googledex lastobs column assuming things in filename have been observed. update_googledex_lastobs(filename, sheetn="FakeGoogledex",time=None,certificate='UCSC Dynamic Scheduler-5b98d1283a95.json') filename - where the observations are logged """ names, times = getObserved(filename) if len(names) == 0: return if time is None: time = datetime.utcnow() ws = get_spreadsheet(sheetn=sheetn, certificate=certificate) vals = ws.get_all_values() col = vals[0].index("lastobs") for i, v in enumerate(vals): # Did we observe this target tonight? if v[0] in names: # We observed this target, so update the cell in the worksheet # update_cell(row, col, val) - col and row are 1 indexed otime = times[names.index(v[0])] if isinstance(otime, float): t = datetime.fromtimestamp(otime) else: hr, min = otime t = datetime(time.year, time.month, time.day, hr, min) jd = float(ephem.julian_date(t)) try: pastdate = float(v[col]) if jd > pastdate: ws.update_cell(i + 1, col + 1, round(jd, 2)) except: print(v[0], v[col]) apflog("Updated Googledex", echo=True)
def checkBstar(self,haveobserved): """ Master.obsBstar(haveobserved) if observing has begun, and the last observation was a success, set Master.obsBstar to false, writes master_var_3 to the current value of obsBstar The variable VAR_3 still overrides """ vals = APFTask.get("master",["VAR_3"]) if vals['VAR_3'] == 'True': self.obsBstar = True else: self.obsBstar = False if haveobserved and self.lastObsSuccess: self.obsBstar = False try: s="" if self.obsBstar: s="True" APFTask.set(parent,suffix="VAR_3",value=s,wait=False) except: apflog("Error: Cannot communicate with apftask",level="error")
def enoughTime(star_table, stars, idx, row, apf_obs, dt): tot_time = row[DS_NSHOTS] * row[DS_EXPT] tot_time += 210 + ( 2 * 40 + 40 * (row[DS_NSHOTS] - 1) ) # two B star exposures + three 70 second acquisitions and the actual observation readout times vis, star_elevations, fin_els = Visible.is_visible(apf_obs, [stars[idx]], [tot_time]) time_left_before_sunrise = compute_sunrise(dt, horizon='-9') try: apflog("enoughTime(): time for obs= %.1f time until sunrise= %.1f " % (tot_time, time_left_before_sunrise), echo=True) except: apflog("enoughTime(): cannot log times!?!", echo=True) if tot_time < time_left_before_sunrise and vis and time_left_before_sunrise < 14 * 3600.: return True else: return False
def parseFracTable(sheet_table_name='2021B_frac', certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json', outfn=None, outdir=None): apflog("Starting parse of %s" % (sheet_table_name), echo=True) if not outdir: outdir = os.getcwd() if outfn is not None and os.path.exists(os.path.join(outdir, outfn)): sheetns = [] frac = [] with open(os.path.join(outdir, outfn)) as fp: lines = fp.readlines() for ln in lines: row = ln.strip().split() if row[0] == 'sheetn': continue sheetns.append(row[0]) try: frac.append(float(row[0])) except: frac.append(0) return sheetns, frac sheetns = [] frac = [] worksheet = getSpreadsheet(sheetn=sheet_table_name, certificate=certificate) if worksheet: cur_codex = worksheet.get_all_values() if len(cur_codex) <= 0: apflog("Worksheet %s exists but is empty, skipping" % (sheet_table_name), level='error', echo=True) return None, None for row in cur_codex: if row[0] == 'sheetn': continue sheetns.append(row[0]) frac.append(floatDefault(row[1])) wait_time = len(frac) apflog("Sleeping %.1f seconds to keep Google happy" % (wait_time), level="info", echo=True) time.sleep(wait_time) return sheetns, frac
def get_spreadsheet(sheetn="FakeGoogledex", certificate='UCSC Dynamic Scheduler-5b98d1283a95.json'): """ Get the spreadsheet from google worksheet = get_spreadsheet(sheetn="FakeGoogledex",certificate='UCSC Dynamic Scheduler-5b98d1283a95.json') worksheet - the worksheet object returned by the gspread module sheetn - name of the google sheet, defaults to "FakeGoogledex" certificate - certificate used to control access to the google sheet """ # this downloads the googledex from the Google Drive # the certificate must be available # these certificates are generated through the Google Developer Interface # the developer must select the correct API for access # the certificate has an email associated with it, that email must # have the document shared with it to allow access certificate_path = os.path.dirname(__file__) json_key = json.load(open(os.path.join(certificate_path, certificate))) scope = [ 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.me https://spreadsheets.google.com/feeds' ] credentials = SignedJwtAssertionCredentials(json_key['client_email'], json_key['private_key'], scope) gs = gspread.authorize(credentials) apflog("Successfully logged in.", echo=True) spreadsheet = gs.open(sheetn) apflog("Loaded Main %s" % (sheetn), echo=True) worksheet = spreadsheet.sheet1 apflog("Got spreadsheet", echo=True) return worksheet
def updateSheetLastobs(observed_file, sheetns=["Bstar"], ctime=None, certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json', outfn='parsesched.dat', outdir=None): """ Update the online googledex lastobs column assuming things in filename have been observed. updateSheetLastobs(filename, sheetn="The Googledex",time=None,certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json') filename - where the observations are logged sheetns - list of sheets that will be updated ctime - current time as a time stamp certificate - required for authentication outfn - the local copy of the star list outdir - directory of data files returns the number of cells updated """ if not outdir: outdir = os.getcwd() obslog = ObservedLog.ObservedLog( filename=os.path.join(outdir, observed_file)) if len(obslog.names) == 0: return if ctime is None: ctime = datetime.utcfromtimestamp(int(time.time())) outfn = os.path.join(outdir, outfn) star_table = readStarTable(outfn) # OK the following code is going to rely on the fact that owner # is the sheet name # this will need to be updated when we start using coverid needed_sheetns = list(set(obslog.owners)) if 'public' in needed_sheetns: needed_sheetns.remove('public') needed_sheetns.append('RECUR_A100') nupdates = 0 for sheetn in needed_sheetns: ws = getSpreadsheet(sheetn=sheetn, certificate=certificate) if ws: vals = ws.get_all_values() else: continue # The top of the sheet is a list of column names nmcol = vals[0].index('Star Name') col = vals[0].index("lastobs") nobscol = vals[0].index("Nobs") tempcol = vals[0].index("Template") wait_time = len(vals) time.sleep(wait_time) for i, v in enumerate(vals): # Starting at the top of vals is important. # the i is the row in the list of lists. # v is the current row in the list. # The columns that are updated are assigned above # By starting at 0 in vals, we will be indexed to the same row as in the sheet # Did we observe this target tonight? local_name = parseStarname(v[nmcol]) if local_name in obslog.names: # We observed this target, so update the cell in the worksheet # update_cell(row, col, val) - col and row are 1 indexed nameidx = obslog.names.index(local_name) otime = obslog.times[nameidx] taketemp = obslog.temps[nameidx] curowner = obslog.owners[nameidx] if curowner == 'public': curowner = 'RECUR_A100' jd = None try: star_table_row = star_table[ (star_table['name'] == local_name) & (star_table['sheetn'] == sheetn)] except: star_table_row = None # idealy get JD from the local table if star_table_row is not None: if len(star_table_row['lastobs']) > 0: jd = float(star_table_row['lastobs'][0]) # if the above fails, we should be able to use the observing log # but this is JUST the UT hour and minute, not the day so we have to use the otime # value to calculate the full JD if jd is None: if isinstance(otime, float): t = datetime.utcfromtimestamp(otime) else: hr, mn = otime t = datetime(ctime.year, ctime.month, ctime.day, hr, mn) jd = float(ephem.julian_date(t)) try: pastdate = float(v[col]) except: pastdate = 0.0 try: n = int(v[nobscol]) except: n = 0 try: if round(jd, 3) > pastdate and curowner == sheetn: ws.update_cell(i + 1, col + 1, round(jd, 3)) ws.update_cell( i + 1, nobscol + 1, n + 1 ) # sheets are indexed at 1 not at 0 like python lists nupdates += 2 apflog("Updated %s from %.4f to %.4f and %d in %s" % (v[0], pastdate, round(jd, 3), n + 1, sheetn), echo=True) except: apflog("Updated %s to %.4f and %d in %s" % (v[0], round(jd, 3), 1, sheetn), echo=True) ws.update_cell(i + 1, col + 1, round(jd, 3)) ws.update_cell(i + 1, nobscol + 1, 1) nupdates += 2 try: have_temp = v[tempcol] if taketemp == "Y" and have_temp == "N" and curowner == sheetn: ws.update_cell(i + 1, tempcol + 1, "Y") nupdates += 1 apflog("Updated %s to having a template in %s" % (v[0], sheetn), echo=True) except: apflog("Error logging template obs for %s" % (v[0]), echo=True, level='error') return nupdates
def updateLocalStarlist(intime, observed_file="observed_targets", outfn='parsesched.dat', toofn='too.dat', outdir=None): """ Update the local copy of the googledex with the last observed star time. updateLocalStarlist(time,googledex_file="googledex.dat", observed_file="observed_targets") opens googledex_file and inputs date of last observation from observed_file in principle can use timestamps as well as scriptobs uth and utm values """ if not outdir: outdir = os.getcwd() obslog = ObservedLog.ObservedLog( filename=os.path.join(outdir, observed_file)) outfn = os.path.join(outdir, outfn) if os.path.exists(outfn): star_table = readStarTable(outfn) else: return obslog, None toofn = os.path.join(outdir, toofn) if os.path.exists(toofn): too_table = readStarTable(toofn) else: too_table = None for name in obslog.names: index = obslog.names.index(name) obstime = obslog.times[index] owner = obslog.owners[index] if owner == 'public': owner = 'RECUR_A100' if isinstance(obstime, float): t = datetime.utcfromtimestamp(obstime) else: hr, min = obstime if type(intime) != datetime: ctime = datetime.now() td = timedelta(0, 3600. * 7) intime = ctime + td t = datetime(intime.year, intime.month, intime.day, hr, min) jd = round(float(ephem.julian_date(t)), 4) selection = (star_table['name'] == name) & (star_table['sheetn'] == owner) if any(selection): if np.any(jd > star_table['lastobs'][selection]): star_table['lastobs'][selection] = jd star_table['nobs'][selection] += 1 apflog( "Updating local googledex star %s in program %s to %.4f" % (name, owner, jd), echo=True) elif too_table is not None: selection = (too_table['name'] == name) & (too_table['sheetn'] == owner) if any(selection) and jd > too_table['lastobs'][selection]: apflog("Updating ToO target %s from time %.4f to %.4f" % (name, too_table['lastobs'][selection], jd), echo=True) too_table['lastobs'][selection] = jd too_table['nobs'][selection] += 1 astropy.io.ascii.write(star_table, outfn, format='ecsv', overwrite=True) if too_table is not None: astropy.io.ascii.write(too_table, toofn, format='ecsv', overwrite=True) star_table = astropy.table.vstack(too_table, star_table) return obslog, star_table
def parseCodex(config, sheetns=["RECUR_A100"], certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json', prilim=1, sleep=True, hour_constraints=None): # These are the columns we need for scheduling req_cols = ["Star Name", "RA hr", "RA min", "RA sec", \ "Dec deg", "Dec min", "Dec sec", "pmRA", "pmDEC", "Vmag", \ "texp", "I2", "expcount", "decker","Close Companion", "APFnshots", \ "owner", "APFpri", "APFcad", "lastobs", "B-V", \ "cad", "pri", "nexp", "count", "uth","utm","duration", \ "Template", "Nobs", "Total Obs", \ "mode", "raoff", "decoff", "Bstar", "obsblock",\ 'sheetn' \ ] negsearch = re.compile("\-(\d+\.*\d*)") full_codex = retrieveCodex( req_cols, sheetns=sheetns, certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json', sleep=sleep) col_names = full_codex[0] codex = full_codex[1:] didx = findColumns(col_names, req_cols) star_table = initStarTable(req_cols) if hour_constraints is not None: done_names = hour_constraints['runname'][hour_constraints['left'] < 0] else: done_names = [] stars = [] # Build the star table to return to for ls in codex: row = [] if ls[0] == '': continue if "pri" in didx and ls[didx["pri"]] is not None: apfpri = intDefault(ls[didx["pri"]], default=-1) else: apfpri = intDefault(ls[didx["APFpri"]], default=-1) nobs = intDefault(ls[didx["Nobs"]]) totobs = intDefault(ls[didx["Total Obs"]], default=-1) csheetn = checkFlag("sheetn", didx, ls, "\A(.*)", 'public') if totobs > 0 and nobs >= totobs: continue if apfpri < prilim: continue if csheetn in done_names: continue if apfpri > MAX_PRI: apfpri = MAX_PRI name = parseStarname(ls[didx["Star Name"]]) # Get the RA raval, rahr, ramin, rasec = Coords.getRARad(ls[didx["RA hr"]], ls[didx["RA min"]], ls[didx["RA sec"]]) if raval is None: # alarm apflog("Error in RA coordinates for %s" % (name), level='warn', echo=True) continue # Get the DEC decval, decdeg, decmin, decsec = Coords.getDECRad( ls[didx["Dec deg"]], ls[didx["Dec min"]], ls[didx["Dec sec"]]) if decval is None: # alarm apflog("Error in Dec coordinates for %s" % (name), level='warn', echo=True) continue # why are we doing this you may ask? # we use Google sheets which cannot have -0 for a value # but if we pass a value like 00:-16:00 to eostele, it generates # an incorrect declination value # so, we move the - to the front of the sexagesimal string # the radian values above are only used for the scheduler, we still # command the telescope in the raw units if name and raval and decval: star_table["name"].append(name) star_table['ra'].append(raval) star_table['RA hr'].append(rahr) star_table['RA min'].append(ramin) star_table['RA sec'].append(rasec) star_table['dec'].append(decval) star_table["Dec deg"].append(decdeg) star_table["Dec min"].append(decmin) star_table["Dec sec"].append(decsec) else: continue mode = checkFlag("mode", didx, ls, "\A(b|B|a|A|c|C)", config["mode"]) if type(mode) == str: mode = mode.upper() star_table['mode'].append(mode) star_table['raoff'].append( checkFlag("raoff", didx, ls, "\A((\+|\-)?\d+\.?\d*)", config["raoff"])) star_table['decoff'].append( checkFlag("decoff", didx, ls, "\A((\+|\-)?\d+\.?\d*)", config["decoff"])) for coln in ("pmRA", "pmDEC"): star_table[coln].append(floatDefault(ls[didx[coln]])) star_table['Vmag'].append(floatDefault(ls[didx["Vmag"]], default=15.0)) star_table['texp'].append(floatDefault(ls[didx["texp"]], default=1200)) expcount = floatDefault(ls[didx["expcount"]], default=1e9) if expcount > EXP_LIM: expcount = EXP_LIM star_table['expcount'].append(expcount) if "nexp" in didx and ls[didx["nexp"]] is not None: star_table['nexp'].append(intDefault(ls[didx["nexp"]], default=1)) elif "count" in didx and ls[didx['count']] is not None: star_table['nexp'].append(intDefault(ls[didx["count"]], default=1)) else: star_table['nexp'].append( intDefault(ls[didx["APFnshots"]], default=1)) # scheduler specific if "cad" in didx and ls[didx['cad']] is not None: star_table['cad'].append(floatDefault(ls[didx["cad"]], default=0.7)) else: star_table['cad'].append( floatDefault(ls[didx["APFcad"]], default=0.7)) star_table['pri'].append(apfpri) star_table["lastobs"].append( floatDefault(ls[didx["lastobs"]], default=0)) inval = floatDefault(ls[didx["B-V"]], default=0.7) if inval < 0: inval = 1. if coln is 'B-V' and inval > 2: inval = 1 star_table['B-V'].append(inval) # Nobs star_table['nobs'].append(nobs) # Total Obs if totobs >= 0: star_table['totobs'].append(totobs) else: star_table['totobs'].append(0) check = checkFlag("Close Companion", didx, ls, "\A(y|Y)", "") if check == "Y" or check == "y": star_table['do'].append(check) else: star_table['do'].append("") star_table['decker'].append( checkFlag("decker", didx, ls, "\A(W|N|T|S|O|K|L|M|B)", config["decker"])) i2select = checkFlag("I2", didx, ls, "\A(n|N)", config["I2"]) star_table['I2'].append(i2select.upper()) tempselect = checkFlag("Template", didx, ls, "\A(n|N)", 'Y') star_table['Template'].append(tempselect.upper()) star_table['owner'].append( checkFlag("owner", didx, ls, "\A(\w?\.?\w+)", config["owner"])) star_table['obsblock'].append( checkFlag("obsblock", didx, ls, "\A(\w+)", config["obsblock"])) # star_table['inst'].append(checkFlag("inst",didx,ls,"(levy|darts)",config['inst']).lower()) # need to check raoff and decoff values and alarm on failure if 'Bstar' in didx: star_table['Bstar'].append( checkFlag('Bstar', didx, ls, "(Y|y)", 'N')) star_table['sheetn'].append(csheetn) else: if 'RECUR_A100' in csheetn: star_table['Bstar'].append("Y") star_table['sheetn'].append('RECUR_A100') else: star_table['Bstar'].append("N") star_table['sheetn'].append(csheetn) badkeylist = [] for k in star_table.keys(): if len(star_table[k]) == 0: badkeylist.append(k) for k in badkeylist: del star_table[k] # This just reorders the columns # This way the ascii table has these columns in front to make finding targets by specific programs easier star_table_names = list(star_table.keys()) for n in ('Dec sec', 'Dec min', 'Dec deg', 'RA sec', 'RA min', 'RA hr', 'APFpri', 'sheetn', 'name'): if n in star_table_names: star_table_names.remove(n) star_table_names = [n] + star_table_names star_table = astropy.table.Table(star_table, names=star_table_names) return star_table
def calibrate(self, phase): apflog("Starting calibrate %s script." % (phase), level='Info', echo=True) if self.test: apflog( "Would have waited for permission (APFControl.instrPermit()) for phase %s" % (phase), echo=True) else: self.apf.instrPermit() if self.test: apflog("Would have run APFControl.ucamStatus() for phase %s" % (phase), echo=True) result = True else: result = self.apf.ucamStatus() if result is False: apflog("Failure in UCAM status and restart!", level='Alert', echo=True) return False time = phase[4:].lower() if self.test: apflog("Would have run APFControl.calibrate for time %s" % (time), echo=True) result = True else: result = self.apf.calibrate(script=opt.calibrate, time=time) if not self.test: APFTask.set(self.task, suffix="LAST_OBS_UCSC", value=self.apf.ucam["OBSNUM"].read()) if result == False: apflog("Calibrate Pre has failed. Trying again", level='warn', echo=True) self.apf.instrPermit() result = self.apf.calibrate(script=opt.calibrate, time='pre') if not result: apflog( "Error: Calibrate Pre has failed twice. Observer is exiting.", level='error', echo=True) self.apf.turnOffLamps() return result
def retrieveCodex(req_cols, sheetns=["The Googledex"], certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json', sleep=True): """retrieveCodex(req_cols,sheetns=["The Googledex"],certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json') returns the "codex", a list of lists containing all of the columns in the req_cols list, source of the data are the Google sheets named in sheetns, needs a certificate to authenticate. - req_cols : a list of column names in the sheets that are required for the final list of lists - sheetns : sheets to download from - certificate : the thing that allows authentication """ full_codex = [] # These are the columns we need for scheduling full_codex.append(req_cols) failed = [] for sheetn in sheetns: wait_time = 0 worksheet = getSpreadsheet(sheetn=sheetn, certificate=certificate) if worksheet: cur_codex = None more_sleeping = 10. while cur_codex is None: try: cur_codex = worksheet.get_all_values() except: if sleep: time.sleep(more_sleeping) cur_codex = None if len(cur_codex) <= 0: apflog("Worksheet %s exists but is empty, skipping" % (sheetn), level='error', echo=True) continue didx = findColumns(cur_codex[0], req_cols) for row in cur_codex[1:]: nrow = [] for c in req_cols: if c in didx.keys(): nrow.append(row[didx[c]]) else: if c is 'sheetn': nrow.append(sheetn) else: nrow.append(None) full_codex.append(nrow) wait_time += .3 if sleep and ((sheetns.index(sheetn) + 1) < len(sheetns)): apflog("Sleeping %.1f seconds to keep Google happy" % (wait_time), level="info", echo=True) time.sleep(wait_time) return full_codex
def getSpreadsheet(sheetn="The Googledex", certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json'): """ Get the spreadsheet from google worksheet = getSpreadsheet(sheetn="The Googledex",certificate='UCSC_Dynamic_Scheduler-4f4f8d64827e.json') worksheet - the worksheet object returned by the gspread module sheetn - name of the google sheet, defaults to "The Googledex" certificate - certificate used to control access to the google sheet """ # this downloads the googledex from the Google Drive # the certificate must be available # these certificates are generated through the Google Developer Interface # the developer must select the correct API for access # the certificate has an email associated with it, that email must # have the document shared with it to allow access certificate_path = os.path.dirname("/usr/local/lick/data/apf/master/") if os.path.exists(certificate_path) is False: certificate_path = os.path.dirname(__file__) finpath = os.path.join(certificate_path, certificate) json_key = json.load(open(finpath)) scope = [ 'https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive' ] credentials = ServiceAccountCredentials.from_json_keyfile_name( os.path.join(certificate_path, certificate), scope) try: gs = gspread.authorize(credentials) apflog("Successfully logged in.", echo=True) except Exception as e: apflog("Cannot log into Google API.", echo=True, level='error') apflog(e, echo=True, level='error') return None worksheet = None tries = 0 while worksheet is None and tries < 3: tries = tries + 1 try: spreadsheet = gs.open(sheetn) apflog("Loaded Main %s" % (sheetn), echo=True) worksheet = spreadsheet.sheet1 apflog("Got spreadsheet", echo=True) errlog = None except Exception as e: errlog = "Cannot Read %s: %s" % (sheetn, e) time.sleep(1) if worksheet is None and errlog is not None: apflog(errlog, echo=True, level='error') return worksheet
def makeHourTable(sheet_table_name, dt, outfn='hour_table', outdir=None, frac_fn='frac_table', hour_constraints=None): if not outdir: outdir = os.getcwd() outfn = os.path.join(outdir, outfn) if os.path.exists(outfn): hour_table = astropy.table.Table.read(outfn, format='ascii') return hour_table frac_fn = os.path.join(outdir, frac_fn) if os.path.exists(frac_fn): frac_table = astropy.table.Table.read(frac_fn, format='ascii') else: sheetns, fracs = ParseUCOSched.parseFracTable( sheet_table_name=sheet_table_name, outfn=frac_fn) frac_table = [] for i in range(0, len(fracs)): frow = [] frow.append(sheetns[i]) frow.append(fracs[i]) frac_table.append(frow) frac_table = astropy.table.Table(rows=frac_table, names=['sheetn', 'frac']) try: frac_table.write(frac_fn, format='ascii') except Exception as e: apflog("Cannot write table %s: %s" % (frac_fn, e), level='error', echo=True) hour_table = astropy.table.Table(frac_table, names=['sheetn', 'frac']) sunset, sunrise = computeSunsetRise(dt, horizon='-9') if sunrise < sunset: sunrise += 86400 tot = sunrise - sunset tot /= 3600. hour_table['tot'] = np.abs(tot * hour_table['frac']) hour_table['cur'] = 0.0 * hour_table['frac'] if hour_constraints is not None: if 'runname' in hour_constraints.keys( ) and 'left' in hour_constraints.keys(): for runname in hour_constraints['runname']: if hour_constraints['left'][ hour_constraints['runname'] == runname] < hour_table[ 'tot'][hour_table['sheetn'] == runname]: hour_table['tot'][ hour_table['sheetn'] == runname] = hour_constraints[ 'left'][hour_constraints['runname'] == runname] elif hour_constraints['left'][hour_constraints['runname'] == runname] < 0: hour_table['tot'][hour_table['sheetn'] == runname] = -1.0 try: hour_table.write(outfn, format='ascii') except Exception as e: apflog("Cannot write table %s: %s" % (outfn, e), level='error', echo=True) return hour_table
def getNext(ctime, seeing, slowdown, bstar=False, template=False, sheetns=["RECUR_A100"], owner='public', outfn="googledex.dat", toofn="too.dat", outdir=None, focval=0, inst='', rank_sheetn='rank_table', frac_sheet=None): """ Determine the best target for UCSC team to observe for the given input. Takes the time, seeing, and slowdown factor. Returns a dict with target RA, DEC, Total Exposure time, and scritobs line """ global last_objs_attempted if not outdir: outdir = os.getcwd() dt = computeDatetime(ctime) config = configDefaults(owner) apflog("getNext(): Finding target for time %s" % (dt), echo=True) if slowdown > SLOWDOWN_MAX: apflog( "getNext(): Slowndown value of %f exceeds maximum of %f at time %s" % (slowdown, SLOWDOWN_MAX, dt), echo=True) return None try: apfguide = ktl.Service('apfguide') stamp = apfguide['midptfin'].read(binary=True) ptime = datetime.utcfromtimestamp(stamp) except: if type(dt) == datetime: ptime = dt else: ptime = datetime.utcfromtimestamp(int(time.time())) apflog("getNext(): Updating star list with previous observations", echo=True) observed, star_table = ParseUCOSched.updateLocalStarlist( ptime, outfn=outfn, toofn=toofn, observed_file="observed_targets") hour_table = None if frac_sheet is not None: hour_table = makeHourTable(frac_sheet, dt) hour_table = updateHourTable(hour_table, observed, dt) # Parse the Googledex # Note -- RA and Dec are returned in Radians if star_table is None: apflog("getNext(): Parsing the star list", echo=True) star_table, stars = ParseUCOSched.parseUCOSched(sheetns=sheetns, outfn=outfn, outdir=outdir, config=config) else: stars = ParseUCOSched.genStars(star_table) targNum = len(stars) # List of targets already observed lastfailure = lastAttempted(observed) if lastfailure is not None: last_objs_attempted.append(lastfailure) if len(last_objs_attempted) == 5: apflog("getNext(): 5 failed acquisition attempts", level="warn", echo=True) ### # Need to update the googledex with the lastObserved date for observed targets # Scriptobs line uth utm can be used for this # Need to convert a uth and utm to a JD quickly. # timedelta = now - uth,utm : minus current JD? ### apf_obs = makeAPFObs(dt) # APF latitude in radians apf_lat = (37 + 20 / 60. + 33.1 / 3600.) * np.pi / 180. # Calculate the moon's location moon = ephem.Moon() moon.compute(apf_obs) do_templates = template and templateConditions(moon, seeing, slowdown) apflog("getNext(): Parsed the Googledex...", echo=True) apflog("getNext(): Finding B stars", echo=True) # Note which of these are B-Stars for later. bstars = (star_table['Bstar'] == 'Y') | (star_table['Bstar'] == 'y') # Distance to stay away from the moon totexptimes = np.zeros(targNum, dtype=float) totexptimes = star_table['texp'] * star_table['nexp'] + 40 * ( star_table['nexp'] - 1) available = np.ones(targNum, dtype=bool) cur_elevations = np.zeros(targNum, dtype=float) scaled_elevations = np.zeros(targNum, dtype=float) # Is the target behind the moon? moon_check = behindMoon(moon, star_table['ra'], star_table['dec']) available = available & moon_check if len(last_objs_attempted) > 0: for n in last_objs_attempted: attempted = (star_table['name'] == n) available = available & np.logical_not( attempted) # Available and not observed if bstar: # We just need a B star apflog("getNext(): Selecting B stars", echo=True) available = available & bstars else: apflog("getNext(): Culling B stars", echo=True) available = available & np.logical_not(bstars) # Calculate the exposure time for the target # Want to pass the entire list of targets to this function apflog("getNext(): Computing exposure times", echo=True) exp_counts = star_table['expcount'] # Is the exposure time too long? apflog("getNext(): Removing really long exposures", echo=True) time_check = timeCheck(star_table, totexptimes, dt, hour_table) available = available & time_check if np.any(available) == False: apflog("getNext(): Not enough time left to observe any targets", level="error", echo=True) return None apflog("getNext(): Computing star elevations", echo=True) fstars = [s for s, _ in zip(stars, available) if _] vis, star_elevations, fin_star_elevations, scaled_els = Visible.visible( apf_obs, fstars, totexptimes[available], shiftwest=True) currently_available = available if len(star_elevations) > 0: currently_available[available] = currently_available[available] & vis else: apflog("getNext(): Couldn't find any suitable targets!", level="error", echo=True) return None cur_elevations[available] += star_elevations[vis] scaled_elevations[available] += scaled_els[vis] if slowdown > SLOWDOWN_THRESH or seeing > SEEING_THRESH: bright_enough = star_table['Vmag'] < SLOWDOWN_VMAG_LIM available = available & bright_enough # Now just sort by priority, then cadence. Return top target if len(star_table['name'][available]) < 1: apflog("getNext(): Couldn't find any suitable targets!", level="error", echo=True) return None final_priorities = computePriorities(star_table, dt, rank_table=makeRankTable(rank_sheetn), hour_table=hour_table, observed=observed) try: pri = max(final_priorities[available]) sort_i = (final_priorities == pri) & available except: apflog("getNext(): Couldn't find any suitable targets!", level="error", echo=True) return None if bstar: sort_j = cur_elevations[sort_i].argsort()[::-1] focval = 2 else: sort_j = scaled_elevations[sort_i].argsort()[::-1] allidx, = np.where(sort_i) idx = allidx[sort_j][0] t_n = star_table['name'][idx] o_n = star_table['sheetn'][idx] p_n = final_priorities[idx] apflog("getNext(): selected target %s for program %s at priority %.0f" % (t_n, o_n, p_n)) nmstr = "getNext(): star names %s" % (np.asarray( star_table['name'][sort_i][sort_j])) pristr = "getNext(): star priorities %s" % (np.asarray( final_priorities[sort_i][sort_j])) mxpristr = "getNext(): max priority %d" % (pri) shstr = "getNext(): star sheet names %s" % (np.asarray( star_table['sheetn'][sort_i][sort_j])) if bstar: elstr = "getNext(): Bstar current elevations %s" % ( cur_elevations[sort_i][sort_j]) else: elstr = "getNext(): star scaled elevations %s" % ( scaled_elevations[sort_i][sort_j]) apflog(nmstr, echo=True) apflog(shstr, echo=True) apflog(pristr, echo=True) apflog(mxpristr, echo=True) apflog(elstr, echo=True) stars[idx].compute(apf_obs) res = makeResult(stars, star_table, totexptimes, final_priorities, dt, idx, focval=focval, bstar=bstar, mode=config['mode']) if do_templates and star_table['Template'][idx] == 'N' and star_table[ 'I2'][idx] == 'Y': bidx, bfinidx = findBstars(star_table, idx, bstars) if enoughTimeTemplates(star_table, stars, idx, apf_obs, dt): bline = makeScriptobsLine(star_table[bidx], dt, decker="N", I2="Y", owner=res['owner'], focval=2) line = makeScriptobsLine(star_table[idx], dt, decker="N", I2="N", owner=res['owner'], temp=True) bfinline = makeScriptobsLine(star_table[bfinidx], dt, decker="N", I2="Y", owner=res['owner'], focval=0) res['SCRIPTOBS'] = [] res['SCRIPTOBS'].append(bfinline + " # temp=Y end") res['SCRIPTOBS'].append(line + " # temp=Y") res['SCRIPTOBS'].append(bline + " # temp=Y") res['isTemp'] = True apflog("Attempting template observation of %s" % (star_table['name'][idx]), echo=True) return res