def test_calibration(test_x, test_y):
     def valid(a, b, name):
         d = sphere.spherical_distance(a,b)
         if d<1e-6:
             print "Testing %s: OK, error is within %.4e" % (name, d)
             return True
         else:
             print "Testing %s: FAIL! error exceeds 1e-6 at %.4e" % (name, d)
             return False
         
     lat, lon = sphere.tuio_to_polar(test_x, test_y)  
     lon += np.pi
     assert(valid(cal.test_calibrated_touch(test_x, test_y, 'gp'),gp_adjust(lon, lat, gp_x, gp_y), 'GP'))        
     assert(valid(cal.test_calibrated_touch(test_x, test_y, 'cubic'),cubic_polar_adjust(lon, lat, cubic_coeff), 'cubic'))        
     assert(valid(cal.test_calibrated_touch(test_x, test_y, 'quadratic'),quadratic_polar_adjust(lon, lat, quadratic_coeff),'quadratic'))        
     assert(valid(cal.test_calibrated_touch(test_x, test_y, 'constant'),polar_adjust(lon, lat, correction_factor), 'constant'))        
     assert(valid(cal.test_calibrated_touch(test_x, test_y, 'none'),polar_adjust(lon, lat, 1), 'none'))
def process_calibration(calibration_name=None, fit=None):        
    global calibration
    
    if calibration_name is None:
        print "WARNING: No calibration file specified."
        latest_csv = latest_file("calibration", ".csv")
        print "Using latest calibration file found: %s" % latest_csv
        calibration_name = latest_csv
    
    print "Processing calibration %s" % calibration_name
    print
    # load the calibration data
    calibration = pd.io.parsers.read_table(os.path.join("calibration", calibration_name), delimiter=",", skipinitialspace=True)
    augment_calibration(calibration)
    
    calibration["distance"] = [sphere.spherical_distance((r["target_lon"], r["target_lat"]),
                                                     (r["touch_lon"], r["touch_lat"])) for ix, r in calibration.iterrows()]
    total_targets = len(calibration)
    print "Read %d calibration targets" % total_targets
    calibration = calibration[calibration["distance"]<np.radians(25)]
    print "%d targets excluded as being outliers with >25 degree offset\n%d calibration targets remain" % (total_targets-len(calibration), len(calibration))

    grouped = calibration.groupby(["target_x", "target_y"])
    print "%d unique targets identified; %d repeats per target" % (len(grouped), int(0.5+total_targets/float(len(grouped))))
    print
    true_rms = estimate_touch_error(calibration, grouped)
    
    if fit is None:
        correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y = fit_models()
    else:
        correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y = fit
    
    uncorrected_rmse =  np.degrees(correction_error(1))
    constant_rmse = np.degrees((correction_error(correction_factor)))
    quadratic_rmse = np.degrees(quadratic_error(quadratic_coeff))
    cubic_rmse = np.degrees(cubic_error(cubic_coeff))
    gp_rmse = np.degrees(gp_error(gp_x, gp_y))    
    
    print "RMS Uncorrected error: %.2f degrees" % uncorrected_rmse
    print "RMS Corrected error: %.2f degrees" %  constant_rmse
    print "RMS Quadratic corrected error: %.2f degrees" %  quadratic_rmse
    print "RMS Cubic corrected error: %.2f degrees" % cubic_rmse
    print "RMS GP corrected error: %.2f degrees" % gp_rmse
    
    print
    plot_correction_models(calibration, correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y)
    fname = "calibration_plot.pdf"
    print
    print "Writing calibration plot to %s" % fname
    try:
        plt.savefig(fname,bbox_inches='tight', pad_inches=0)
    except IOError:
        print "WARNING: Could not write to %s" % fname
    
    plt.figtext(0.04, 0.16, "Calibration data from %s" % calibration_name, fontdict={"size":5})
    plt.figtext(0.04, 0.13, "%d calibration targets; %d unique" % (len(calibration), len(grouped)), fontdict={"size":5})
    plt.figtext(0.04, 0.1, "%.2f degrees intra-target error" % np.degrees(true_rms), fontdict={"size":5})
    error_labels(calibration, uncorrected_rmse, constant_rmse, quadratic_rmse, cubic_rmse, gp_rmse)    
    fname = "calibration_state.pdf"
    print
    print "Writing calibration plot to %s" % fname
    try:
        plt.savefig(fname)
    except IOError:
        print "WARNING: Could not write to %s" % fname
        
    plt.figure(figsize=(6,9))
    errors = [
    #error_distribution(calibration, lambda x,y: polar_adjust(x,y,1)),
    error_distribution(calibration, lambda x,y:polar_adjust(x,y,s=correction_factor)),
    error_distribution(calibration, lambda x,y:quadratic_polar_adjust(x,y,quadratic_coeff)),
    error_distribution(calibration, lambda x,y:cubic_polar_adjust(x,y,cubic_coeff)),
    error_distribution(calibration, lambda x,y:gp_adjust(x,y,gp_x,gp_y))]        
    plt.xlabel("Correction method")
    plt.ylabel("Error (degrees)")
    plt.gca().set_frame_on(False)
    simpleaxis(plt.gca())    
    seaborn.boxplot(errors)
    plt.xticks([0,1,2,3,4], ["","Constant", "Quadratic", "Cubic", "GP"])
    
    
    fname = "calibration_dist.pdf"    
    print "Writing calibration boxplot to %s" % fname
    try:
        plt.savefig(fname,bbox_inches='tight', pad_inches=0)
    except IOError:
        print "WARNING: Could not write to %s" % fname
    
        
    gp_offset_shading(calibration, gp_x, gp_y, correction_factor)
    fname = "gp_offset.pdf"
    plt.savefig("gp_offset.svg",bbox_inches='tight', pad_inches=0)
    print "Writing GP offset plot to %s" % fname
    try:
        plt.savefig(fname,bbox_inches='tight', pad_inches=0)
    except IOError:
        print "WARNING: Could not write to %s" % fname

    cal = calibration        
    # only write the calibration if we fitted new models
    if fit is None:    
        print
        print "Generating calibration.py"    
        write_module("calibration.py", calibration_name, calibration, correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y)    
        print
        print "Testing calibration.py..."

        import calibration as cal
        def test_calibration(test_x, test_y):
            def valid(a, b, name):
                d = sphere.spherical_distance(a,b)
                if d<1e-6:
                    print "Testing %s: OK, error is within %.4e" % (name, d)
                    return True
                else:
                    print "Testing %s: FAIL! error exceeds 1e-6 at %.4e" % (name, d)
                    return False
                
            lat, lon = sphere.tuio_to_polar(test_x, test_y)  
            lon += np.pi
            assert(valid(cal.test_calibrated_touch(test_x, test_y, 'gp'),gp_adjust(lon, lat, gp_x, gp_y), 'GP'))        
            assert(valid(cal.test_calibrated_touch(test_x, test_y, 'cubic'),cubic_polar_adjust(lon, lat, cubic_coeff), 'cubic'))        
            assert(valid(cal.test_calibrated_touch(test_x, test_y, 'quadratic'),quadratic_polar_adjust(lon, lat, quadratic_coeff),'quadratic'))        
            assert(valid(cal.test_calibrated_touch(test_x, test_y, 'constant'),polar_adjust(lon, lat, correction_factor), 'constant'))        
            assert(valid(cal.test_calibrated_touch(test_x, test_y, 'none'),polar_adjust(lon, lat, 1), 'none'))
        test_calibration(0.5, 0.5)
        print "calibration.py seems to be working"
        
        
    import calibration as cal
    tuio_time = timeit.timeit("lon, lat = sphere.polar_to_tuio(0.5, 0.5)", setup="import sphere", number=1000)
    for mode in ["none", "constant", "quadratic", "cubic", "gp"]:
        ds = []
        
        tx,ty=0.5,0.5
        mode_time = timeit.timeit("lon, lat = calibration.test_calibrated_touch(0.5, 0.5, mode='%s')"%mode, setup="import calibration", number=1000)
        print "%s took %d ms, %d ms more thant TUIO time" %(mode, 1000*mode_time, 1000*(mode_time-tuio_time))
        
        for ix, row in calibration.iterrows():
            tx, ty = row["tuio_x"], row["tuio_y"]                               
            lon, lat = cal.test_calibrated_touch(tx, ty, mode=mode)
            d = np.degrees(sphere.spherical_distance((lon, lat), (row["target_lon"], row["target_lat"])))
            ds.append(d)
        print "calibration.py median error for mode %s is %.2f degrees" % (mode, np.median(ds))
        
        
    return (correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y)