コード例 #1
0
def fit_wave_soln(fnlist, doprint=False):
    """Fits a wavelength solution to rings in a set of images. Appends this
    wavelength solution to the image headers as the keywords:
    'fpwave0' and 'fpcalf'

    Each object in fnlist must have a corresponding "median.fits" in its
    image directory, or this routine will not work.

    ARC ring images are fitted by adjusting the center, while the center is
    held fixed for night sky rings. A combination of fits to both sets of
    rings is used to determine a wavelength solution for the whole set of
    images.

    If the ARC rings disagree substantially with the night sky rings, it is
    recommended that users delete the ARC rings from the fit and use only the
    night sky rings.

    It is also known that the wavelength solution can sometimes be piecewise in
    time when a large jump in 'z' happens between two images; i.e. different
    wavelength solutions exist before and after the jump. The routine allows
    the user to make a piecewise solution for this reason, but this ability
    should be used sparingly.

    This routine contains one of the few hard-coded numbers in the pipeline,
    Fguess=5600. Currently F values are not written to the fits image headers,
    and this is a reasonable guess.

    Inputs:
    fnlist -> List of strings, each the path to a fits image. These images
    should all have been taken with the same order filter. If not, the routine
    will crash.

    """

    # This bit takes care of the 's' to save shortcut in matplotlib.
    oldsavekey = plt.rcParams["keymap.save"]
    plt.rcParams["keymap.save"] = ""

    # Open all of the images
    arclist = []
    objlist = []
    images = [FPImage(fn) for fn in fnlist]

    # Separate ARCs and Object images and median-subtract Object images
    isarclist = [image.object == "ARC" for image in images]
    for i in range(len(isarclist)):
        if isarclist[i]:
            arclist.append(images[i])
        else:
            if not isfile(join(split(fnlist[i])[0], "median.fits")):
                exit("Error! No 'median.fits' file found.")
            medimage = fits.open(join(split(fnlist[i])[0], "median.fits"))
            images[i].inty -= medimage[0].data
            images[i].inty -= np.median(images[i].inty[images[i].badp != 1])
            medimage.close()
            objlist.append(images[i])

    filts = [image.filter for image in images]
    arclibs = []
    nightlibs = []
    for i in range(len(fnlist)):
        if isarclist[i]:
            arclibs.append(get_libraries(filts[i])[0])
        else:
            nightlibs.append(get_libraries(filts[i])[1])
        if get_libraries(filts[i])[0] is None:
            exit("Error! Filter "+filts[i]+" not in the wavelength library!")

    # This next bit fits all of the rings that the user marks

    # Fit rings in the object images
    radlists = []
    for i in range(len(objlist)):
        radlists.append([])
    i = 0
    while True:
        xcen = objlist[i].xcen
        ycen = objlist[i].ycen
        axcen = objlist[i].axcen
        aycen = objlist[i].aycen
        arad = objlist[i].arad
        rgrid = objlist[i].rarray(axcen, aycen)
        # Create radius bins
        rbins = np.arange(arad-np.int(max(abs(axcen-xcen), abs(aycen-ycen))))+1
        intbins = np.empty_like(rbins)
        # Get the median intensity within each radius bin
        for j in range(len(rbins)):
            binmask = np.logical_and(rgrid > rbins[j]-1,
                                     rgrid < rbins[j])
            goodbinmask = np.logical_and(binmask,
                                         objlist[i].badp == 0)
            if np.sum(goodbinmask) != 0:
                intbins[j] = np.median(objlist[i].inty[goodbinmask])
            else:
                intbins[j] = 0
        # Shift/scale the radius and intensity for the purposes of plotting
        plotxcen = xcen-axcen+arad
        plotycen = ycen-aycen+arad
        plotrbins = rbins+plotxcen
        plotintbins = intbins*arad/np.percentile(np.abs(intbins), 98)+plotycen

        # Plot the data interactively
        ringplot = PlotRingProfile(objlist[i].inty[aycen-arad:aycen+arad,
                                                   axcen-arad:axcen+arad],
                                   plotrbins, plotintbins,
                                   plotxcen, plotycen,
                                   radlists[i],
                                   repr(i+1)+"/"+repr(len(objlist)))

        # Changing images and loop breakout conditions
        if ringplot.key == "d":
            i += 1
        if ringplot.key == "a":
            i += -1
        if i == -1 or i == len(objlist):
            while True:
                yn = raw_input("Finished marking sky rings? (y/n) ")
                if "n" in yn or "N" in yn:
                    if i == -1:
                        i = 0
                    if i == len(objlist):
                        i = len(objlist)-1
                    break
                elif "y" in yn or "Y" in yn:
                    break
        if i == -1 or i == len(objlist):
            break

        # Force-marking a ring
        if ringplot.key == "e" and ringplot.xcoo is not None:
            radlists[i].append(ringplot.xcoo-arad-(xcen-axcen))

        # Deleting a ring
        if (ringplot.key == "s" and ringplot.xcoo is not None and
                len(radlists[i]) > 0):
            distarray = np.abs((np.array(radlists[i]) -
                                np.sqrt((ringplot.xcoo-arad-(xcen-axcen))**2 +
                                        (ringplot.ycoo-arad-(ycen-aycen))**2)))
            radlists[i].pop(np.argmin(distarray))

        # Fitting a ring profile
        if ringplot.key == "w" and ringplot.xcoo is not None:
            lower_index = max(ringplot.xcoo-plotxcen-50, 0)
            upper_index = min(ringplot.xcoo-plotxcen+50, len(rbins))
            x = rbins[lower_index:upper_index]**2
            y = intbins[lower_index:upper_index]
            fit = GaussFit(x, y)
            fitplot = PlotRingFit(x, y, fit)
            if fitplot.key == "w":
                radlists[i].append(np.sqrt(fit[2]))

    zo = []
    to = []
    ro = []
    lib_o = []
    for i in range(len(objlist)):
        for j in range(len(radlists[i])):
            zo.append(objlist[i].z)
            to.append(objlist[i].jd)
            ro.append(radlists[i][j])
            lib_o.append(nightlibs[i])
            if doprint:
                print objlist[i].z, objlist[i].jd, radlists[i][j]

    # Fit rings in the ARC images if there are any
    xcen = objlist[0].xcen
    ycen = objlist[0].ycen
    radlists = []
    for i in range(len(arclist)):
        radlists.append([])
    i = 0
    while len(arclist) > 0:
        axcen = arclist[i].axcen
        aycen = arclist[i].aycen
        arad = arclist[i].arad
        rgrid = arclist[i].rarray(axcen, aycen)
        # Create radius bins
        rbins = np.arange(arad-np.int(max(abs(axcen-xcen), abs(aycen-ycen))))+1
        intbins = np.empty_like(rbins)
        # Get the median intensity in each radius bin
        for j in range(len(rbins)):
            binmask = np.logical_and(rgrid > rbins[j]-1,
                                     rgrid < rbins[j])
            goodbinmask = np.logical_and(binmask,
                                         arclist[i].badp == 0)
            intbins[j] = np.median(arclist[i].inty[goodbinmask])
        # Shift/scale the radius and intensity for the purposes of plotting
        plotxcen = xcen-axcen+arad
        plotycen = ycen-aycen+arad
        plotrbins = rbins+plotxcen
        plotintbins = intbins*arad/np.percentile(np.abs(intbins), 98)+plotycen

        # Plot the data interactively
        ringplot = PlotRingProfile(arclist[i].inty[aycen-arad:aycen+arad,
                                                   axcen-arad:axcen+arad],
                                   plotrbins, plotintbins,
                                   plotxcen, plotycen,
                                   radlists[i],
                                   repr(i+1)+"/"+repr(len(arclist)))

        # Changing images and loop breakout conditions
        if ringplot.key == "d":
            i += 1
        if ringplot.key == "a":
            i += -1
        if i == -1 or i == len(arclist):
            while True:
                yn = raw_input("Finished marking ARC rings? (y/n) ")
                if "n" in yn or "N" in yn:
                    if i == -1:
                        i = 0
                    if i == len(arclist):
                        i = len(arclist)-1
                    break
                elif "y" in yn or "Y" in yn:
                    break
        if i == -1 or i == len(arclist):
            break

        # Force-marking a ring
        if ringplot.key == "e" and ringplot.xcoo is not None:
            radlists[i].append(ringplot.xcoo-arad-(xcen-axcen))

        # Deleting a ring
        if (ringplot.key == "s" and ringplot.xcoo is not None and
                len(radlists[i]) > 0):
            distarray = np.abs((np.array(radlists[i]) -
                                np.sqrt((ringplot.xcoo-arad-(xcen-axcen))**2 +
                                        (ringplot.ycoo-arad-(ycen-aycen))**2)))
            radlists[i].pop(np.argmin(distarray))
        # Fitting a ring profile
        if ringplot.key == "w" and ringplot.xcoo is not None:
            lower_index = max(ringplot.xcoo-plotxcen-50, 0)
            upper_index = min(ringplot.xcoo-plotxcen+50, len(rbins))
            x = rbins[lower_index:upper_index]**2
            y = intbins[lower_index:upper_index]
            fit = GaussFit(x, y)
            fitplot = PlotRingFit(x, y, fit)
            if fitplot.key == "w":
                radlists[i].append(np.sqrt(fit[2]))

    za = []
    ta = []
    ra = []
    lib_a = []
    for i in range(len(arclist)):
        for j in range(len(radlists[i])):
            za.append(arclist[i].z)
            ta.append(arclist[i].jd)
            ra.append(radlists[i][j])
            lib_a.append(arclibs[i])
            if doprint:
                print arclist[i].z, arclist[i].jd, radlists[i][j]

    # Now we try to get a good guess at the wavelengths

    # Get a good guess at which wavelengths are which
    Bguess = objlist[0].b
    Fguess = objlist[0].f
    if Fguess is None:
        Fguess = 5600

    # Figure out A by matching rings to the wavelength libraries
    master_r = np.array(ro+ra)
    master_z = np.array(zo+za)
    wavematch = np.zeros_like(master_r)
    oldrms = 10000  # Really high initial RMS for comparisons
    master_lib = lib_o+lib_a
    for i in range(len(master_r)):
        lib = master_lib[i]
        for j in range(len(lib)):
            # Assume the i'th ring is the j'th line
            Aguess = (lib[j]*np.sqrt(1+master_r[i]**2/Fguess**2) -
                      Bguess*master_z[i])
            # What are all of the other rings, given this A?
            waveguess = ((Aguess+Bguess*master_z) /
                         np.sqrt(1+master_r**2/Fguess**2))
            for k in range(len(master_r)):
                wherematch = np.argmin(np.abs(master_lib[k]-waveguess[k]))
                wavematch[k] = master_lib[k][wherematch]
            rms = np.sqrt(np.average((waveguess-wavematch)**2))
            if rms < oldrms:
                # This is the new best solution. Keep it!
                oldrms = rms
                bestA = Aguess
                master_wave = wavematch.copy()

    # Make more master arrays for the plotting
    master_t = np.array(to+ta)
    t0 = np.min(master_t)
    master_t += -t0
    master_t *= 24*60  # Convert to minutes
    master_color = np.array(len(ro)*["blue"]+len(ra)*["red"])
    toggle = np.ones(len(master_r), dtype="bool")
    dotime = False
    time_dividers = []

    # Do the interactive plotting
    while True:
        rplot = master_r[toggle]
        zplot = master_z[toggle]
        tplot = master_t[toggle]
        colorplot = master_color[toggle]
        wplot = master_wave[toggle]
        fitplot = np.zeros(len(wplot))
        xs = np.zeros((3, len(rplot)))
        xs[0] = rplot
        xs[1] = zplot
        xs[2] = tplot
        fit = [0]*(len(time_dividers)+1)
        time_dividers = sorted(time_dividers)
        if len(time_dividers) > 1:
            print ("Warning: Too many time divisions is likely unphysical." +
                   "Be careful!")
        for i in range(len(time_dividers)+1):
            # Create a slice for all of the wavelengths before this time
            # divider but after the one before it
            if len(time_dividers) == 0:
                tslice = tplot == tplot
            elif i == 0:
                tslice = tplot < time_dividers[i]
            elif i == len(time_dividers):
                tslice = tplot > time_dividers[i-1]
            else:
                tslice = np.logical_and(tplot < time_dividers[i],
                                        tplot > time_dividers[i-1])
            # Case for fitting time dependence
            if dotime:
                fit[i] = curve_fit(fpfunc_for_curve_fit_with_t,
                                   xs[:, tslice], wplot[tslice],
                                   p0=(bestA, Bguess, 0, Fguess))[0]
                fitplot[tslice] = fpfunc_for_curve_fit_with_t(xs[:, tslice],
                                                              fit[i][0],
                                                              fit[i][1],
                                                              fit[i][2],
                                                              fit[i][3])
            # Case without time dependence
            else:
                fit[i] = curve_fit(fpfunc_for_curve_fit,
                                   xs[:, tslice], wplot[tslice],
                                   p0=(bestA, Bguess, Fguess))[0]
                fitplot[tslice] = fpfunc_for_curve_fit(xs[:, tslice],
                                                       fit[i][0],
                                                       fit[i][1],
                                                       fit[i][2])
        # Calculate residuals to the fit
        resid = wplot - fitplot

        # Interactively plot the residuals
        solnplot = WaveSolnPlot(rplot, zplot, tplot, wplot,
                                resid, colorplot, time_dividers)
        # Breakout case
        if solnplot.key == "a":
            while True:
                for i in range(len(time_dividers)+1):
                    if dotime:
                        solnstring = ("Solution "+repr(i+1) +
                                      ": A = "+str(fit[i][0]) +
                                      ", B = "+str(fit[i][1]) +
                                      ", E = "+str(fit[i][2]) +
                                      ", F = "+str(fit[i][3]))
                    else:
                        solnstring = ("Solution "+repr(i+1) +
                                      ": A = "+str(fit[i][0]) +
                                      ", B = "+str(fit[i][1]) +
                                      ", F = "+str(fit[i][2]))
                    print solnstring
                rms = np.sqrt(np.average(resid**2))
                print ("Residual rms="+str(rms) +
                       " for "+repr(len(time_dividers)+1) +
                       " independent "+repr(3+dotime) +
                       "-parameter fits to "+repr(len(rplot))+" rings.")
                yn = raw_input("Accept wavelength solution? (y/n) ")
                if "n" in yn or "N" in yn:
                    break
                elif "y" in yn or "Y" in yn:
                    solnplot.key = "QUIT"
                    break
        if solnplot.key == "QUIT":
            break

        # Restore all points case
        if solnplot.key == "r":
            toggle = np.ones(len(master_r), dtype="bool")

        # Delete nearest point case
        if solnplot.key == "d" and solnplot.axis is not None:
            # Figure out which plot was clicked in
            if solnplot.axis == 1:
                # Resid vs. z plot
                z_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = (((zplot-z_loc)/(np.max(zplot)-np.min(zplot)))**2 +
                         ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2)
            elif solnplot.axis == 2:
                # Resid vs. R plot
                r_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = (((rplot-r_loc)/(np.max(rplot)-np.min(rplot)))**2 +
                         ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2)
            elif solnplot.axis == 3:
                # Resit vs. T plot
                t_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = (((tplot-t_loc)/(np.max(tplot)-np.min(tplot)))**2 +
                         ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2)
            elif solnplot.axis == 4:
                # Resid vs. Wave plot
                wave_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = (((wplot-wave_loc)/(np.max(wplot)-np.min(wplot)))**2 +
                         ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2)
            # Get the radius and time of the worst ring
            r_mask = rplot[dist2 == np.min(dist2)][0]
            t_mask = tplot[dist2 == np.min(dist2)][0]
            toggle[np.logical_and(master_r == r_mask,
                                  master_t == t_mask)] = False

        # Toggle time fit case
        if solnplot.key == "t":
            dotime = not dotime

        # Add time break case
        if solnplot.key == "w":
            timeplot = TimePlot(tplot, resid, colorplot, time_dividers)
            if timeplot.xcoo is not None:
                time_dividers.append(timeplot.xcoo)

        # Remove time breaks case
        if solnplot.key == "q":
            time_dividers = []

    # Close all images
    for image in images:
        image.close()

    # For each image, write the central wavelength and F to the image header
    for i in range(len(fnlist)):
        image = FPImage(fnlist[i], update=True)
        image_t = (image.jd-t0)*24*60
        # Figure out which time division it's in
        div_index = np.where(np.array(time_dividers) > image_t)[0]
        if len(div_index > 0):
            div_index = div_index[0]
        else:
            div_index = len(time_dividers)
        image_fit = fit[div_index]
        if dotime:
            image_wave0 = (image_fit[0] +
                           image_fit[1]*image.z +
                           image_fit[2]*image_t)
            image_F = image_fit[3]
        else:
            image_wave0 = (image_fit[0] +
                           image_fit[1]*image.z)
            image_F = image_fit[2]
        image.wave0 = image_wave0
        image.calf = image_F
        image.calrms = rms
        image.calnring = repr(len(rplot))
        image.calnfits = repr(len(time_dividers)+1)
        image.calnpars = repr(3+dotime)
        image.close()

    # Restore the old keyword shortcut
    plt.rcParams["keymap.save"] = oldsavekey

    return
コード例 #2
0
def fit_wave_soln(fnlist):
    """Fits a wavelength solution to rings in a set of images. Appends this
    wavelength solution to the image headers as the keywords:
    'fpcala', 'fpcalb', ... 'fpcalf'
    
    Each object in fnlist must have a corresponding "median.fits" in its
    image directory, or this routine will not work.
    
    ARC ring images are fitted by adjusting the center, while the center is held
    fixed for night sky rings. A combination of fits to both sets of rings is
    used to determine a wavelength solution for the whole set of images.
    
    If the ARC rings disagree substantially with the night sky rings, it is
    recommended that users delete the ARC rings from the fit and use only the
    night sky rings.
    
    It is also known that the wavelength solution can sometimes be piecewise in
    time when a large jump in 'z' happens between two images; i.e. different
    wavelength solutions exist before and after the jump. The routine allows
    the user to make a piecewise solution for this reason, but this ability
    should be used sparingly.
    
    This routine contains one of the few hard-coded numbers in the pipeline,
    Fguess=5600. Currently F values are not written to the fits image headers,
    and this is a reasonable guess.
    
    Inputs:
    fnlist -> List of strings, each the path to a fits image. These images
    should all have been taken with the same order filter. If not, the routine
    will crash.
    
    """
    
    #This bit takes care of the 's' to save shortcut in matplotlib.
    oldsavekey = plt.rcParams["keymap.save"]
    plt.rcParams["keymap.save"] = ""
        
    #Open all of the images
    imagelist = []
    arclist = []
    objlist = []
    for i in range(len(fnlist)):
        imagelist.append(openfits(fnlist[i]))
        if i == 0: filt = imagelist[0][0].header["FILTER"]
        if imagelist[i][0].header["FILTER"] != filt:
            print "Error! Some of these images are in different filters!"
            crash()
        if imagelist[i][0].header["OBJECT"]=="ARC": arclist.append(imagelist[i])
        else:
            if not isfile(join(split(fnlist[i])[0],"median.fits")):
                print "Error! No 'median.fits' file found."
                crash()
            medimage = openfits(join(split(fnlist[i])[0],"median.fits"))
            imagelist[i][0].data += -medimage[0].data
            medimage.close()
            objlist.append(imagelist[i])
    
    #Load wavelength libraries
    arclib, nightlib = get_libraries(filt)
    if arclib is None:
        print "Error! Your filter isn't the wavelength library!"
        crash()

    



    #This next bit fits all of the rings that the user marks

    #Fit rings in the object images
    radlists = []
    for i in range(len(objlist)):
        radlists.append([])
    i=0
    while True:
        xgrid, ygrid = np.meshgrid(np.arange(objlist[i][0].data.shape[1]), np.arange(objlist[i][0].data.shape[0]))
        xcen = objlist[i][0].header["FPXCEN"]
        ycen = objlist[i][0].header["FPYCEN"]
        axcen = objlist[i][0].header["FPAXCEN"]
        aycen = objlist[i][0].header["FPAYCEN"]
        arad = objlist[i][0].header["FPARAD"]
        rgrid = np.sqrt((xgrid - xcen)**2 + (ygrid - ycen)**2)
        rbins = np.arange(arad-np.int(max(abs(axcen-xcen),abs(aycen-ycen))))+1
        intbins = np.empty_like(rbins)
        for j in range(len(rbins)):
            intbins[j] = np.median(objlist[i][0].data[np.logical_and(np.logical_and(objlist[i][0].data!=0,rgrid<rbins[j]),rgrid>rbins[j]-1)])
        ringplot = PlotRingProfile(objlist[i][0].data[aycen-arad:aycen+arad,axcen-arad:axcen+arad], #Data to be plotted. Only want stuff inside aperture
                                   rbins+(xcen-axcen)+arad, #Radii bins shifted to image center
                                   intbins*arad/np.percentile(np.abs(intbins),98)+(ycen-aycen)+arad, #Intensity bins, rescaled and shifted by image center
                                   xcen-axcen+arad, ycen-aycen+arad, #Shifted center
                                   radlists[i], #Previously fitted rings
                                   repr(i+1)+"/"+repr(len(objlist))) #numstring
        #Changing images and loop breakout conditions
        if ringplot.key == "d": i+=1
        if ringplot.key == "a": i+=-1
        if i == -1 or i == len(objlist):
            while True:
                yn = raw_input("Finished marking sky rings? (y/n) ")
                if "n" in yn or "N" in yn:
                    if i == -1: i=0
                    if i == len(objlist): i = len(objlist)-1
                    break
                elif "y" in yn or "Y" in yn:
                    break
        if i == -1 or i == len(objlist): break
        #Force-marking a ring
        if ringplot.key == "e" and ringplot.xcoo != None: radlists[i].append(ringplot.xcoo-arad-(xcen-axcen))
        #Deleting a ring
        if ringplot.key == "s" and ringplot.xcoo != None and len(radlists[i])>0:
            radlists[i].pop(np.argmin(np.array(radlists[i])-np.sqrt((ringplot.xcoo-arad-(xcen-axcen))**2 + (ringplot.ycoo-arad-(ycen-aycen))**2)))
        #Fitting a ring profile
        if ringplot.key == "w" and ringplot.xcoo != None:
            x = rbins[max(ringplot.xcoo-arad-(xcen-axcen)-50,0):min(ringplot.xcoo-arad-(xcen-axcen)+50,len(rbins))]**2
            y = intbins[max(ringplot.xcoo-arad-(xcen-axcen)-50,0):min(ringplot.xcoo-arad-(xcen-axcen)+50,len(rbins))]
            fit = GaussFit(x,y)
            fitplot = PlotRingFit(x,y,fit)
            if fitplot.key == "w": radlists[i].append(np.sqrt(fit[2]))
    zo = []
    to = []
    ro = []
    for i in range(len(objlist)):
        for j in range(len(radlists[i])):
            zo.append(objlist[i][0].header["ET1Z"])
            to.append(objlist[i][0].header["JD"])
            ro.append(radlists[i][j])
            
    #Fit rings in the ARC images
    xcen = objlist[0][0].header["FPXCEN"]
    ycen = objlist[0][0].header["FPYCEN"]
    radlists = []
    for i in range(len(arclist)):
        radlists.append([])
    i=0
    while True:
        xgrid, ygrid = np.meshgrid(np.arange(arclist[i][0].data.shape[1]), np.arange(arclist[i][0].data.shape[0]))
        axcen = arclist[i][0].header["FPAXCEN"]
        aycen = arclist[i][0].header["FPAYCEN"]
        arad = arclist[i][0].header["FPARAD"]
        rgrid = np.sqrt((xgrid - xcen)**2 + (ygrid - ycen)**2)
        rbins = np.arange(arad-np.int(max(abs(axcen-xcen),abs(aycen-ycen))))+1
        intbins = np.empty_like(rbins)
        for j in range(len(rbins)):
            intbins[j] = np.median(arclist[i][0].data[np.logical_and(np.logical_and(arclist[i][0].data!=0,rgrid<rbins[j]),rgrid>rbins[j]-1)])
        ringplot = PlotRingProfile(arclist[i][0].data[aycen-arad:aycen+arad,axcen-arad:axcen+arad], #Data to be plotted. Only want stuff inside aperture
                                   rbins+(xcen-axcen)+arad, #Radii bins shifted to image center
                                   intbins*arad/np.percentile(np.abs(intbins),98)+(ycen-aycen)+arad, #Intensity bins, rescaled and shifted by image center
                                   xcen-axcen+arad, ycen-aycen+arad, #Shifted center
                                   radlists[i], #Previously fitted rings
                                   repr(i+1)+"/"+repr(len(arclist))) #numstring
        #Changing images and loop breakout conditions
        if ringplot.key == "d": i+=1
        if ringplot.key == "a": i+=-1
        if i == -1 or i == len(arclist):
            while True:
                yn = raw_input("Finished marking ARC rings? (y/n) ")
                if "n" in yn or "N" in yn:
                    if i == -1: i=0
                    if i == len(arclist): i = len(arclist)-1
                    break
                elif "y" in yn or "Y" in yn:
                    break
        if i == -1 or i == len(arclist): break
        #Force-marking a ring
        if ringplot.key == "e" and ringplot.xcoo != None: radlists[i].append(ringplot.xcoo-arad-(xcen-axcen))
        #Deleting a ring
        if ringplot.key == "s" and ringplot.xcoo != None and len(radlists[i])>0:
            radlists[i].pop(np.argmin(np.array(radlists[i])-np.sqrt((ringplot.xcoo-arad-(xcen-axcen))**2 + (ringplot.ycoo-arad-(ycen-aycen))**2)))
        #Fitting a ring profile
        if ringplot.key == "w" and ringplot.xcoo != None:
            x = rbins[max(ringplot.xcoo-arad-(xcen-axcen)-50,0):min(ringplot.xcoo-arad-(xcen-axcen)+50,len(rbins))]**2
            y = intbins[max(ringplot.xcoo-arad-(xcen-axcen)-50,0):min(ringplot.xcoo-arad-(xcen-axcen)+50,len(rbins))]
            fit = GaussFit(x,y)
            fitplot = PlotRingFit(x,y,fit)
            if fitplot.key == "w": radlists[i].append(np.sqrt(fit[2]))
    za = []
    ta = []
    ra = []
    for i in range(len(arclist)):
        for j in range(len(radlists[i])):
            za.append(arclist[i][0].header["ET1Z"])
            ta.append(arclist[i][0].header["JD"])
            ra.append(radlists[i][j])
    
#     #Load previous ring fits from a text file - COMMENT THIS OUT LATER
#     rr,zz,tt = np.loadtxt("test.out",unpack=True)
#     za = list(zz[zz>0])
#     ta = list(tt[zz>0])
#     ra = list(rr[zz>0])
#     zo = list(zz[zz<0])
#     to = list(tt[zz<0])
#     ro = list(rr[zz<0])

    #Now we try to get a good guess at the wavelengths
    
    #Get a good guess at which wavelengths are which
    Bguess = objlist[0][0].header["ET1B"]
    Fguess = 5600
    
    #Figure out A by matching rings to the wavelength libraries
    master_r = np.array(ro+ra)
    master_z = np.array(zo+za)
    wavematch = np.zeros_like(master_r)
    isnight = np.array([True]*len(ro)+[False]*len(ra))
    oldrms = 10000 #Really high initial RMS for comparisons
    for i in range(len(master_r)):
        if isnight[i]: lib = nightlib
        else: lib = arclib
        for j in range(len(lib)):
            #Assume the i'th ring is the j'th line
            Aguess = lib[j]*np.sqrt(1+master_r[i]**2/Fguess**2)-Bguess*master_z[i]
            #What are all of the other rings, given this A?
            waveguess = (Aguess+Bguess*master_z)/np.sqrt(1+master_r**2/Fguess**2)
            for k in range(len(master_r)):
                if isnight[k]: wavematch[k] = nightlib[np.argmin(np.abs(nightlib-waveguess[k]))]
                else: wavematch[k] = arclib[np.argmin(np.abs(arclib-waveguess[k]))]
            rms = np.sqrt(np.average((waveguess-wavematch)**2))
            if rms < oldrms:
                #This is the new best solution. Keep it!
                oldrms = rms
                bestA = Aguess
                master_wave = wavematch.copy()
    
    #Make more master arrays for the plotting
    master_t = np.array(to+ta)
    t0 = np.min(master_t)
    master_t += -t0
    master_t *= 24*60 #Convert to minutes
    master_color = np.array(len(ro)*["blue"]+len(ra)*["red"]) #Colors for plotting
    toggle = np.ones(len(master_r),dtype="bool")
    dotime = False
    time_dividers = []
    
    #Do the interactive plotting
    while True:
        rplot = master_r[toggle]
        zplot = master_z[toggle]
        tplot = master_t[toggle]
        colorplot = master_color[toggle]
        waveplot = master_wave[toggle]
        fitplot = np.zeros(len(waveplot))
        xs = np.zeros((3,len(rplot)))
        xs[0] = rplot
        xs[1] = zplot
        xs[2] = tplot
        fit = [0]*(len(time_dividers)+1)
        time_dividers = sorted(time_dividers)
        if len(time_dividers)>1: print "Warning: Too many time divisions is likely unphysical. Be careful!"
        for i in range(len(time_dividers)+1):
            #Create a slice for all of the wavelengths before this time divider
            #but after the one before it
            if len(time_dividers)==0: tslice = tplot==tplot
            elif i == 0: tslice = tplot<time_dividers[i]
            elif i==len(time_dividers): tslice = tplot>time_dividers[i-1]
            else: tslice = np.logical_and(tplot<time_dividers[i],tplot>time_dividers[i-1])
            if dotime:
                fit[i] = curve_fit(fpfunc_for_curve_fit_with_t, xs[:,tslice], waveplot[tslice], p0=(bestA,Bguess,0,Fguess))[0]
                fitplot[tslice] = fpfunc_for_curve_fit_with_t(xs[:,tslice], fit[i][0], fit[i][1], fit[i][2], fit[i][3])
            else:
                fit[i] = curve_fit(fpfunc_for_curve_fit, xs[:,tslice], waveplot[tslice], p0=(bestA,Bguess,Fguess))[0]
                fitplot[tslice] = fpfunc_for_curve_fit(xs[:,tslice], fit[i][0], fit[i][1], fit[i][2])
        resid = waveplot - fitplot
        solnplot = WaveSolnPlot(rplot,zplot,tplot,waveplot,resid,colorplot,time_dividers)
        #Breakout case
        if solnplot.key == "a":
            while True:
                for i in range(len(time_dividers)+1):
                    if dotime: print "Solution 1: A = "+str(fit[i][0])+", B = "+str(fit[i][1])+", E = "+str(fit[i][2])+", F = "+str(fit[i][3])
                    else: print "Solution 1: A = "+str(fit[i][0])+", B = "+str(fit[i][1])+", F = "+str(fit[i][2])
                print "Residual rms="+str(np.sqrt(np.average(resid**2)))+" for "+repr(len(time_dividers)+1)+" independent "+repr(3+dotime)+"-parameter fits to "+repr(len(rplot))+" rings."
                yn = raw_input("Accept wavelength solution? (y/n) ")
                if "n" in yn or "N" in yn:
                    break
                elif "y" in yn or "Y" in yn:
                    solnplot.key = "QUIT"
                    break
        if solnplot.key == "QUIT": break
        #Restore all points case
        if solnplot.key == "r": toggle = np.ones(len(master_r),dtype="bool")
        #Delete nearest point case
        if solnplot.key == "d" and solnplot.axis != None:
            #Figure out which plot was clicked in
            if solnplot.axis == 1:
                #Resid vs. z plot
                z_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = ((zplot-z_loc)/(np.max(zplot)-np.min(zplot)))**2 + ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2
            elif solnplot.axis == 2:
                #Resid vs. R plot
                r_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = ((rplot-r_loc)/(np.max(rplot)-np.min(rplot)))**2 + ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2
            elif solnplot.axis == 3:
                #Resit vs. T plot
                t_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = ((tplot-t_loc)/(np.max(tplot)-np.min(tplot)))**2 + ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2
            elif solnplot.axis == 4:
                #Resid vs. Wave plot
                wave_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = ((waveplot-wave_loc)/(np.max(waveplot)-np.min(waveplot)))**2 + ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2
            #Get the radius and time of the worst ring
            r_mask = rplot[dist2 == np.min(dist2)][0]
            t_mask = tplot[dist2 == np.min(dist2)][0]
            toggle[np.logical_and(master_r == r_mask, master_t == t_mask)] = False
        #Fit for time case
        if solnplot.key == "t": dotime = not dotime
        #Add time break
        if solnplot.key == "w":
            timeplot = TimePlot(tplot,resid,colorplot,time_dividers)
            if timeplot.xcoo != None: time_dividers.append(timeplot.xcoo)
        #Remove time breaks
        if solnplot.key == "q":
            time_dividers = []