#!/usr/bin/env python try: import pylab from pylab import * from matplotlib import * except: print 'ERROR -- Cannot find the python module matplotlib in your executable path' print ' download and install from http://matplotlib.sourceforge.net\n' usage() # numpy try: import numpy from numpy import * seterr(all="ignore") except: print 'ERROR -- Cannot find the python module numpy in your executable path' print ' download and install from http://numpy.scipy.org\n' usage() # pyfits try: import pyfits except: print 'ERROR -- Cannot find the python module pyfits in your executable path' print ' download and install from http://www.stsci.edu/resources/software_hardware/pyfits\n' usage() import getopt, sys, urllib, time, re # global variables ffifile = False; aperfile = False; maskfile = 'KeplerFFI.txt' plotfile = 'KeplerFFI.png'; pimg = None; mask = [] xmin = 0.0; xmax = 1000.0; ymin = 0.0; ymax = 1000.0; zmin = False; zmax = False kepid = ''; ra = ''; dec = ''; kepmag = ''; season = ''; quarter = -1 skygroup = ''; channel = ''; module = ''; output = ''; column = ''; row = '' cmap = 'jet'; aid = None; cid = None; did = None; eid = None; fid = None pylab_latex = True # ----------------------------------------------------------- # core code def main(): global pimg, zmin, zmax, xmin, xmax, ymin, ymax, quarter global kepid, ra, dec, kepmag, skygroup, season, channel global module, output, row, column, cmap, maskfile, plotfile global pylab_latex # input arguments kepid,ra,dec,ffifile,zmin,zmax,zscale,cmap,npix = parameters() if kepid: maskfile = re.sub('.txt',kepid+'.txt',maskfile) plotfile = re.sub('.png',kepid+'.png',plotfile) else: status = 0 # open FFI FITS file ffi, status = openfits(ffifile,'readonly') try: quarter = ffi[0].header['QUARTER'] except: try: dateobs = ffi[0].header['DATE-OBS'] if dateobs == '2009-04-24': quarter = 0 if dateobs == '2009-04-25': quarter = 0 if dateobs == '2009-04-26': quarter = 0 if dateobs == '2009-06-19': quarter = 2 if dateobs == '2009-08-19': quarter = 2 if dateobs == '2009-09-17': quarter = 2 if dateobs == '2009-10-19': quarter = 3 if dateobs == '2009-11-18': quarter = 3 if dateobs == '2009-12-17': quarter = 3 if dateobs == '2010-01-19': quarter = 4 if dateobs == '2010-01-20': quarter = 4 if dateobs == '2010-02-18': quarter = 4 if dateobs == '2010-03-19': quarter = 4 if dateobs == '2010-04-21': quarter = 5 if dateobs == '2010-05-20': quarter = 5 if dateobs == '2010-06-23': quarter = 5 except: txt = 'ERROR -- cannot determine quarter when FFI was taken. Either a ' txt += 'QUARTER or DATE-OBS keyword is expected in the primary header' sys.exit(txt) if quarter == 0: quarter = 1 if quarter < 0: txt = 'ERROR -- cannot determine quarter from FFI. Try downloading a new\n' txt += 'version of KeplerFFI.py from http://keplergo.arc.nasa.gov' sys.exit(txt) if int(quarter) == 0: season = 3 else: season = (int(quarter) - 2) % 4 # locate target in MAST if kepid: kepid,ra,dec,kepmag,skygroup,channel,module,output,row,column \ = MASTKepID(kepid,season) else: kepid,ra,dec,kepmag,skygroup,channel,module,output,row,column \ = MASTRADec(ra,dec,120.0,season) ra = float(int(ra * 1.0e5)) / 1e5 dec = float(int(dec * 1.0e5)) / 1e5 # read and close FFI FITS file try: img, status = readimage(ffi,int(channel)) status = closefits(ffi) except: txt = 'ERROR -- target is not on a legal channel during season ' + str(season) sys.exit(txt) # print target data print '' print ' KepID: %s' % kepid print ' RA (J2000): %s' % ra print 'Dec (J2000): %s' % dec print ' KepMag: %s' % kepmag print ' SkyGroup: %2s' % skygroup print ' Season: %2s' % str(season) print ' Channel: %2s' % channel print ' Module: %2s' % module print ' Output: %1s' % output print ' Column: %4s' % column print ' Row: %4s' % row print '' # subimage of channel for plot ymin = max([int(row)-npix/2,0]) ymax = min([int(row)+npix/2+1,img.shape[0]]) xmin = max([int(column)-npix/2,0]) xmax = min([int(column)+npix/2+1,img.shape[1]]) # intensity scale nstat = 2; pixels = [] for i in range(ymin,ymax): for j in range(xmin,xmax): pixels.append(img[i,j]) pixels = array(sort(pixels),dtype=float32) if int(float(len(pixels)) / 10 + 0.5) > nstat: nstat = int(float(len(pixels)) / 10 + 0.5) if not zmin: zmin = median(pixels[:nstat]) if not zmax: zmax = median(pixels[-nstat:]) if 'log' in zscale: img = log10(img) zmin = log10(zmin) zmax = log10(zmax) if 'sq' in zscale: img = sqrt(img) zmin = sqrt(zmin) zmax = sqrt(zmax) pimg = img[ymin:ymax,xmin:xmax] # plot limits ymin = float(ymin) - 0.5 ymax = float(ymax) - 0.5 xmin = float(xmin) - 0.5 xmax = float(xmax) - 0.5 # plot style try: params = {'backend': 'png', 'axes.linewidth': 2.5, 'axes.labelsize': 24, 'axes.font': 'sans-serif', 'axes.fontweight' : 'bold', 'text.fontsize': 12, 'legend.fontsize': 12, 'xtick.labelsize': 16, 'ytick.labelsize': 16} pylab.rcParams.update(params) pylab_latex = True except: pylab_latex = False pylab.figure(1,figsize=[10,7]) plotimage(pylab_latex) return # ----------------------------------------------------------- # plot channel image def plotimage(pylab_latex): global aid, cid, did, eid, fid # print FFI and source location data on plot pylab.clf() pylab.axes([0.73,0.09,0.25,0.4]) if pylab_latex: pylab.text(0.1,1.0,' KepID: %s' % kepid,fontsize=12) pylab.text(0.1,0.9,' RA (J2000): %s' % ra,fontsize=12) pylab.text(0.1,0.8,'Dec (J2000): %s' % dec,fontsize=12) pylab.text(0.1,0.7,' KepMag: %s' % kepmag,fontsize=12) pylab.text(0.1,0.6,' SkyGroup: %2s' % skygroup,fontsize=12) pylab.text(0.1,0.5,' Season: %2s' % str(season),fontsize=12) pylab.text(0.1,0.4,' Channel: %2s' % channel,fontsize=12) pylab.text(0.1,0.3,' Module: %2s' % module,fontsize=12) pylab.text(0.1,0.2,' Output: %1s' % output,fontsize=12) pylab.text(0.1,0.1,' Column: %4s' % column,fontsize=12) pylab.text(0.1,0.0,' Row: %4s' % row,fontsize=12) else: pylab.text(0.1,1.0,'KepID: %s' % kepid,fontsize=12) pylab.text(0.1,0.9,'RA (J2000): %s' % ra,fontsize=12) pylab.text(0.1,0.8,'Dec (J2000): %s' % dec,fontsize=12) pylab.text(0.1,0.7,'KepMag: %s' % kepmag,fontsize=12) pylab.text(0.1,0.6,'SkyGroup: %2s' % skygroup,fontsize=12) pylab.text(0.1,0.5,'Season: %2s' % str(season),fontsize=12) pylab.text(0.1,0.4,'Channel: %2s' % channel,fontsize=12) pylab.text(0.1,0.3,'Module: %2s' % module,fontsize=12) pylab.text(0.1,0.2,'Output: %1s' % output,fontsize=12) pylab.text(0.1,0.1,'Column: %4s' % column,fontsize=12) pylab.text(0.1,0.0,'Row: %4s' % row,fontsize=12) pylab.setp(pylab.gca(),xticklabels=[],xticks=[],yticklabels=[],yticks=[]) xlim(0.0,1.0) ylim(-0.05,1.12) # clear button pylab.axes([0.73,0.87,0.25,0.09]) pylab.text(0.5,0.5,'CLEAR',fontsize=24,weight='heavy', horizontalalignment='center',verticalalignment='center') pylab.setp(pylab.gca(),xticklabels=[],xticks=[],yticklabels=[],yticks=[]) pylab.fill([0.0,1.0,1.0,0.0,0.0],[0.0,0.0,1.0,1.0,0.0],'#ffffee') xlim(0.0,1.0) ylim(0.0,1.0) aid = connect('button_press_event',clicker1) # dump custom aperture to file button pylab.axes([0.73,0.77,0.25,0.09]) pylab.text(0.5,0.5,'DUMP',fontsize=24,weight='heavy', horizontalalignment='center',verticalalignment='center') pylab.setp(pylab.gca(),xticklabels=[],xticks=[],yticklabels=[],yticks=[]) pylab.fill([0.0,1.0,1.0,0.0,0.0],[0.0,0.0,1.0,1.0,0.0],'#ffffee') xlim(0.0,1.0) ylim(0.0,1.0) cid = connect('button_press_event',clicker3) # print window to png file button pylab.axes([0.73,0.67,0.25,0.09]) pylab.text(0.5,0.5,'PRINT',fontsize=24,weight='heavy', horizontalalignment='center',verticalalignment='center') pylab.setp(pylab.gca(),xticklabels=[],xticks=[],yticklabels=[],yticks=[]) pylab.fill([0.0,1.0,1.0,0.0,0.0],[0.0,0.0,1.0,1.0,0.0],'#ffffee') xlim(0.0,1.0) ylim(0.0,1.0) did = connect('button_press_event',clicker4) # print window to png file button pylab.axes([0.73,0.57,0.25,0.09]) pylab.text(0.5,0.5,'CLOSE',fontsize=24,weight='heavy', horizontalalignment='center',verticalalignment='center') pylab.setp(pylab.gca(),xticklabels=[],xticks=[],yticklabels=[],yticks=[]) pylab.fill([0.0,1.0,1.0,0.0,0.0],[0.0,0.0,1.0,1.0,0.0],'#ffffee') xlim(0.0,1.0) ylim(0.0,1.0) eid = connect('button_press_event',clicker5) # plot the image window ax = pylab.axes([0.08,0.09,0.63,0.88]) pylab.subplots_adjust(0.06,0.1,0.93,0.88) pylab.gca().xaxis.set_major_formatter(pylab.ScalarFormatter(useOffset=False)) pylab.gca().yaxis.set_major_formatter(pylab.ScalarFormatter(useOffset=False)) labels = ax.get_yticklabels() setp(labels, 'rotation', 90) imshow(pimg,aspect='auto',interpolation='nearest',origin='lower', vmin=zmin,vmax=zmax,extent=(xmin,xmax,ymin,ymax),cmap=cmap) pylab.gca().set_autoscale_on(False) xlabel('Pixel Column Number', {'color' : 'k'}) ylabel('Pixel Row Number', {'color' : 'k'}) grid() # plot the mask if cmap in ['Greys','binary','bone','gist_gray','gist_yarg', 'gray','pink','RdGy']: sqcol = 'g' alpha = 0.5 else: sqcol = '#ffffee' alpha = 0.8 for pixel in mask: m = int(pixel.split(',')[0]) n = int(pixel.split(',')[1]) x = [m-0.5,m+0.5,m+0.5,m-0.5,m-0.5] y = [n-0.5,n-0.5,n+0.5,n+0.5,n-0.5] pylab.fill(x,y,sqcol,alpha=alpha,ec=sqcol) fid = connect('key_press_event',clicker6) show() sys.exit() return # ----------------------------------------------------------- # get input parameters def parameters(): global mask, cmap kepid = False ra = False dec = False quarter = False zmin = False zmax = False zscale = 'log' npix = 30 decimal = True try: opts, args = getopt.getopt(sys.argv[1:],"h:krdfayzscn", ["help","kepid=","ra=","dec=","ffifile=", "aperfile=","zmin=","zmax=","zscale=", "cmap=","npix="]) except getopt.GetoptError: usage() for o, a in opts: if o in ("-h", "--help"): usage() # kepid if o in ("-k", "--kepid"): try: kepid = int(a) if kepid <= 0: txt = 'ERROR -- kepid is not a positive integer' sys.exit(txt) except: txt = 'ERROR -- kepid is not an integer' sys.exit(txt) kepid = str(kepid) # ra coordinate if o in ("-r", "--ra"): try: ra = float(a) if ra < 0.0 or ra > 360.0: txt = 'ERROR -- ra is not a sensible positive floating point number' sys.exit(txt) except: decimal = False ra = str(a) # dec coordinate if o in ("-d", "--dec"): try: dec = float(a) if dec < 0: txt = 'ERROR -- dec is not a sensible floating point number' sys.exit(txt) except: decimal = False dec = str(a) # ffifile if o in ("-f", "--ffifile"): ffifile = str(a) # aperfile: an aperture definition file if o in ("-a", "--aperfile"): aperfile = str(a) if os.path.isfile(aperfile): out = open(aperfile,'r') for line in out: if 'HALO' in line and 'UNDERSHOOT' in line: line = line.strip().split('|') r = int(line[3]) c = int(line[4]) for pixel in line[5].split(';'): y = int(pixel.split(',')[0]) x = int(pixel.split(',')[1]) mask.append(str(x+c) + ',' + str(y+r)) out.close() else: txt = 'ERROR -- a custom aperture file ' + aperfile + ' doesn''t exist' sys.exit() # zmin: minimum intensity level if o in ("-y", "--zmin"): try: zmin = float(a) if zmin < 0: txt = 'ERROR -- zmin is not a positive floating point number' sys.exit(txt) if zmin == 0: zmin = 1 except: txt = 'ERROR -- zmin is not a floating point numnber' sys.exit(txt) # zmax: maximum intensity level if o in ("-z", "--zmax"): try: zmax = float(a) if zmax <= 0: txt = 'ERROR -- zmax is not a positive floating point number' sys.exit(txt) if zmax <= zmin: txt = 'ERROR -- zmax <= zmin' sys.exit(txt) except: txt = 'ERROR -- kepid is not a floating point numnber' sys.exit(txt) # zscale: form of image intensity scale zscales = ['lin','log','sq'] if o in ("-s", "--zscale"): y = str(a) for z in zscales: if z in y: zscale = z if zscale not in zscales: txt = 'ERROR -- not a recognized image intensity scale' sys.exit(txt) # cmap: pylab colormap for image if o in ("-c", "--cmap"): cmap = str(a) if cmap == 'browse': cmap_plot() else: cmaps=[m for m in cm.datad if not m.endswith("_r")] if cmap not in cmaps: txt = 'ERROR -- cmap is not a valid colormap. Try --cmap=browse' sys.exit(txt) # npix: sub-image dimension if o in ("-n", "--npix"): try: npix = int(a) if npix <= 1: txt = 'ERROR -- npix is too small' sys.exit(txt) except: txt = 'ERROR -- npix is not a postive integer' sys.exit(txt) # decimal coordinate conversion if not decimal: try: ra,dec = sex2dec(ra,dec) except: if not kepid: txt = 'ERROR -- no sensible RA and Dec coordinates provided' sys.exit(txt) # check we have all input parameters if not kepid and (not ra or not dec): txt = 'ERROR -- kepid or coordinates have not been supplied' sys.exit(txt) if not ffifile: txt = 'ERROR -- ffifile has not been supplied' sys.exit(txt) if not os.path.isfile(ffifile): txt = 'ERROR -- can''t find FFI file ' + ffifile sys.exit(txt) return kepid,ra,dec,ffifile,zmin,zmax,zscale,cmap,npix # ----------------------------------------------------------- # target data retrieval from MAST based upon KepID def MASTKepID(id,season): global skygroup, column, row # build mast query url = 'http://archive.stsci.edu/kepler/kepler_fov/search.php?' url += 'action=Search' url += '&kic_kepler_id=' + id url += '&max_records=100' url += '&verb=3' url += '&outputformat=CSV' # retrieve results from MAST out = '' lines = urllib.urlopen(url) for line in lines: line = line.strip() if (len(line) > 0 and 'Kepler' not in line and 'integer' not in line and 'no rows found' not in line): out = line.split(',') if len(out) > 0: kepid = out[0] ra = out[4] dec = out[5] kepmag = out[19] skygroup = out[45] channel = out[67 + season * 5] module = out[68 + season * 5] output = out[69 + season * 5] row = out[70 + season * 5] column = out[71 + season * 5] else: txt = 'ERROR -- no target found with KepID %s' % id sys.exit(txt) return kepid,ra,dec,kepmag,skygroup,channel,module,output,row,column # ------------------------------------- # detector location retrieval based upon RA and Dec def MASTRADec(ra,dec,darcsec,season): global skygroup, column, row # WCS data cd1_1 = 0.000702794927969 cd1_2 = -0.000853190160515 cd2_1 = -0.000853190160515 cd2_2 = -0.000702794927969 cd = array([[cd1_1,cd1_2],[cd2_1,cd2_2]]) cd = linalg.inv(cd) # coordinate limits x1 = 1.0e30 x2 = x1 darcsec /= 3600.0 ra1 = ra - darcsec / 15.0 / cos(dec * pi / 180) ra2 = ra + darcsec / 15.0 / cos(dec * pi / 180) dec1 = dec - darcsec dec2 = dec + darcsec # build mast query url = 'http://archive.stsci.edu/kepler/kepler_fov/search.php?' url += 'action=Search' url += '&kic_degree_ra=' + str(ra1) + '..' + str(ra2) url += '&kic_dec=' + str(dec1) + '..' + str(dec2) url += '&max_records=100' url += '&verb=3' url += '&outputformat=CSV' # retrieve results from MAST: nearest KIC source to supplied coordinates z = '' x = 1.0e30 lines = urllib.urlopen(url) for line in lines: line = line.strip() if (len(line) > 0 and 'Kepler' not in line and 'integer' not in line and 'no rows found' not in line): out = line.split(',') r = (float(out[4].split(' ')[0]) + \ float(out[4].split(' ')[1]) / 60.0 + \ float(out[4].split(' ')[2]) / 3600.0) * 15.0 d = float(out[5].split(' ')[0]) + \ float(out[5].split(' ')[1]) / 60.0 + \ float(out[5].split(' ')[2]) / 3600.0 a = sqrt((abs(r - ra) / 15.0 / cos(d * pi / 180))**2 + abs(d - dec)**2) if a < x: x = a z = line.split(',') if len(z) > 0: kepid = None kepmag = None skygroup = out[45] channel = out[67 + season * 5] module = out[68 + season * 5] output = out[69 + season * 5] else: txt = 'ERROR -- row and column could not be calculated. Is location on silicon?' sys.exit(txt) # convert coordinates to decimal for the two targets, determine distance from input zra,zdec = sex2dec(z[4],z[5]) dra = zra - ra ddec = zdec - dec drow = cd[0,0] * dra + cd[0,1] * ddec dcol = cd[1,0] * dra + cd[1,1] * ddec # pixel coordinates of the nearest KIC target row = z[70 + season * 5] column = z[71 + season * 5] # pixel coordinate of target row = str(int(float(row) + drow + 0.5)) column = str(int(float(column) + dcol + 0.5)) return kepid,ra,dec,kepmag,skygroup,channel,module,output,row,column # ----------------------------------- # convert sexadecimal hours to decimal degrees def sex2dec(ra,dec): ra = re.sub('\s+','|',ra.strip()) ra = re.sub(':','|',ra.strip()) ra = re.sub(';','|',ra.strip()) ra = re.sub(',','|',ra.strip()) ra = re.sub('-','|',ra.strip()) ra = ra.split('|') outra = (float(ra[0]) + float(ra[1]) / 60 + float(ra[2]) / 3600) * 15.0 dec = re.sub('\s+','|',dec.strip()) dec = re.sub(':','|',dec.strip()) dec = re.sub(';','|',dec.strip()) dec = re.sub(',','|',dec.strip()) dec = re.sub('-','|',dec.strip()) dec = dec.split('|') if float(dec[0]) > 0.0: outdec = float(dec[0]) + float(dec[1]) / 60 + float(dec[2]) / 3600 else: outdec = float(dec[0]) - float(dec[1]) / 60 - float(dec[2]) / 3600 return outra, outdec # ----------------------------------------------------------- # open HDU structure def openfits(file,mode): status = 0 try: struct = pyfits.open(file,mode=mode) except: txt = 'ERROR -- cannot open ' + file + ' as a FITS file' sys.exit(txt) return struct, status # ----------------------------------------------------------- # close HDU structure def closefits(struct): status = 0 try: struct.close() except: txt = 'ERROR -- cannot close HDU structure' sys.exit(txt) return status # ----------------------------------------------------------- # read image from HDU structure def readimage(struct,hdu): status = 0 try: imagedata = struct[hdu].data except: txt = 'ERROR -- cannot read image data from HDU ' + str(hdu) sys.exit(txt) return imagedata, status # ----------------------------------------------------------- # clear all pixels from pixel mask def clicker1(event): global mask, aid, cid, did, eid, fid if event.inaxes: if event.button == 1: if (event.x > 585 and event.x < 783 and event.y > 488 and event.y < 537): disconnect(aid) disconnect(cid) disconnect(did) disconnect(eid) disconnect(fid) mask = [] pylab.clf() plotimage(pylab_latex) return # ----------------------------------------------------------- # dump custom aperture definition file def clicker3(event): global aid, cid, did, eid, fid if event.inaxes: if event.button == 1: if (event.x > 585 and event.x < 783 and event.y > 432 and event.y < 480): masktxt = 'NEW|' masktxt += skygroup + '|' masktxt += '{' + re.sub('\s+',':',str(ra)) masktxt += ',' + re.sub('\s+',':',str(dec)) masktxt += '},TAD_NO_HALO,TAD_NO_UNDERSHOOT_COLUMN|' masktxt += row + '|' masktxt += column + '|' for coord in sorted(set(mask)): masktxt += str(int(coord.split(',')[1]) - int(row)) + ',' masktxt += str(int(coord.split(',')[0]) - int(column)) + ';' if (os.path.isfile(maskfile)): os.remove(maskfile) out = open(maskfile,'a') out.write(masktxt[:-1]+'\n') out.close() print 'Wrote custom aperture definition file ' + maskfile + ' with ' + str(len(mask)) + ' pixels' return # ----------------------------------------------------------- # print plot to png with left-mouse click def clicker4(event): if event.inaxes: if event.button == 1: if (event.x > 585 and event.x < 783 and event.y > 377 and event.y < 425): pylab.savefig(plotfile) print 'Wrote plot hardcopy file ' + plotfile return # ----------------------------------------------------------- # close plot and exit program def clicker5(event): if event.inaxes: if event.button == 1: if (event.x > 585 and event.x < 783 and event.y > 320 and event.y < 368): pylab.close('all') return # ----------------------------------------------------------- # this function will be called with every click of the mouse def clicker6(event): global mask, aid, cid, did, eid, fid if event.inaxes: if event.key == '' or \ event.key == 'x': if cmap in ['Greys','binary','bone','gist_gray','gist_yarg', 'gray','pink','RdGy']: sqcol = 'g' alpha = 0.5 else: sqcol = '#ffffee' alpha = 0.8 m = float(int(event.xdata + 0.5)) n = float(int(event.ydata + 0.5)) txt = str(int(m))+','+str(int(n)) if txt in mask: tmpmask = [] for pixel in mask: if pixel != txt: tmpmask.append(pixel) mask = tmpmask else: mask.append(txt) disconnect(aid) disconnect(cid) disconnect(did) disconnect(eid) disconnect(fid) plotimage(pylab_latex) # ----------------------------------------------------------- # these are the choices for the image colormap def cmap_plot(): pylab.figure(1,figsize=[5,10]) a=outer(ones(10),arange(0,1,0.01)) subplots_adjust(top=0.99,bottom=0.00,left=0.01,right=0.8) maps=[m for m in cm.datad if not m.endswith("_r")] maps.sort() l=len(maps)+1 for i, m in enumerate(maps): subplot(l,1,i+1) pylab.setp(pylab.gca(),xticklabels=[],xticks=[],yticklabels=[],yticks=[]) imshow(a,aspect='auto',cmap=get_cmap(m),origin="lower") pylab.text(100.85,0.5,m,fontsize=10) show() sys.exit() # ----------------------------------------------------------- # usage def usage(): print ' ' print ' ----------------------------------------------------------------------' print ' Martin Still NASA Ames May 14, 2010' print ' ' print ' 30-minute duration Full Frame Images (FFIs) are obtained nominally by' print ' Kepler once per month, immediately prior to reorienting the spacecraft to' print ' a stored data downlink attitude. Each FFI includes data from all 96.5' print ' Megapixels from the 42 individual CCDs that comprise the Kepler photometer.' print ' At all other times, due to onboard storage, telemetry and bandwidth' print ' constraints, the only pixels stored onboard Kepler and telemetered to the' print ' ground are the target apertures of interest, a subset of 170,000 specially' print ' selected targets. The majority of these are bright, photometrically quiet' print ' G or K type stars on or close to the main sequence, comprising 6% of the' print ' full pixel set of the photometer. These monthly FFIs are the only Kepler' print ' data available for the vast majority of stars in the field. During routine' print ' science operations, FFIs are utilized only to verify target apertures and' print ' obtain important full-field calibration information (Haas et al, 2010;' print ' http://adsabs.harvard.edu/abs/2010ApJ...713L.115H). Since the Kepler' print ' mission uses the FFIs solely for engineering purposes, the Kepler Team do' print ' not spend resources on these images for scientific purposes and they are' print ' released to the public as soon as calibration is complete.' print ' ' print ' KeplerFFI has two purposes. The first is to plot the local area around a' print ' user-provided target within a Kepler FFI. This can be used to' print ' examine the nearby field of a target, identify source confusion or' print ' understand effects of aperture on Kepler PA and PDC light curve data. The' print ' minimum input is 1. the location and name of a FITS file containing an FFI,' print ' 2. either the Kepler ID from the Kepler Input Catalog, or the J2000 RA and' print ' Dec of a target, 3. the pixel dimension of the square subimage being' print ' displayed.' print ' ' print ' The second purpose is to construct a custom aperture for sources that are' print ' e.g. extended or saturated. Because standard pixel capture apertures' print ' assume that a source is point like , in many cases of bright or extended' print ' targets the standard apertures does not provide optimized photometry. This' print ' tool creates a custom aperture by simple choosing pixels for inclusion' print ' within the aperture. Individual pixels are chosen by a click of the' print ' mouses middle button.' print ' ' print ' FFI FITS files can be downloaded from:' print ' http://archive.stsci.edu/pub/kepler/ffi' print ' ' print ' The terminology of Kepler ''seasons'' and Kepler ''quarters'' needs to be' print ' understood in order to interpret the output of this tool correctly. Kepler' print ' operates over 4 seasons each year. At the beginning of each season data' print ' from the previous season are downlinked from the spacecraft, new commands' print ' and target lists are uploaded, and the spacecraft is rotated by 90 degrees' print ' to repoint the solar array. Rotation means that each star in the Kepler' print ' field will be recorded on a different CCD in the detector array each' print ' season of the year, the parameters used to locate each source on the' print ' detector array are season-dependent. There are 84 output nodes in the' print ' Kepler detector array, each provides a separate image in the FFI FITS' print ' file. Each has a numerical identifier called the channel number, ranging' print ' from 1-84. CCD Channels rotate with the spacecraft. Consequently each' print ' Kepler target will land on 4 different and specific channels each year.' print ' This is the significance of a Kepler season. There are 4 output nodes per' print ' CCD module, of which there are 21. And so the mod.out (e.g. 12.3, 20.4' print ' etc) is an alternative naming scheme to the channel number. The column and' print ' row locate the centroid of a target on a particular channel. The' print ' [column,row] range for all channels is [1:1132,1:1070]. The annual seasons' print ' are identified in MAST target and archive searches by the numbers 0,1,2,3.' print ' Kepler quarters are defined by seasons but increment continuously from the' print ' first quarter of the mission. Kepler science quarters began at Q1, but' print ' critically pre-science commissioning data is termed Q0 and were collected' print ' at the same spacecraft orientation as Q1. The following table defines the' print ' relationship between season and quarter (dates are approximate):' print ' ' print ' Date Season Quarters' print ' ----------------------------------' print ' 06/20-09/20 0 Q2,Q6,Q10...' print ' 09/20-12/20 1 Q3,Q7,Q11...' print ' 12/20-03/20 2 Q4,Q8,Q12...' print ' 03/20-06/20 3 Q0,Q1,Q5,Q9...' print ' ' print ' The skygroup is similar in nature to the channel number. There are 84' print ' skygroups but these remain fixed in position on the sky. A target does' print ' not change its skygroup after a spacecraft roll. The skygroup and channel' print ' number are identical during season 2.' print ' There are four function buttons provided on the GUI:' print ' ' print ' CLEAR -- removes all pixel selections for the custom aperture so that a' print ' user can start the pixel selection process from scratch.' print ' DUMP -- write out the custom target pixel defintion file.' print ' PRINT -- saves a hardcopy version of the gui window in PNG format' print ' CLOSE -- closes the GUI and quits the application' print ' ' print ' The user has some freedom to choose the image intensity scale in order to' print ' best display the FFI. Optional input parameters are zmin and zmax which' print ' define the range of the intensity scale, and zscale which defines the' print ' intensity relation which can be ''linear'', ''logarithmic'' or ''square' print ' root''. Without user-defined parameters the default is a logarithmic scale.' print ' The minimum is selected by determining the median value of faintest 10%' print ' of pixels in the subimage. The maximum is selected by determining the' print ' median value of brightest 10% of pixels in the subimage.' print ' ' print ' There is a wide selection of color maps to choose from when displaying the' print ' FFI image. All possible choices can be found by calling the tool with the' print ' following command: keplerFFI.py --cmap=browse.' print ' ' print ' An existing custom aperture definition file can be inspected and modified' print ' using this tool. The defintion file can be read using the optional input' print ' argument --aperfile which requires the supply of a path and filename.' print ' Mutliple custom apertures can be inspected simultaneously, but not edited' print ' simultaneously. The format of the ascii defintion file has one target per' print ' line. Here is a specific example:' print ' ' print ' NEW|79|{19:39:48.43,+39:27:23.7},TAD_NO_HALO,TAD_NO_UNDERSHOOT_COLUMN|859|1044|-1,-3;0,-3;1,-3;-1,-2;0,-2;1,-2;2,-2;-2,-1;-1,-1;0,-1;1,-1;2,-1;3,-1;-2,0;-1,0;0,0;1,0;2,0;3,0;-1,1;0,1;1,1;2,1;-1,2;0,2' print ' ' print ' Each line is a sequence of parameters delimited by a pipe ''|''. The first' print ' parameter is a dummy identifier, the second is the skygroup. The third is' print ' required during target management, the fourth and fifth are column and row' print ' reference points for the mask, and the sixth is the pixel map (column,row)' print ' for the aperture mask relative to the reference pixel.' print ' ' print ' KeplerFFI is a python script and requires the core python modules to be' print ' in your executable path plus three add-on modules: matplotlib - a python' print ' matlab emulator, numpy - a numerical array handling package, and pyfits -' print ' a FITS file I/O module. Mac users can obtain all of the above from the' print ' fink sofware repository (http://www.finkproject.org). Alternatively users' print ' of Mac, Linux and PCs can obtain copies from:' print ' ' print ' python: http://www.python.org' print ' numpy: http://numpy.scipy.org' print ' matplotlib: http://matplotlib.sourceforge.net' print ' pyfits: http://www.stsci.edu/resources/software_hardware/pyfits' print ' ' print ' The script has been developed and tested against python2.6, matplotlib0.99,' print ' pyfits1.3 and numpy1.3.0. Correct execution by earlier versions of these' print ' modules cannot be guaranteed. In order to determine the FFI location of' print ' specific sources and sky coordinates, KeplerFFI requires access to the MAST' print ' archive (http://archive.stsci.edu/kepler) and so users must be online' print ' during execution. A version of this tool is also maintained within the' print ' PyKEP PyRAF package.' print ' ' print ' Typical usage examples:' print ' ' print ' KeplerFFI.py --kepid=6522823 --ffifile=kplr2009292020429_ffi-SocCal.fits' print ' --npix=30' print ' KeplerFFI.py --kepid=7659570 --ffifile=kplr2009292020429_ffi-SocCal.fits' print ' --zmin=5e5 --zmax=1e7 --zscale=lin --npix=40' print ' KeplerFFI.py --ra=19:27:31.21 --dec=41:57:02.3' print ' --ffifile=kplr2009292020429_ffi-SocCal.fits --npix=20 --cmap=RdBu' print ' KeplerFFI.py --kepid=8635620 --ffifile=kplr2009292020429_ffi-SocCal.fits' print ' --aperfile=KeplerFFI8635620.txt --npix=20 --cmap=RdYlBu' print ' ' print ' --kepid Kepler target ID from Kepler Input Catalog' print ' --ra (alternative to kepid) RA J2000 coordinate of target' print ' --dec (alternative to kepid) Dec J2000 coordinate of target' print ' --ffifile path+name of the FFI FITS file' print ' --aperfile path+name of a custom aperture definition file' print ' --zmin minimum level of the image intensity scale [e-/cadence]' print ' --zmax maximum level of the image intensity scale [e-/cadence]' print ' --zscale scale of image intensity [lin|log|sqrt]' print ' --cmap color intensity map' print ' --npix square dimension of plotted sub-image [pixels]' print ' ' print ' aperfile, zmin, zmax, zscale, cmap and npix are optional control' print ' parameters. ffifile is a compulsory parameter. The location of a source' print ' is described either using kepid or ra and dec.' print ' ----------------------------------------------------------------------' sys.exit(' ') #------------------------------- # main if __name__ == "__main__": main()