int fit_sip_wcs(const double* starxyz, const double* fieldxy, const double* weights, int M, const tan_t* tanin1, int sip_order, int inv_order, sip_t* sipout) { int sip_coeffs; double xyzcrval[3]; double cdinv[2][2]; double sx, sy, sU, sV, su, sv; int N; int i, j, p, q, order; double totalweight; int rtn; gsl_matrix *mA; gsl_vector *b1, *b2, *x1, *x2; gsl_vector *r1=NULL, *r2=NULL; tan_t tanin2; int ngood; const tan_t* tanin = &tanin2; // We need at least the linear terms to compute CD. if (sip_order < 1) sip_order = 1; // convenience: allow the user to call like: // fit_sip_wcs(... &(sipout.wcstan), ..., sipout); memcpy(&tanin2, tanin1, sizeof(tan_t)); memset(sipout, 0, sizeof(sip_t)); memcpy(&(sipout->wcstan), tanin, sizeof(tan_t)); sipout->a_order = sipout->b_order = sip_order; sipout->ap_order = sipout->bp_order = inv_order; // The SIP coefficients form an (order x order) upper triangular // matrix missing the 0,0 element. sip_coeffs = (sip_order + 1) * (sip_order + 2) / 2; N = sip_coeffs; if (M < N) { ERROR("Too few correspondences for the SIP order specified (%i < %i)\n", M, N); return -1; } mA = gsl_matrix_alloc(M, N); b1 = gsl_vector_alloc(M); b2 = gsl_vector_alloc(M); assert(mA); assert(b1); assert(b2); /* * We use a clever trick to estimate CD, A, and B terms in two * seperated least squares fits, then finding A and B by multiplying * the found parameters by CD inverse. * * Rearranging the SIP equations (see sip.h) we get the following * matrix operation to compute x and y in world intermediate * coordinates, which is convienently written in a way which allows * least squares estimation of CD and terms related to A and B. * * First use the x's to find the first set of parametetrs * * +--------------------- Intermediate world coordinates in DEGREES * | +--------- Pixel coordinates u and v in PIXELS * | | +--- Polynomial u,v terms in powers of PIXELS * v v v * ( x1 ) ( 1 u1 v1 p1 ) (sx ) * ( x2 ) = ( 1 u2 v2 p2 ) * (cd11 ) : * ( x3 ) ( 1 u3 v3 p3 ) (cd12 ) : * ( ...) ( ... ) (cd11*A + cd12*B ) : * cd11 is a scalar, degrees per pixel * cd12 is a scalar, degrees per pixel * cd11*A and cs12*B are mixture of SIP terms (A,B) and CD matrix * (cd11,cd12) * * Then find cd21 and cd22 with the y's * * ( y1 ) ( 1 u1 v1 p1 ) (sy ) * ( y2 ) = ( 1 u2 v2 p2 ) * (cd21 ) : * ( y3 ) ( 1 u3 v3 p3 ) (cd22 ) : * ( ...) ( ... ) (cd21*A + cd22*B ) : (Y4) * y2: scalar, degrees per pixel * y3: scalar, degrees per pixel * Y4: mixture of SIP terms (A,B) and CD matrix (cd21,cd22) * * These are both standard least squares problems which we solve with * QR decomposition, ie * min_{cd,A,B} || x - [1,u,v,p]*[s;cd;cdA+cdB]||^2 with * x reference, cd,A,B unrolled parameters. * * We get back (for x) a vector of optimal * [sx;cd11;cd12; cd11*A + cd12*B] * Now we can pull out sx, cd11 and cd12 from the beginning of this vector, * and call the rest of the vector [cd11*A] + [cd12*B]; * similarly for the y fit, we get back a vector of optimal * [sy;cd21;cd22; cd21*A + cd22*B] * once we have all those we can figure out A and B as follows * -1 * A' = [cd11 cd12] * [cd11*A' + cd12*B'] * B' [cd21 cd22] [cd21*A' + cd22*B'] * * which recovers the A and B's. * */ /* * Dustin's interpretation of the above: * We want to solve: * * min || b[M-by-1] - A[M-by-N] x[N-by-1] ||_2 * * M = the number of correspondences. * N = the number of SIP terms. * * And we want an overdetermined system, so M >= N. * * [ 1 u_1 v_1 u_1^2 u_1 v_1 v_1^2 ... ] * mA = [ 1 u_2 v_2 u_2^2 u_2 v_2 v_2^2 ... ] * [ ...... ] * * Where (u_i, v_i) are *undistorted* pixel positions minus CRPIX. * * The answers we want are: * * [ sx ] * x1 = [ cd11 ] * [ cd12 ] * [ (A) (B) ] * [ cd11*(A) + cd12*(B) ] * [ (A) (B) ] * * [ sy ] * x2 = [ cd21 ] * [ cd22 ] * [ (A) (B) ] * [ cd21*(A) + cd22*(B) ] * [ (A) (B) ] * * And the target vectors are the intermediate world coords of the * reference stars, in degrees. * * [ ix_1 ] * b1 = [ ix_2 ] * [ ... ] * * [ iy_1 ] * b2 = [ iy_2 ] * [ ... ] * * * (where A and B are tall vectors of SIP coefficients of order 2 * and above) * */ // Fill in matrix mA: radecdeg2xyzarr(tanin->crval[0], tanin->crval[1], xyzcrval); totalweight = 0.0; ngood = 0; for (i=0; i<M; i++) { double x=0, y=0; double weight = 1.0; double u; double v; Unused anbool ok; u = fieldxy[2*i + 0] - tanin->crpix[0]; v = fieldxy[2*i + 1] - tanin->crpix[1]; // B contains Intermediate World Coordinates (in degrees) // tangent-plane projection ok = star_coords(starxyz + 3*i, xyzcrval, TRUE, &x, &y); if (!ok) { logverb("Skipping star that cannot be projected to tangent plane\n"); continue; } gsl_vector_set(b1, ngood, weight * rad2deg(x)); gsl_vector_set(b2, ngood, weight * rad2deg(y)); if (weights) { weight = weights[i]; assert(weight >= 0.0); assert(weight <= 1.0); totalweight += weight; if (weight == 0.0) continue; } /* The coefficients are stored in this order: * p q * (0,0) = 1 <- order 0 * (1,0) = u <- order 1 * (0,1) = v * (2,0) = u^2 <- order 2 * (1,1) = uv * (0,2) = v^2 * ... */ j = 0; for (order=0; order<=sip_order; order++) { for (q=0; q<=order; q++) { p = order - q; assert(j >= 0); assert(j < N); assert(p >= 0); assert(q >= 0); assert(p + q <= sip_order); gsl_matrix_set(mA, ngood, j, weight * pow(u, (double)p) * pow(v, (double)q)); j++; } } assert(j == N); // The shift - aka (0,0) - SIP coefficient must be 1. assert(gsl_matrix_get(mA, i, 0) == 1.0 * weight); assert(fabs(gsl_matrix_get(mA, i, 1) - u * weight) < 1e-12); assert(fabs(gsl_matrix_get(mA, i, 2) - v * weight) < 1e-12); ngood++; } if (ngood == 0) { ERROR("No stars projected within the image\n"); return -1; } if (weights) logverb("Total weight: %g\n", totalweight); if (ngood < M) { _gsl_vector_view sub_b1 = gsl_vector_subvector(b1, 0, ngood); _gsl_vector_view sub_b2 = gsl_vector_subvector(b2, 0, ngood); _gsl_matrix_view sub_mA = gsl_matrix_submatrix(mA, 0, 0, ngood, N); rtn = gslutils_solve_leastsquares_v(&(sub_mA.matrix), 2, &(sub_b1.vector), &x1, NULL, &(sub_b2.vector), &x2, NULL); } else { // Solve the equation. rtn = gslutils_solve_leastsquares_v(mA, 2, b1, &x1, NULL, b2, &x2, NULL); } if (rtn) { ERROR("Failed to solve SIP matrix equation!"); return -1; } // Row 0 of X are the shift (p=0, q=0) terms. // Row 1 of X are the terms that multiply "u". // Row 2 of X are the terms that multiply "v". // Grab CD. sipout->wcstan.cd[0][0] = gsl_vector_get(x1, 1); sipout->wcstan.cd[0][1] = gsl_vector_get(x1, 2); sipout->wcstan.cd[1][0] = gsl_vector_get(x2, 1); sipout->wcstan.cd[1][1] = gsl_vector_get(x2, 2); // Compute inv(CD) i = invert_2by2_arr((const double*)(sipout->wcstan.cd), (double*)cdinv); assert(i == 0); // Grab the shift. sx = gsl_vector_get(x1, 0); sy = gsl_vector_get(x2, 0); // Extract the SIP coefficients. // (this includes the 0 and 1 order terms, which we later overwrite) j = 0; for (order=0; order<=sip_order; order++) { for (q=0; q<=order; q++) { p = order - q; assert(j >= 0); assert(j < N); assert(p >= 0); assert(q >= 0); assert(p + q <= sip_order); sipout->a[p][q] = cdinv[0][0] * gsl_vector_get(x1, j) + cdinv[0][1] * gsl_vector_get(x2, j); sipout->b[p][q] = cdinv[1][0] * gsl_vector_get(x1, j) + cdinv[1][1] * gsl_vector_get(x2, j); j++; } } assert(j == N); // We have already dealt with the shift and linear terms, so zero them out // in the SIP coefficient matrix. sipout->a[0][0] = 0.0; sipout->a[0][1] = 0.0; sipout->a[1][0] = 0.0; sipout->b[0][0] = 0.0; sipout->b[0][1] = 0.0; sipout->b[1][0] = 0.0; sip_compute_inverse_polynomials(sipout, 0, 0, 0, 0, 0, 0); sU = cdinv[0][0] * sx + cdinv[0][1] * sy; sV = cdinv[1][0] * sx + cdinv[1][1] * sy; logverb("Applying shift of sx,sy = %g,%g deg (%g,%g pix) to CRVAL and CD.\n", sx, sy, sU, sV); sip_calc_inv_distortion(sipout, sU, sV, &su, &sv); debug("sx = %g, sy = %g\n", sx, sy); debug("sU = %g, sV = %g\n", sU, sV); debug("su = %g, sv = %g\n", su, sv); wcs_shift(&(sipout->wcstan), -su, -sv); if (r1) gsl_vector_free(r1); if (r2) gsl_vector_free(r2); gsl_matrix_free(mA); gsl_vector_free(b1); gsl_vector_free(b2); gsl_vector_free(x1); gsl_vector_free(x2); return 0; }
void test_tweak_1(CuTest* tc) { int GX = 5; int GY = 5; double origxy[GX*GY*2]; double xy[GX*GY*2]; double radec[GX*GY*2]; double gridx[GX]; double gridy[GY]; sip_t thesip; sip_t* sip = &thesip; tan_t* tan = &(sip->wcstan); int i; sip_t* outsip; printf("\ntest_tweak_1\n\n"); log_init(LOG_VERB); memset(sip, 0, sizeof(sip_t)); tan->imagew = 2000; tan->imageh = 2000; tan->crval[0] = 150; tan->crval[1] = -30; tan->crpix[0] = 1000.5; tan->crpix[1] = 1000.5; tan->cd[0][0] = 1./1000.; tan->cd[0][1] = 0; tan->cd[1][1] = 1./1000.; tan->cd[1][0] = 0; sip->a_order = sip->b_order = 2; sip->a[2][0] = 10.*1e-6; sip->ap_order = sip->bp_order = 4; sip_compute_inverse_polynomials(sip, 0, 0, 0, 0, 0, 0); /* printf("After compute_inverse_polynomials:\n"); sip_print_to(sip, stdout); fflush(NULL); */ set_grid(GX, GY, tan, sip, origxy, radec, xy, gridx, gridy); /*{ int i,j; printf("RA,Dec\n"); for (i=0; i<GY; i++) { for (j=0; j<GX; j++) { fflush(NULL); printf("gy %i gyx %i: %g %g\n", i, j, radec[2*(i*GX+j)], radec[2*(i*GX+j) + 1]); fflush(NULL); } } }*/ outsip = run_test(tc, sip, GX*GY, xy, radec); CuAssertDblEquals(tc, tan->crval[0], outsip->wcstan.crval[0], 1e-6); CuAssertDblEquals(tc, tan->crval[1], outsip->wcstan.crval[1], 1e-6); CuAssertDblEquals(tc, tan->cd[0][0], outsip->wcstan.cd[0][0], 1e-10); CuAssertDblEquals(tc, tan->cd[0][1], outsip->wcstan.cd[0][1], 1e-14); CuAssertDblEquals(tc, tan->cd[1][0], outsip->wcstan.cd[1][0], 1e-14); CuAssertDblEquals(tc, tan->cd[1][1], outsip->wcstan.cd[1][1], 1e-10); double *d1, *d2; d1 = (double*)outsip->a; d2 = (double*)&(sip->a); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) CuAssertDblEquals(tc, d2[i], d1[i], 1e-13); d1 = (double*)outsip->b; d2 = (double*)&(sip->b); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) { printf("test_tweak_1: Expecting %.18g, got %.18g\n", d2[i], d1[i]); fflush(NULL); CuAssertDblEquals(tc, d2[i], d1[i], 3e-18); } d1 = (double*)outsip->ap; d2 = (double*)&(sip->ap); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) CuAssertDblEquals(tc, d2[i], d1[i], 1e-10); d1 = (double*)outsip->bp; d2 = (double*)&(sip->bp); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) CuAssertDblEquals(tc, d2[i], d1[i], 1e-10); CuAssertIntEquals(tc, sip->a_order, outsip->a_order); CuAssertIntEquals(tc, sip->b_order, outsip->b_order); CuAssertIntEquals(tc, sip->ap_order, outsip->ap_order); CuAssertIntEquals(tc, sip->bp_order, outsip->bp_order); }
static void tst_tweak_n(CuTest* tc, int run, int GX, int GY) { double origxy[GX*GY*2]; double xy[GX*GY*2]; double noisyxy[GX*GY*2]; double radec[GX*GY*2]; double gridx[GX]; double gridy[GY]; sip_t thesip; sip_t* sip = &thesip; tan_t* tan = &(sip->wcstan); int i,j; sip_t* outsip; printf("\ntest_tweak_%i\n\n", run); log_init(LOG_VERB); memset(sip, 0, sizeof(sip_t)); tan->imagew = 2000; tan->imageh = 2000; tan->crval[0] = 150; tan->crval[1] = -30; tan->crpix[0] = 1000.5; tan->crpix[1] = 1000.5; tan->cd[0][0] = 1./1000.; tan->cd[0][1] = 0; tan->cd[1][1] = 1./1000.; tan->cd[1][0] = 0; sip->a_order = sip->b_order = 2; sip->a[2][0] = 10.*1e-6; sip->b[0][2] = -10.*1e-6; sip->ap_order = sip->bp_order = 4; sip_compute_inverse_polynomials(sip, 0, 0, 0, 0, 0, 0); set_grid(GX, GY, tan, sip, origxy, radec, xy, gridx, gridy); // add noise to observed xy positions. srand(42); for (i=0; i<(GX*GY*2); i++) { noisyxy[i] = xy[i] + gaussian_sample(0.0, 1.0); } fprintf(stderr, "from numpy import array\n"); fprintf(stderr, "x0,y0 = %g,%g\n", tan->crpix[0], tan->crpix[1]); fprintf(stderr, "gridx_%i=array([", run); for (i=0; i<GX; i++) fprintf(stderr, "%g, ", gridx[i]); fprintf(stderr, "])\n"); fprintf(stderr, "gridy_%i=array([", run); for (i=0; i<GY; i++) fprintf(stderr, "%g, ", gridy[i]); fprintf(stderr, "])\n"); fprintf(stderr, "origxy_%i = array([", run); for (i=0; i<(GX*GY); i++) fprintf(stderr, "[%g,%g],", origxy[2*i+0], origxy[2*i+1]); fprintf(stderr, "])\n"); fprintf(stderr, "xy_%i = array([", run); for (i=0; i<(GX*GY); i++) fprintf(stderr, "[%g,%g],", xy[2*i+0], xy[2*i+1]); fprintf(stderr, "])\n"); fprintf(stderr, "noisyxy_%i = array([", run); for (i=0; i<(GX*GY); i++) fprintf(stderr, "[%g,%g],", noisyxy[2*i+0], noisyxy[2*i+1]); fprintf(stderr, "])\n"); fprintf(stderr, "truesip_a_%i = array([", run); for (i=0; i<=sip->a_order; i++) for (j=0; j<=sip->a_order; j++) if (sip->a[i][j] != 0) fprintf(stderr, "[%i,%i,%g],", i, j, sip->a[i][j]); fprintf(stderr, "])\n"); fprintf(stderr, "truesip_b_%i = array([", run); for (i=0; i<=sip->a_order; i++) for (j=0; j<=sip->a_order; j++) if (sip->b[i][j] != 0) fprintf(stderr, "[%i,%i,%g],", i, j, sip->b[i][j]); fprintf(stderr, "])\n"); fprintf(stderr, "sip_a_%i = {}\n", run); fprintf(stderr, "sip_b_%i = {}\n", run); int o; for (o=2; o<6; o++) { sip->a_order = o; outsip = run_test(tc, sip, GX*GY, noisyxy, radec); fprintf(stderr, "sip_a_%i[%i] = array([", run, o); for (i=0; i<=outsip->a_order; i++) for (j=0; j<=outsip->a_order; j++) if (outsip->a[i][j] != 0) fprintf(stderr, "[%i,%i,%g],", i, j, outsip->a[i][j]); fprintf(stderr, "])\n"); fprintf(stderr, "sip_b_%i[%i] = array([", run, o); for (i=0; i<=outsip->a_order; i++) for (j=0; j<=outsip->a_order; j++) if (outsip->b[i][j] != 0) fprintf(stderr, "[%i,%i,%g],", i, j, outsip->b[i][j]); fprintf(stderr, "])\n"); } sip->a_order = 2; outsip = run_test(tc, sip, GX*GY, noisyxy, radec); CuAssertDblEquals(tc, tan->crval[0], outsip->wcstan.crval[0], 1e-3); CuAssertDblEquals(tc, tan->crval[1], outsip->wcstan.crval[1], 1e-3); CuAssertDblEquals(tc, tan->cd[0][0], outsip->wcstan.cd[0][0], 1e-6); CuAssertDblEquals(tc, tan->cd[0][1], outsip->wcstan.cd[0][1], 1e-6); CuAssertDblEquals(tc, tan->cd[1][0], outsip->wcstan.cd[1][0], 1e-6); CuAssertDblEquals(tc, tan->cd[1][1], outsip->wcstan.cd[1][1], 1e-6); if (run == 2) { double *d1, *d2; d1 = (double*)outsip->a; d2 = (double*)&(sip->a); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) // rather large error, no? CuAssertDblEquals(tc, d2[i], d1[i], 6e-7); d1 = (double*)outsip->b; d2 = (double*)&(sip->b); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) { printf("test_tweak_2, run 2: Expecting %.18g, got %.18g\n", d2[i], d1[i]); fflush(NULL); CuAssertDblEquals(tc, d2[i], d1[i], 3e-7); } d1 = (double*)outsip->ap; d2 = (double*)&(sip->ap); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) CuAssertDblEquals(tc, d2[i], d1[i], 1e-6); d1 = (double*)outsip->bp; d2 = (double*)&(sip->bp); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) CuAssertDblEquals(tc, d2[i], d1[i], 1e-6); CuAssertIntEquals(tc, sip->a_order, outsip->a_order); CuAssertIntEquals(tc, sip->b_order, outsip->b_order); CuAssertIntEquals(tc, sip->ap_order, outsip->ap_order); CuAssertIntEquals(tc, sip->bp_order, outsip->bp_order); } else if (run == 3) { double *d1, *d2; d1 = (double*)outsip->a; d2 = (double*)&(sip->a); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) { // rather large error, no? printf("test_tweak_2, run 3: Expecting %.18g, got %.18g\n", d2[i], d1[i]); fflush(NULL); CuAssertDblEquals(tc, d2[i], d1[i], 7e-7); } d1 = (double*)outsip->b; d2 = (double*)&(sip->b); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) { printf("test_tweak_2, run 3b: Expecting %.18g, got %.18g\n", d2[i], d1[i]); fflush(NULL); CuAssertDblEquals(tc, d2[i], d1[i], 2e-6); } d1 = (double*)outsip->ap; d2 = (double*)&(sip->ap); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) CuAssertDblEquals(tc, d2[i], d1[i], 1e-6); d1 = (double*)outsip->bp; d2 = (double*)&(sip->bp); for (i=0; i<(SIP_MAXORDER * SIP_MAXORDER); i++) { printf("test_tweak_2, run 3c: Expecting %.18g, got %.18g\n", d2[i], d1[i]); fflush(NULL); CuAssertDblEquals(tc, d2[i], d1[i], 2e-6); } CuAssertIntEquals(tc, sip->a_order, outsip->a_order); CuAssertIntEquals(tc, sip->b_order, outsip->b_order); CuAssertIntEquals(tc, sip->ap_order, outsip->ap_order); CuAssertIntEquals(tc, sip->bp_order, outsip->bp_order); } }
sip_t* wcs_pv2sip_header(qfits_header* hdr, double* xy, int Nxy, double stepsize, double xlo, double xhi, double ylo, double yhi, int imageW, int imageH, int order, anbool forcetan, int doshift) { double* radec = NULL; int rtn = -1; tan_t tanwcs; double x,y, px,py; double* rddist = NULL; int i, j; int nx, ny; double xstep, ystep; sip_t* sip = NULL; /** From http://iraf.noao.edu/projects/mosaic/tpv.html p = PV1_ xi' = p0 + p1 * xi + p2 * eta + p3 * r + p4 * xi^2 + p5 * xi * eta + p6 * eta^2 + p7 * xi^3 + p8 * xi^2 * eta + p9 * xi * eta^2 + p10 * eta^3 + p11 * r^3 + p12 * xi^4 + p13 * xi^3 * eta + p14 * xi^2 * eta^2 + p15 * xi * eta^3 + p16 * eta^4 + p17 * xi^5 + p18 * xi^4 * eta + p19 * xi^3 * eta^2 + p20 * xi^2 * eta^3 + p21 * xi * eta^4 + p22 * eta^5 + p23 * r^5 + p24 * xi^6 + p25 * xi^5 * eta + p26 * xi^4 * eta^2 + p27 * xi^3 * eta^3 + p28 * xi^2 * eta^4 + p29 * xi * eta^5 + p30 * eta^6 p31 * xi^7 + p32 * xi^6 * eta + p33 * xi^5 * eta^2 + p34 * xi^4 * eta^3 + p35 * xi^3 * eta^4 + p36 * xi^2 * eta^5 + p37 * xi * eta^6 + p38 * eta^7 + p39 * r^7 p = PV2_ eta' = p0 + p1 * eta + p2 * xi + p3 * r + p4 * eta^2 + p5 * eta * xi + p6 * xi^2 + p7 * eta^3 + p8 * eta^2 * xi + p9 * eta * xi^2 + p10 * xi^3 + p11 * r^3 + p12 * eta^4 + p13 * eta^3 * xi + p14 * eta^2 * xi^2 + p15 * eta * xi^3 + p16 * xi^4 + p17 * eta^5 + p18 * eta^4 * xi + p19 * eta^3 * xi^2 + p20 * eta^2 * xi^3 + p21 * eta * xi^4 + p22 * xi^5 + p23 * r^5 + p24 * eta^6 + p25 * eta^5 * xi + p26 * eta^4 * xi^2 + p27 * eta^3 * xi^3 + p28 * eta^2 * xi^4 + p29 * eta * xi^5 + p30 * xi^6 p31 * eta^7 + p32 * eta^6 * xi + p33 * eta^5 * xi^2 + p34 * eta^4 * xi^3 + p35 * eta^3 * xi^4 + p36 * eta^2 * xi^5 + p37 * eta * xi^6 + p38 * xi^7 + p39 * r^7 Note the "cross-over" -- the xi' powers are in terms of xi,eta while the eta' powers are in terms of eta,xi. */ // 1 x y r x2 xy y2 x3 x2y xy2 y3 r3 x4 x3y x2y2 xy3 y4 // x5 x4y x3y2 x2y3 xy4 y5 r5 x6 x5y x4y2, x3y3 x2y4 xy5 y6 // x7 x6y x5y2 x4y3 x3y4 x2y5 xy6 y7 r7 int xp[] = { 0, 1, 0, 0, 2, 1, 0, 3, 2, 1, 0, 0, 4, 3, 2, 1, 0, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 0}; int yp[] = { 0, 0, 1, 0, 0, 1, 2, 0, 1, 2, 3, 0, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 7, 0}; int rp[] = { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7}; double xpows[8]; double ypows[8]; double rpows[8]; double pv1[40]; double pv2[40]; double r; char* ct; ct = fits_get_dupstring(hdr, "CTYPE1"); if ((ct && streq(ct, "RA---TPV")) || forcetan) { // http://iraf.noao.edu/projects/ccdmosaic/tpv.html logmsg("Replacing CTYPE1 = %s header with RA---TAN\n", ct); fits_update_value(hdr, "CTYPE1", "RA---TAN"); } ct = fits_get_dupstring(hdr, "CTYPE2"); if ((ct && streq(ct, "DEC--TPV")) || forcetan) { logmsg("Replacing CTYPE2 = %s header with DEC--TAN\n", ct); fits_update_value(hdr, "CTYPE2", "DEC--TAN"); } tan_read_header(hdr, &tanwcs); if (log_get_level() >= LOG_VERB) { printf("Read TAN header:\n"); tan_print(&tanwcs); } if (imageW && (imageW != tanwcs.imagew)) { logmsg("Overriding image width %f with user-specified %i\n", tanwcs.imagew, imageW); tanwcs.imagew = imageW; } if (imageH && (imageH != tanwcs.imageh)) { logmsg("Overriding image height %f with user-specified %i\n", tanwcs.imageh, imageH); tanwcs.imageh = imageH; } for (i=0; i<sizeof(pv1)/sizeof(double); i++) { char key[10]; double defaultval; if (i == 1) { defaultval = 1.0; } else { defaultval = 0.0; } sprintf(key, "PV1_%i", i); pv1[i] = qfits_header_getdouble(hdr, key, defaultval); sprintf(key, "PV2_%i", i); pv2[i] = qfits_header_getdouble(hdr, key, defaultval); } // choose grid for evaluating TAN-PV WCS if (xlo == 0 && xhi == 0) { xlo = 1.; xhi = tanwcs.imagew; } if (ylo == 0 && yhi == 0) { ylo = 1.; yhi = tanwcs.imageh; } assert(xhi >= xlo); assert(yhi >= ylo); if (stepsize == 0) stepsize = 100.; nx = MAX(2, round((xhi - xlo)/stepsize)); ny = MAX(2, round((yhi - ylo)/stepsize)); xstep = (xhi - xlo) / (double)(nx - 1); ystep = (yhi - ylo) / (double)(ny - 1); logverb("Stepping from x = %g to %g, steps of %g\n", xlo, xhi, xstep); logverb("Stepping from y = %g to %g, steps of %g\n", ylo, yhi, ystep); Nxy = nx * ny; if (xy == NULL) { int k = 0; xy = malloc(Nxy * 2 * sizeof(double)); for (i=0; i<ny; i++) { y = ylo + i*ystep; for (j=0; j<nx; j++) { x = xlo + j*xstep; //if (i == 0) //printf("x=%f\n", x); xy[k] = x; k++; xy[k] = y; k++; } //printf("y=%f\n", y); } assert(k == (Nxy*2)); } // distorted RA,Dec rddist = malloc(2 * Nxy * sizeof(double)); for (j=0; j<Nxy; j++) { double ix = xy[2*j+0]; double iy = xy[2*j+1]; tan_pixelxy2iwc(&tanwcs, ix, iy, &x, &y); // "x,y" here are most commonly known as "xi, eta". r = sqrt(x*x + y*y); // compute powers of x,y xpows[0] = ypows[0] = rpows[0] = 1.0; for (i=1; i<sizeof(xpows)/sizeof(double); i++) { xpows[i] = xpows[i-1]*x; ypows[i] = ypows[i-1]*y; rpows[i] = rpows[i-1]*r; } px = py = 0; for (i=0; i<sizeof(xp)/sizeof(int); i++) { px += pv1[i] * xpows[xp[i]] * ypows[yp[i]] * rpows[rp[i]]; // here's the "cross-over" mentioned above py += pv2[i] * ypows[xp[i]] * xpows[yp[i]] * rpows[rp[i]]; } // Note that the PV terms *include* a linear term, so no need // to re-add x,y to px,py. tan_iwc2radec(&tanwcs, px, py, rddist + 2*j, rddist + 2*j + 1); } sip = malloc(sizeof(sip_t)); assert(sip); { double* starxyz; starxyz = malloc(3 * Nxy * sizeof(double)); for (i=0; i<Nxy; i++) radecdegarr2xyzarr(rddist + i*2, starxyz + i*3); memset(sip, 0, sizeof(sip_t)); rtn = fit_sip_coefficients(starxyz, xy, NULL, Nxy, &tanwcs, order, order, sip); assert(rtn == 0); if (log_get_level() >= LOG_VERB) { printf("Fit SIP:\n"); sip_print(sip); } // FIXME? -- use xlo,xhi,ylo,yhi here?? Not clear. sip_compute_inverse_polynomials(sip, 0, 0, 0, 0, 0, 0); if (log_get_level() >= LOG_VERB) { printf("Fit SIP inverse polynomials:\n"); sip_print(sip); } free(starxyz); } free(rddist); free(radec); return sip; }