9 Various utilities for use in VisAOScript
15 import sys, os, time, calendar, glob, math, copy, pyfits, shutil
18 def get_visao_filename(prefix, t):
20 Gets the standard VisAO filename (without the leading base name) for a time t.
22 prefix = prefix of the filename
23 t = the time in seconds since the epoch.
24 Returns: a string containing the VisAO filename time portion.
27 t_usec =
int((t - math.floor(t))*1e6)
29 res =
"%s_%04i%02i%02i%02i%02i%02i%06i" % (prefix, gmt.tm_year, gmt.tm_mon, gmt.tm_mday, gmt.tm_hour, gmt.tm_min, gmt.tm_sec, t_usec)
33 def get_visao_filename_now(prefix):
37 sd = get_visao_filename(prefix, time.time())
41 def visao_wait(waitt):
45 t = time.mktime(time.localtime())
46 while(time.mktime(time.localtime()) - t < waitt):
52 Return a sequence containing the names of the VisAO bandpasses
54 filters=[
"r'", "i'", "z'", "Ys"]
57 def xDither(scale = 1.0):
59 Return a the x offsets for a standard 5 point dither pattern (starting from 0)
61 scale = multiplicative scale to multiply by, default is 1.0
63 dx = [-.5*scale, 1.*scale, 0.*scale, -1.*scale]
67 def yDither(scale = 1.0):
69 Return the y offsets for a standard 5 point dither pattern (starting from 0)
71 scale = multiplicative scale to multiply by, default is 1.0
73 dy = [.5*scale, 0.*scale, -1.*scale, 0.*scale]
79 Say something using the MagAO sound server as the VisAO personality.
81 txt = the words to say
83 cmd =
"echo vicki %s | nc zorro.lco.cl 50000" % (txt)
86 def visao_script_complete():
87 visao_say(
"viz ay oh script complete")
89 def visao_script_error():
90 visao_say(
"viz ay oh script airor")
92 def visao_script_stopped():
93 visao_say(
"viz ay oh script stopped")
97 Issue an audible alert to the terminal.
99 n = the number of alerts to issue with a 2 second wait between.
109 def parseSysLog(fname):
117 tv_sec = float(elem[0])
118 tv_used = float(elem[1])
120 fsec = tv_sec + tv_used/1e6
133 def timestampFileName(fname, prefix="V47_"):
135 yr =
int(fname[off:off+4])
136 mo =
int(fname[off+4:off+6])
137 d =
int(fname[off+6:off+8])
138 hr =
int(fname[off+8:off+10])
139 mn =
int(fname[off+10:off+12])
140 s =
int(fname[off+12:off+14])
141 usec =
int(fname[off+14:off+20])
143 tv = calendar.timegm([yr, mo, d, hr, mn, float(s)+float(usec)/1e6,
'',
'' ,
''])
148 def getFileTimes(dir, prefix="V47_", ext=".fits"):
150 if(dir[len(dir)-1] !=
'/'):
153 fnames = glob.glob(dir+prefix+
"*"+ext)
158 fn = dfn[len(dir):len(dfn)]
159 ft = timestampFileName(fn, prefix)
161 joetimes = getJoeTimes(dfn)
165 res.append([dfn, fn, ft, joetimes[0], joetimes[1]])
167 return sorted(res, key=
lambda times: times[2])
169 def getOneFileTime(dir, prefix="V47_", ext=".fits"):
171 if(dir[len(dir)-1] !=
'/'):
174 fnames = glob.glob(dir+prefix+
"*"+ext)
175 fnames.sort(key=os.path.getmtime)
183 fn = dfn[len(dir):len(dfn)]
184 ft = timestampFileName(fn, prefix)
186 joetimes = getJoeTimes(dfn)
190 res.append([dfn, fn, ft, joetimes[0], joetimes[1]])
192 return sorted(res, key=
lambda times: times[2])
194 def chooseLogFile(ftime, loglist):
196 for i
in range(len(loglist)-1):
197 if(loglist[i][2] <= ftime
and loglist[i+1][2] >= ftime):
202 def getVisAOExpTimes(ftime, rotime, exptime):
204 expend = ftime-rotime
205 expst = ftime-rotime-exptime
207 return [expst, expend]
209 def getLogEntries(ftime1, ftime2, loglist):
215 ist = chooseLogFile(ftime1, loglist)
220 iend = chooseLogFile(ftime2, loglist)
223 if(iend < len(loglist) - 1):
228 entries = parseSysLog(loglist[ist][0])
230 for i
in range(ist+1, iend):
231 entries.extend(parseSysLog(loglist[i][0]))
236 def getCCD47MinExp(pxrt,window, bin):
238 Get the minimum exposure time (also the readout time) for the VisAO CCD47 image.
240 pxrt = the pixel rate (2500,250, or 80)
241 window = the window size (1024, 512,256, 64, or 32)
242 bin = the binning (1,2,16)
244 minexptime = the minimum exposure time
251 minexptime = 1./3.528
253 minexptime = 1./0.440
255 minexptime = 1./0.144
258 minexptime = 1./6.703
260 minexptime = 1./1.487
262 minexptime = 1./0.535
265 minexptime = 1./1.772
268 minexptime = 1./31.283
271 minexptime = 1./42.779
275 minexptime = 1./0.551
282 print 'getCCD47MinExp: Read out time not found for parameters'
288 def getJoeTimes(fname):
290 Get the exposure time and readout time for a VisAO CCD47 image.
296 hdu = pyfits.open(fname)
298 exptime = float(hdu[0].header[
"EXPTIME"])
299 pxrt = float(hdu[0].header[
"V47PIXRT"])
300 window = float(hdu[0].header[
"V47WINDX"])
301 bin = float(hdu[0].header[
"V47BINX"])
340 print 'Read out time not found for %s' % fname
345 return [rotime, exptime]
347 def getLoopStat(datadir, aosysdir, force = 0):
349 Calculates loop status and avg WFE during each image in a directory.
350 Loop status is closed iff the loop was closed during the entire exposure. If exposure time is less than 1 second, the nearest time is used.
351 A check of WFE is first made for inf and spuriously large values, which are interpolated. Avg WFE is calculated as the average of the 1 second averages recorded in the system logs.
352 If exptime is shorter than 1 second, the nearest WFE entry is used. Std dev of WFE is calculated as the average of the 1 second averages recorded in the system logs.
354 Writes a data file to the datadir with the wfe, and a file with the second by second system status.
357 datadir = directory where the images are located
358 aosysdir = directory where the ao system logs are located
359 force = perform update even if the file has already been updated.
362 list of [full_path, file_name, loop_status, wfe_avg, std_avg]
366 datalistone = getOneFileTime(datadir)
368 if len(datalistone) < 1:
369 print 'No VisAO fits files found in %s' % datadir
370 return ([
'-1',
'-1',
'-1',
'-1',
'-1'])
372 if checkLoopStat(datalistone) == 1
and force == 0:
373 return ([
'-2',
'-2',
'-2',
'-2',
'-2'])
376 datalist = getFileTimes(datadir)
380 loglist = getFileTimes(aosysdir,
'aosys_',
'*.txt')
384 if len(datalist) < 1:
385 print 'No visao fits files found'
386 return ([
'-1',
'-1',
'-1',
'-1',
'-1'])
388 print 'Getting system logs:'
389 print ' Directory: %s' % datadir
390 print ' Start time: %f' % datalist[0][2]
391 print ' End time: %f' % datalist[len(datalist)-1][2]
392 print ' Elapsed time: %f' % (float(datalist[len(datalist)-1][2]) - float(datalist[0][2]))
396 rawentries = getLogEntries(datalist[0][2]-datalist[0][3]-datalist[0][4], datalist[len(datalist)-1][2], loglist)
399 entries = copy.deepcopy(rawentries)
401 print 'Starting WFE interpolation'
403 for i
in range(1,len(entries)-1):
404 if entries[i][2] ==
'inf' or float(entries[i][2]) > 1e5:
408 if(entries[i-q][0] !=
'inf' and float(entries[i-q][2] < 1e5)):
414 t0 = float(entries[i-n][0])
415 wfe0 = float(entries[i-n][2])
416 std0 = float(entries[i-n][3])
418 t1 = float(entries[i][0])
420 if(entries[i+1][0] !=
'inf' and float(entries[i+1][2] < 1e5)):
428 if(entries[i+q][0] !=
'inf' and float(entries[i+q][2] < 1e5)):
431 if i+q > len(entries)-1:
432 n = len(entries)-1 - i
435 t2 = float(entries[i+n][0])
436 wfe2 = float(entries[i+n][2])
437 std2 = float(entries[i+n][2])
439 wfe1 = wfe0 + (wfe2-wfe0)/(t2-t0)*(t1-t0)
440 std1 = std0 + (std2-std0)/(t2-t0)*(t1-t0)
442 entries[i][2] = str(wfe1)
443 entries[i][3] = str(std1)
446 f = file(datadir+
'/wfe.txt',
'w')
447 for i
in range(len(rawentries)):
448 f.write(
"%s %s %s %s %s %s \n" % (rawentries[i][0], rawentries[i][1], rawentries[i][2], rawentries[i][3], entries[i][2], entries[i][3]))
452 for i
in range(len(datalist)):
454 joetime = getJoeTimes(datalist[i][0])
456 etimes = getVisAOExpTimes(datalist[i][2], joetime[0], joetime[1])
458 if len(entries) <= 0:
459 print 'No AOSYS entries'
460 return ([
'-1',
'-1',
'-1',
'-1',
'-1'])
464 while(float(entries[j][0]) <= etimes[0]):
466 if j >= len(entries):
break
478 while(float(entries[k][0]) <= etimes[1]
and k < len(entries)-1):
480 if(
int(entries[k][1]) != 1): loopst = 0
481 wfeavg = wfeavg + float(entries[k][2])
482 stdavg = stdavg + float(entries[k][3])
484 if k >= len(entries):
break
486 if(k < len(entries)-1):
489 if(
int(entries[j][1]) != 1
or int(entries[j+1][1]) != 1): loopst = 0
490 wfeavg = 0.5*(float(entries[j][2]) + float(entries[j][2]))
491 if(j < len(entries)-1):
492 stdavg = 0.5*(float(entries[j][3]) + float(entries[j+1][3]))
496 wfeavg = wfeavg/(k-j)
497 stdavg = stdavg/(k-j)
499 statent.append([datalist[i][0], datalist[i][1], loopst, wfeavg, stdavg])
501 f = file(datadir+
'/status.txt',
'w')
502 for i
in range(len(statent)):
503 f.write(
"%s %i %f %f\n" % (statent[i][1], statent[i][2], statent[i][3], statent[i][4]))
507 def updateLoopStat(statlist):
513 hdu = pyfits.open(im[0],
'update')
516 hdu[0].header.update(
'AOLOOPST',
"CLOSED",comment=
"AO loop status during exposure")
519 hdu[0].header.update(
'AOLOOPST',
"OPEN",comment=
"AO loop status during exposure")
521 hdu[0].header.update(
'AVGWFE', im[3],comment=
'Avg WFE (nm rms phase)')
522 hdu[0].header.update(
'STDWFE', im[4],comment=
'Std Dev of WFE (nm rms phase)')
524 hdu[0].header.update(
'HISTORY',
'VisAO system status update applied %s' % time.asctime(time.localtime(time.time())))
528 def checkLoopStat(statlist):
531 if len(statlist) <= 0:
535 hdu = pyfits.open(im[0],
'update')
537 if hdu[0].header.get(
'AOLOOPST') !=
'NOT PROCESSED':
543 def getGoodIms(dir, movedir):
545 run this after headers updated
547 if(dir[len(dir)-1] !=
'/'):
550 if(movedir[len(movedir)-1] !=
'/'):
551 movedir = movedir +
'/'
553 fnames = glob.glob(dir+
"V47_*.fits")
562 hdu = pyfits.open(dfn)
563 fn = dfn[len(dir):len(dfn)]
565 if (hdu[0].header[
"AOLOOPST"] ==
'OPEN' and hdu[0].header[
"VIMTYPE"] ==
'SCIENCE'):
569 if (hdu[0].header[
"AOLOOPST"] ==
'CLOSED' and hdu[0].header[
"VIMTYPE"] ==
'SCIENCE'):
571 good.append([dfn, fn])
573 if (hdu[0].header[
"VIMTYPE"] ==
'DARK'):
575 good.append([dfn, fn])
583 newf = movedir + f[1]
584 shutil.copyfile(f[0], newf)
587 def getDarks(dir, movedir):
589 run this after headers updated
591 if(dir[len(dir)-1] !=
'/'):
594 if(movedir[len(movedir)-1] !=
'/'):
595 movedir = movedir +
'/'
597 fnames = glob.glob(dir+
"V47_*.fits")
606 hdu = pyfits.open(dfn)
607 fn = dfn[len(dir):len(dfn)]
609 if (hdu[0].header[
"VIMTYPE"] ==
'DARK'):
611 good.append([dfn, fn])
619 newf = movedir + f[1]
620 shutil.copyfile(f[0], newf)
624 Returns a list of the standard VisAO filters
626 filters=[
"r'", "i'", "z'", "Ys"]
629 def xDither(scale = 1.0):
631 Returns a list of x offsets for a gimbal dither. Units are mm.
633 scale = total width of the pattern in mm
635 dx = [-.5*scale, 1.*scale, 0.*scale, -1.*scale]
639 def yDither(scale = 1.0):
641 Returns a list of y offsets for a gimbal dither. Units are mm.
643 scale = total width of the pattern in mm
645 dy = [.5*scale, 0.*scale, -1.*scale, 0.*scale]
650 cmd =
"echo vicki %s | nc zorro.lco.cl 50000" % (txt)
653 def visao_script_complete():
654 visao_say(
"viz ay oh script complete")
656 def visao_script_error():
657 visao_say(
"viz ay oh script airor")
659 def visao_script_stopped():
660 visao_say(
"viz ay oh script stopped")