diff options
Diffstat (limited to 'wcs/tnxpos.c')
-rw-r--r-- | wcs/tnxpos.c | 1234 |
1 files changed, 1234 insertions, 0 deletions
diff --git a/wcs/tnxpos.c b/wcs/tnxpos.c new file mode 100644 index 0000000..e13d78e --- /dev/null +++ b/wcs/tnxpos.c @@ -0,0 +1,1234 @@ +/*** File wcslib/tnxpos.c + *** September 17, 2008 + *** By Jessica Mink, jmink@cfa.harvard.edu + *** Harvard-Smithsonian Center for Astrophysics + *** After IRAF mwcs/wftnx.x and mwcs/wfgsurfit.x + *** Copyright (C) 1998-2008 + *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Correspondence concerning WCSTools should be addressed as follows: + Internet email: jmink@cfa.harvard.edu + Postal address: Jessica Mink + Smithsonian Astrophysical Observatory + 60 Garden St. + Cambridge, MA 02138 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "wcs.h" + +#define SPHTOL 0.00001 +#define BADCVAL 0.0 +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +/* wftnx -- wcs function driver for the gnomonic projection with correction. + * tnxinit (header, wcs) + * tnxclose (wcs) + * tnxfwd (xpix, ypix, wcs, xpos, ypos) Pixels to WCS + * tnxrev (xpos, ypos, wcs, xpix, ypix) WCS to pixels + */ + +#define max_niter 500 +#define SZ_ATSTRING 2000 +static void wf_gsclose(); +static void wf_gsb1pol(); +static void wf_gsb1leg(); +static void wf_gsb1cheb(); + +/* tnxinit -- initialize the gnomonic forward or inverse transform. + * initialization for this transformation consists of, determining which + * axis is ra / lon and which is dec / lat, computing the celestial longitude + * and colatitude of the native pole, reading in the the native longitude + * of the pole of the celestial coordinate system longpole from the attribute + * list, precomputing euler angles and various intermediaries derived from the + * coordinate reference values, and reading in the projection parameter ro + * from the attribute list. if longpole is undefined then a value of 180.0 + * degrees is assumed. if ro is undefined a value of 180.0 / pi is assumed. + * the tan projection is equivalent to the azp projection with mu set to 0.0. + * in order to determine the axis order, the parameter "axtype={ra|dec} + * {xlon|glat}{xlon|elat}" must have been set in the attribute list for the + * function. the longpole and ro parameters may be set in either or both of + * the axes attribute lists, but the value in the ra axis attribute list takes + * precedence. + */ + +int +tnxinit (header, wcs) + +const char *header; /* FITS header */ +struct WorldCoor *wcs; /* pointer to WCS structure */ +{ + struct IRAFsurface *wf_gsopen(); + char *str1, *str2, *lngstr, *latstr; + extern void wcsrotset(); + + /* allocate space for the attribute strings */ + str1 = malloc (SZ_ATSTRING); + str2 = malloc (SZ_ATSTRING); + hgetm (header, "WAT1", SZ_ATSTRING, str1); + hgetm (header, "WAT2", SZ_ATSTRING, str2); + + lngstr = malloc (SZ_ATSTRING); + latstr = malloc (SZ_ATSTRING); + + /* determine the native longitude of the pole of the celestial + coordinate system corresponding to the FITS keyword longpole. + this number has no default and should normally be set to 180 + degrees. search both axes for this quantity. */ + + if (wcs->longpole > 360.0) { + if (!igetr8 (str1, "longpole", &wcs->longpole)) { + if (!igetr8 (str2, "longpole", &wcs->longpole)) + wcs->longpole = 180.0; + } + } + + /* Fetch the ro projection parameter which is the radius of the + generating sphere for the projection. if ro is absent which + is the usual case set it to 180 / pi. search both axes for + this quantity. */ + + if (!igetr8 (str1, "ro", &wcs->rodeg)) { + if (!igetr8 (str2, "ro", &wcs->rodeg)) + wcs->rodeg = 180.0 / PI; + } + + /* Fetch the longitude correction surface. note that the attribute + string may be of any length so the length of atvalue may have + to be adjusted. */ + + if (!igets (str1, "lngcor", SZ_ATSTRING, lngstr)) { + if (!igets (str2, "lngcor", SZ_ATSTRING, lngstr)) + wcs->lngcor = NULL; + else + wcs->lngcor = wf_gsopen (lngstr); + } + else + wcs->lngcor = wf_gsopen (lngstr); + + /* Fetch the latitude correction surface. note that the attribute + string may be of any length so the length of atvalue may have + to be adjusted. */ + + if (!igets (str2, "latcor", SZ_ATSTRING, latstr)) { + if (!igets (str1, "latcor", SZ_ATSTRING, latstr)) + wcs->latcor = NULL; + else + wcs->latcor = wf_gsopen (latstr); + } + else + wcs->latcor = wf_gsopen (latstr); + + /* Compute image rotation */ + wcsrotset (wcs); + + /* free working space. */ + free (str1); + free (str2); + free (lngstr); + free (latstr); + + /* Return 1 if there are no correction coefficients */ + if (wcs->latcor == NULL && wcs->lngcor == NULL) + return (1); + else + return (0); +} + + +/* tnxpos -- forward transform (physical to world) gnomonic projection. */ + +int +tnxpos (xpix, ypix, wcs, xpos, ypos) + +double xpix, ypix; /*i physical coordinates (x, y) */ +struct WorldCoor *wcs; /*i pointer to WCS descriptor */ +double *xpos, *ypos; /*o world coordinates (ra, dec) */ +{ + int ira, idec; + double x, y, r, phi, theta, costhe, sinthe, dphi, cosphi, sinphi, dlng, z; + double colatp, coslatp, sinlatp, longp; + double xs, ys, ra, dec, xp, yp; + double wf_gseval(); + + /* Convert from pixels to image coordinates */ + xpix = xpix - wcs->crpix[0]; + ypix = ypix - wcs->crpix[1]; + + /* Scale and rotate using CD matrix */ + if (wcs->rotmat) { + x = xpix * wcs->cd[0] + ypix * wcs->cd[1]; + y = xpix * wcs->cd[2] + ypix * wcs->cd[3]; + } + + else { + + /* Check axis increments - bail out if either 0 */ + if (wcs->cdelt[0] == 0.0 || wcs->cdelt[1] == 0.0) { + *xpos = 0.0; + *ypos = 0.0; + return 2; + } + + /* Scale using CDELT */ + xs = xpix * wcs->cdelt[0]; + ys = ypix * wcs->cdelt[1]; + + /* Take out rotation from CROTA */ + if (wcs->rot != 0.0) { + double cosr = cos (degrad (wcs->rot)); + double sinr = sin (degrad (wcs->rot)); + x = xs * cosr - ys * sinr; + y = xs * sinr + ys * cosr; + } + else { + x = xs; + y = ys; + } + } + + /* get the axis numbers */ + if (wcs->coorflip) { + ira = 1; + idec = 0; + } + else { + ira = 0; + idec = 1; + } + colatp = degrad (90.0 - wcs->crval[idec]); + coslatp = cos(colatp); + sinlatp = sin(colatp); + longp = degrad(wcs->longpole); + + /* Compute native spherical coordinates phi and theta in degrees from the + projected coordinates. this is the projection part of the computation */ + if (wcs->lngcor != NULL) + xp = x + wf_gseval (wcs->lngcor, x, y); + else + xp = x; + if (wcs->latcor != NULL) + yp = y + wf_gseval (wcs->latcor, x, y); + else + yp = y; + x = xp; + y = yp; + r = sqrt (x * x + y * y); + + /* Compute phi */ + if (r == 0.0) + phi = 0.0; + else + phi = atan2 (x, -y); + + /* Compute theta */ + theta = atan2 (wcs->rodeg, r); + + /* Compute the celestial coordinates ra and dec from the native + coordinates phi and theta. this is the spherical geometry part + of the computation */ + + costhe = cos (theta); + sinthe = sin (theta); + dphi = phi - longp; + cosphi = cos (dphi); + sinphi = sin (dphi); + + /* Compute the ra */ + x = sinthe * sinlatp - costhe * coslatp * cosphi; + if (fabs (x) < SPHTOL) + x = -cos (theta + colatp) + costhe * coslatp * (1.0 - cosphi); + y = -costhe * sinphi; + if (x != 0.0 || y != 0.0) + dlng = atan2 (y, x); + else + dlng = dphi + PI ; + ra = wcs->crval[ira] + raddeg(dlng); + + /* normalize ra */ + if (wcs->crval[ira] >= 0.0) { + if (ra < 0.0) + ra = ra + 360.0; + } + else { + if (ra > 0.0) + ra = ra - 360.0; + } + if (ra > 360.0) + ra = ra - 360.0; + else if (ra < -360.0) + ra = ra + 360.0; + + /* compute the dec */ + if (fmod (dphi, PI) == 0.0) { + dec = raddeg(theta + cosphi * colatp); + if (dec > 90.0) + dec = 180.0 - dec; + if (dec < -90.0) + dec = -180.0 - dec; + } + else { + z = sinthe * coslatp + costhe * sinlatp * cosphi; + if (fabs(z) > 0.99) { + if (z >= 0.0) + dec = raddeg(acos (sqrt(x * x + y * y))); + else + dec = raddeg(-acos (sqrt(x * x + y * y))); + } + else + dec = raddeg(asin (z)); + } + + /* store the results */ + *xpos = ra; + *ypos = dec; + return (0); +} + + +/* tnxpix -- inverse transform (world to physical) gnomonic projection */ + +int +tnxpix (xpos, ypos, wcs, xpix, ypix) + +double xpos, ypos; /*i world coordinates (ra, dec) */ +struct WorldCoor *wcs; /*i pointer to WCS descriptor */ +double *xpix, *ypix; /*o physical coordinates (x, y) */ +{ + int ira, idec, niter; + double ra, dec, cosdec, sindec, cosra, sinra, x, y, phi, theta; + double s, r, dphi, z, dpi, dhalfpi, twopi, tx; + double xm, ym, f, fx, fy, g, gx, gy, denom, dx, dy; + double colatp, coslatp, sinlatp, longp, sphtol; + double wf_gseval(), wf_gsder(); + + /* get the axis numbers */ + if (wcs->coorflip) { + ira = 1; + idec = 0; + } + else { + ira = 0; + idec = 1; + } + + /* Compute the transformation from celestial coordinates ra and + dec to native coordinates phi and theta. this is the spherical + geometry part of the transformation */ + + ra = degrad (xpos - wcs->crval[ira]); + dec = degrad (ypos); + cosra = cos (ra); + sinra = sin (ra); + cosdec = cos (dec); + sindec = sin (dec); + colatp = degrad (90.0 - wcs->crval[idec]); + coslatp = cos (colatp); + sinlatp = sin (colatp); + if (wcs->longpole == 999.0) + longp = degrad (180.0); + else + longp = degrad(wcs->longpole); + dpi = PI; + dhalfpi = dpi * 0.5; + twopi = PI + PI; + sphtol = SPHTOL; + + /* Compute phi */ + x = sindec * sinlatp - cosdec * coslatp * cosra; + if (fabs(x) < sphtol) + x = -cos (dec + colatp) + cosdec * coslatp * (1.0 - cosra); + y = -cosdec * sinra; + if (x != 0.0 || y != 0.0) + dphi = atan2 (y, x); + else + dphi = ra - dpi; + phi = longp + dphi; + if (phi > dpi) + phi = phi - twopi; + else if (phi < -dpi) + phi = phi + twopi; + + /* Compute theta */ + if (fmod (ra, dpi) == 0.0) { + theta = dec + cosra * colatp; + if (theta > dhalfpi) + theta = dpi - theta; + if (theta < -dhalfpi) + theta = -dpi - theta; + } + else { + z = sindec * coslatp + cosdec * sinlatp * cosra; + if (fabs (z) > 0.99) { + if (z >= 0.0) + theta = acos (sqrt(x * x + y * y)); + else + theta = -acos (sqrt(x * x + y * y)); + } + else + theta = asin (z); + } + + /* Compute the transformation from native coordinates phi and theta + to projected coordinates x and y */ + + s = sin (theta); + if (s == 0.0) { + x = BADCVAL; + y = BADCVAL; + } + else { + r = wcs->rodeg * cos (theta) / s; + if (wcs->lngcor == NULL && wcs->latcor == NULL) { + if (wcs->coorflip) { + y = r * sin (phi); + x = -r * cos (phi); + } + else { + x = r * sin (phi); + y = -r * cos (phi); + } + } + else { + xm = r * sin (phi); + ym = -r * cos (phi); + x = xm; + y = ym; + niter = 0; + while (niter < max_niter) { + if (wcs->lngcor != NULL) { + f = x + wf_gseval (wcs->lngcor, x, y) - xm; + fx = wf_gsder (wcs->lngcor, x, y, 1, 0); + fx = 1.0 + fx; + fy = wf_gsder (wcs->lngcor, x, y, 0, 1); + } + else { + f = x - xm; + fx = 1.0 ; + fy = 0.0; + } + if (wcs->latcor != NULL) { + g = y + wf_gseval (wcs->latcor, x, y) - ym; + gx = wf_gsder (wcs->latcor, x, y, 1, 0); + gy = wf_gsder (wcs->latcor, x, y, 0, 1); + gy = 1.0 + gy; + } + else { + g = y - ym; + gx = 0.0 ; + gy = 1.0; + } + + denom = fx * gy - fy * gx; + if (denom == 0.0) + break; + dx = (-f * gy + g * fy) / denom; + dy = (-g * fx + f * gx) / denom; + x = x + dx; + y = y + dy; + if (MAX(MAX(fabs(dx),fabs(dy)),MAX(fabs(f),fabs(g))) < 2.80e-8) + break; + + niter = niter + 1; + } + + /* Reverse x and y if axes flipped */ + if (wcs->coorflip) { + tx = x; + x = y; + y = tx; + } + } + } + + /* Scale and rotate using CD matrix */ + if (wcs->rotmat) { + *xpix = x * wcs->dc[0] + y * wcs->dc[1]; + *ypix = x * wcs->dc[2] + y * wcs->dc[3]; + } + + else { + + /* Correct for rotation */ + if (wcs->rot!=0.0) { + double cosr = cos (degrad (wcs->rot)); + double sinr = sin (degrad (wcs->rot)); + *xpix = x * cosr + y * sinr; + *ypix = y * cosr - x * sinr; + } + else { + *xpix = x; + *ypix = y; + } + + /* Scale using CDELT */ + if (wcs->xinc != 0.) + *xpix = *xpix / wcs->xinc; + if (wcs->yinc != 0.) + *ypix = *ypix / wcs->yinc; + } + + /* Convert to pixels */ + *xpix = *xpix + wcs->xrefpix; + *ypix = *ypix + wcs->yrefpix; + + return (0); +} + + +/* TNXCLOSE -- free up the distortion surface pointers */ + +void +tnxclose (wcs) + +struct WorldCoor *wcs; /* pointer to the WCS descriptor */ + +{ + if (wcs->lngcor != NULL) + wf_gsclose (wcs->lngcor); + if (wcs->latcor != NULL) + wf_gsclose (wcs->latcor); + return; +} + +/* copyright(c) 1986 association of universities for research in astronomy inc. + * wfgsurfit.x -- surface fitting package used by wcs function drivers. + * Translated to C from SPP by Jessica Mink, SAO, May 26, 1998 + * + * the following routines are used by the experimental function drivers tnx + * and zpx to decode polynomial fits stored in the image header in the form + * of a list of parameters and coefficients into surface descriptors in + * ra / dec or longitude latitude. the polynomial surfaces so encoded consist + * of corrections to function drivers tan and zpn. the package routines are + * modelled after the equivalent gsurfit routines and are consistent with them. + * the routines are: + * + * sf = wf_gsopen (wattstr) + * wf_gsclose (sf) + * + * z = wf_gseval (sf, x, y) + * ncoeff = wf_gscoeff (sf, coeff) + * zder = wf_gsder (sf, x, y, nxder, nyder) + * + * wf_gsopen is used to open a surface fit encoded in a wcs attribute, returning + * the sf surface fitting descriptor. wf_gsclose should be called later to free + * the descriptor. wf_gseval is called to evaluate the surface at a point. + */ + + +#define SZ_GSCOEFFBUF 20 + +/* define the structure elements for the wf_gsrestore task */ +#define TNX_SAVETYPE 0 +#define TNX_SAVEXORDER 1 +#define TNX_SAVEYORDER 2 +#define TNX_SAVEXTERMS 3 +#define TNX_SAVEXMIN 4 +#define TNX_SAVEXMAX 5 +#define TNX_SAVEYMIN 6 +#define TNX_SAVEYMAX 7 +#define TNX_SAVECOEFF 8 + + +/* wf_gsopen -- decode the longitude / latitude or ra / dec mwcs attribute + * and return a gsurfit compatible surface descriptor. + */ + +struct IRAFsurface * +wf_gsopen (astr) + +char *astr; /* the input mwcs attribute string */ + +{ + double dval; + char *estr; + int npar, szcoeff; + double *coeff; + struct IRAFsurface *gs; + struct IRAFsurface *wf_gsrestore(); + + if (astr[1] == 0) + return (NULL); + + gs = NULL; + npar = 0; + szcoeff = SZ_GSCOEFFBUF; + coeff = (double *) malloc (szcoeff * sizeof (double)); + + estr = astr; + while (*estr != (char) 0) { + dval = strtod (astr, &estr); + if (*estr == '.') + estr++; + if (*estr != (char) 0) { + npar++; + if (npar >= szcoeff) { + szcoeff = szcoeff + SZ_GSCOEFFBUF; + coeff = (double *) realloc (coeff, (szcoeff * sizeof (double))); + } + coeff[npar-1] = dval; + astr = estr; + while (*astr == ' ') astr++; + } + } + + gs = wf_gsrestore (coeff); + + free (coeff); + + if (npar == 0) + return (NULL); + else + return (gs); +} + + +/* wf_gsclose -- procedure to free the surface descriptor */ + +static void +wf_gsclose (sf) + +struct IRAFsurface *sf; /* the surface descriptor */ + +{ + if (sf != NULL) { + if (sf->xbasis != NULL) + free (sf->xbasis); + if (sf->ybasis != NULL) + free (sf->ybasis); + if (sf->coeff != NULL) + free (sf->coeff); + free (sf); + } + return; +} + + +/* wf_gseval -- procedure to evaluate the fitted surface at a single point. + * the wf->ncoeff coefficients are stored in the vector pointed to by sf->coeff. + */ + +double +wf_gseval (sf, x, y) + +struct IRAFsurface *sf; /* pointer to surface descriptor structure */ +double x; /* x value */ +double y; /* y value */ +{ + double sum, accum; + int i, ii, k, maxorder, xorder; + + /* Calculate the basis functions */ + switch (sf->type) { + case TNX_CHEBYSHEV: + wf_gsb1cheb (x, sf->xorder, sf->xmaxmin, sf->xrange, sf->xbasis); + wf_gsb1cheb (y, sf->yorder, sf->ymaxmin, sf->yrange, sf->ybasis); + break; + case TNX_LEGENDRE: + wf_gsb1leg (x, sf->xorder, sf->xmaxmin, sf->xrange, sf->xbasis); + wf_gsb1leg (y, sf->yorder, sf->ymaxmin, sf->yrange, sf->ybasis); + break; + case TNX_POLYNOMIAL: + wf_gsb1pol (x, sf->xorder, sf->xbasis); + wf_gsb1pol (y, sf->yorder, sf->ybasis); + break; + default: + fprintf (stderr,"TNX_GSEVAL: unknown surface type\n"); + return (0.0); + } + + /* Initialize accumulator basis functions */ + sum = 0.0; + + /* Loop over y basis functions */ + if (sf->xorder > sf->yorder) + maxorder = sf->xorder + 1; + else + maxorder = sf->yorder + 1; + xorder = sf->xorder; + ii = 0; + + for (i = 0; i < sf->yorder; i++) { + + /* Loop over the x basis functions */ + accum = 0.0; + for (k = 0; k < xorder; k++) { + accum = accum + sf->coeff[ii] * sf->xbasis[k]; + ii = ii + 1; + } + accum = accum * sf->ybasis[i]; + sum = sum + accum; + + /* Elements of the coefficient vector where neither k = 1 or i = 1 + are not calculated if sf->xterms = no. */ + if (sf->xterms == TNX_XNONE) + xorder = 1; + else if (sf->xterms == TNX_XHALF) { + if ((i + 1 + sf->xorder + 1) > maxorder) + xorder = xorder - 1; + } + } + + return (sum); +} + + +/* TNX_GSCOEFF -- procedure to fetch the number and magnitude of the coefficients + * if the sf->xterms = wf_xbi (yes) then the number of coefficients will be + * (sf->xorder * sf->yorder); if wf_xterms is wf_xtri then the number + * of coefficients will be (sf->xorder * sf->yorder - order * + * (order - 1) / 2) where order is the minimum of the x and yorders; if + * sf->xterms = TNX_XNONE then the number of coefficients will be + * (sf->xorder + sf->yorder - 1). + */ + +int +wf_gscoeff (sf, coeff) + +struct IRAFsurface *sf; /* pointer to the surface fitting descriptor */ +double *coeff; /* the coefficients of the fit */ + +{ + int ncoeff; /* the number of coefficients */ + int i; + + /* Exctract coefficients from data structure and calculate their number */ + ncoeff = sf->ncoeff; + for (i = 0; i < ncoeff; i++) + coeff[i] = sf->coeff[i]; + return (ncoeff); +} + + +static double *coeff = NULL; +static int nbcoeff = 0; + +/* wf_gsder -- procedure to calculate a new surface which is a derivative of + * the input surface. + */ + +double +wf_gsder (sf1, x, y, nxd, nyd) + +struct IRAFsurface *sf1; /* pointer to the previous surface */ +double x; /* x values */ +double y; /* y values */ +int nxd, nyd; /* order of the derivatives in x and y */ +{ + int nxder, nyder, i, j, k, nbytes; + int order, maxorder1, maxorder2, nmove1, nmove2; + struct IRAFsurface *sf2 = 0; + double *ptr1, *ptr2; + double zfit, norm; + double wf_gseval(); + + if (sf1 == NULL) + return (0.0); + + if (nxd < 0 || nyd < 0) { + fprintf (stderr, "TNX_GSDER: order of derivatives cannot be < 0\n"); + return (0.0); + } + + if (nxd == 0 && nyd == 0) { + zfit = wf_gseval (sf1, x, y); + return (zfit); + } + + /* Allocate space for new surface */ + sf2 = (struct IRAFsurface *) malloc (sizeof (struct IRAFsurface)); + + /* Check the order of the derivatives */ + nxder = MIN (nxd, sf1->xorder - 1); + nyder = MIN (nyd, sf1->yorder - 1); + + /* Set up new surface */ + sf2->type = sf1->type; + + /* Set the derivative surface parameters */ + if (sf2->type == TNX_LEGENDRE || + sf2->type == TNX_CHEBYSHEV || + sf2->type == TNX_POLYNOMIAL) { + + sf2->xterms = sf1->xterms; + + /* Find the order of the new surface */ + switch (sf2->xterms) { + case TNX_XNONE: + if (nxder > 0 && nyder > 0) { + sf2->xorder = 1; + sf2->yorder = 1; + sf2->ncoeff = 1; + } + else if (nxder > 0) { + sf2->xorder = MAX (1, sf1->xorder - nxder); + sf2->yorder = 1; + sf2->ncoeff = sf2->xorder; + } + else if (nyder > 0) { + sf2->xorder = 1; + sf2->yorder = MAX (1, sf1->yorder - nyder); + sf2->ncoeff = sf2->yorder; + } + break; + + case TNX_XHALF: + maxorder1 = MAX (sf1->xorder+1, sf1->yorder+1); + order = MAX(1, MIN(maxorder1-1-nyder-nxder,sf1->xorder-nxder)); + sf2->xorder = order; + order = MAX(1, MIN(maxorder1-1-nyder-nxder,sf1->yorder-nyder)); + sf2->yorder = order; + order = MIN (sf2->xorder, sf2->yorder); + sf2->ncoeff = sf2->xorder * sf2->yorder - (order*(order-1)/2); + break; + + default: + sf2->xorder = MAX (1, sf1->xorder - nxder); + sf2->yorder = MAX (1, sf1->yorder - nyder); + sf2->ncoeff = sf2->xorder * sf2->yorder; + } + + /* define the data limits */ + sf2->xrange = sf1->xrange; + sf2->xmaxmin = sf1->xmaxmin; + sf2->yrange = sf1->yrange; + sf2->ymaxmin = sf1->ymaxmin; + } + + else { + fprintf (stderr, "TNX_GSDER: unknown surface type %d\n", sf2->type); + return (0.0); + } + + /* Allocate space for coefficients and basis functions */ + nbytes = sf2->ncoeff * sizeof(double); + sf2->coeff = (double *) malloc (nbytes); + nbytes = sf2->xorder * sizeof(double); + sf2->xbasis = (double *) malloc (nbytes); + nbytes = sf2->yorder * sizeof(double); + sf2->ybasis = (double *) malloc (nbytes); + + /* Get coefficients */ + nbytes = sf1->ncoeff * sizeof(double); + if (nbytes > nbcoeff) { + if (nbcoeff > 0) + coeff = (double *) realloc (coeff, nbytes); + else + coeff = (double *) malloc (nbytes); + nbcoeff = nbytes; + } + (void) wf_gscoeff (sf1, coeff); + + /* Compute the new coefficients */ + switch (sf2->xterms) { + case TNX_XFULL: + ptr2 = sf2->coeff + (sf2->yorder - 1) * sf2->xorder; + ptr1 = coeff + (sf1->yorder - 1) * sf1->xorder; + for (i = sf1->yorder - 1; i >= nyder; i--) { + for (j = i; j >= i-nyder+1; j--) { + for (k = 0; k < sf2->xorder; k++) + ptr1[nxder+k] = ptr1[nxder+k] * (double)(j); + } + for (j = sf1->xorder; j >= nxder+1; j--) { + for (k = j; k >= j-nxder+1; k--) + ptr1[j-1] = ptr1[j-1] * (double)(k - 1); + } + for (j = 0; j < sf2->xorder; j++) + ptr2[j] = ptr1[nxder+j]; + ptr2 = ptr2 - sf2->xorder; + ptr1 = ptr1 - sf1->xorder; + } + break; + + case TNX_XHALF: + maxorder1 = MAX (sf1->xorder + 1, sf1->yorder + 1); + maxorder2 = MAX (sf2->xorder + 1, sf2->yorder + 1); + ptr2 = sf2->coeff + sf2->ncoeff; + ptr1 = coeff + sf1->ncoeff; + for (i = sf1->yorder; i >= nyder+1; i--) { + nmove1 = MAX (0, MIN (maxorder1 - i, sf1->xorder)); + nmove2 = MAX (0, MIN (maxorder2 - i + nyder, sf2->xorder)); + ptr1 = ptr1 - nmove1; + ptr2 = ptr2 - nmove2; + for (j = i; j > i - nyder + 1; j--) { + for (k = 0; k < nmove2; k++) + ptr1[nxder+k] = ptr1[nxder+k] * (double)(j-1); + } + for (j = nmove1; j >= nxder+1; j--) { + for (k = j; k >= j-nxder+1; k--) + ptr1[j-1] = ptr1[j-1] * (double)(k - 1); + } + for (j = 0; j < nmove2; j++) + ptr2[j] = ptr1[nxder+j]; + } + break; + + default: + if (nxder > 0 && nyder > 0) + sf2->coeff[0] = 0.0; + + else if (nxder > 0) { + ptr1 = coeff; + ptr2 = sf2->coeff + sf2->ncoeff - 1; + for (j = sf1->xorder; j >= nxder+1; j--) { + for (k = j; k >= j - nxder + 1; k--) + ptr1[j-1] = ptr1[j-1] * (double)(k - 1); + ptr2[0] = ptr1[j-1]; + ptr2 = ptr2 - 1; + } + } + + else if (nyder > 0) { + ptr1 = coeff + sf1->ncoeff - 1; + ptr2 = sf2->coeff; + for (i = sf1->yorder; i >= nyder + 1; i--) { + for (j = i; j >= i - nyder + 1; j--) + *ptr1 = *ptr1 * (double)(j - 1); + ptr1 = ptr1 - 1; + } + for (i = 0; i < sf2->ncoeff; i++) + ptr2[i] = ptr1[i+1]; + } + } + + /* evaluate the derivatives */ + zfit = wf_gseval (sf2, x, y); + + /* normalize */ + if (sf2->type != TNX_POLYNOMIAL) { + norm = pow (sf2->xrange, (double)nxder) * + pow (sf2->yrange, (double)nyder); + zfit = norm * zfit; + } + + /* free the space */ + wf_gsclose (sf2); + + return (zfit); +} + + +/* wf_gsrestore -- procedure to restore the surface fit encoded in the + image header as a list of double precision parameters and coefficients + to the surface descriptor for use by the evaluating routines. the + surface parameters, surface type, xorder (or number of polynomial + terms in x), yorder (or number of polynomial terms in y), xterms, + xmin, xmax and ymin and ymax, are stored in the first eight elements + of the double array fit, followed by the wf->ncoeff surface coefficients. + */ + +struct IRAFsurface * +wf_gsrestore (fit) + +double *fit; /* array containing the surface parameters + and coefficients */ +{ + struct IRAFsurface *sf; /* surface descriptor */ + int surface_type, xorder, yorder, order, i; + double xmin, xmax, ymin, ymax; + + xorder = (int) (fit[TNX_SAVEXORDER] + 0.5); + if (xorder < 1) { + fprintf (stderr, "wf_gsrestore: illegal x order %d\n", xorder); + return (NULL); + } + + yorder = (int) (fit[TNX_SAVEYORDER] + 0.5); + if (yorder < 1) { + fprintf (stderr, "wf_gsrestore: illegal y order %d\n", yorder); + return (NULL); + } + + xmin = fit[TNX_SAVEXMIN]; + xmax = fit[TNX_SAVEXMAX]; + if (xmax <= xmin) { + fprintf (stderr, "wf_gsrestore: illegal x range %f-%f\n",xmin,xmax); + return (NULL); + } + ymin = fit[TNX_SAVEYMIN]; + ymax = fit[TNX_SAVEYMAX]; + if (ymax <= ymin) { + fprintf (stderr, "wf_gsrestore: illegal y range %f-%f\n",ymin,ymax); + return (NULL); + } + + /* Set surface type dependent surface descriptor parameters */ + surface_type = (int) (fit[TNX_SAVETYPE] + 0.5); + + if (surface_type == TNX_LEGENDRE || + surface_type == TNX_CHEBYSHEV || + surface_type == TNX_POLYNOMIAL) { + + /* allocate space for the surface descriptor */ + sf = (struct IRAFsurface *) malloc (sizeof (struct IRAFsurface)); + sf->xorder = xorder; + sf->xrange = 2.0 / (xmax - xmin); + sf->xmaxmin = - (xmax + xmin) / 2.0; + sf->yorder = yorder; + sf->yrange = 2.0 / (ymax - ymin); + sf->ymaxmin = - (ymax + ymin) / 2.0; + sf->xterms = fit[TNX_SAVEXTERMS]; + switch (sf->xterms) { + case TNX_XNONE: + sf->ncoeff = sf->xorder + sf->yorder - 1; + break; + case TNX_XHALF: + order = MIN (xorder, yorder); + sf->ncoeff = sf->xorder * sf->yorder - order * (order-1) / 2; + break; + case TNX_XFULL: + sf->ncoeff = sf->xorder * sf->yorder; + break; + } + } + else { + fprintf (stderr, "wf_gsrestore: unknown surface type %d\n", surface_type); + return (NULL); + } + + /* Set remaining curve parameters */ + sf->type = surface_type; + + /* Restore coefficient array */ + sf->coeff = (double *) malloc (sf->ncoeff*sizeof (double)); + for (i = 0; i < sf->ncoeff; i++) + sf->coeff[i] = fit[TNX_SAVECOEFF+i]; + + /* Allocate space for basis vectors */ + sf->xbasis = (double *) malloc (sf->xorder*sizeof (double)); + sf->ybasis = (double *) malloc (sf->yorder*sizeof (double)); + + return (sf); +} + + +/* wf_gsb1pol -- procedure to evaluate all the non-zero polynomial functions + for a single point and given order. */ + +static void +wf_gsb1pol (x, order, basis) + +double x; /*i data point */ +int order; /*i order of polynomial, order = 1, constant */ +double *basis; /*o basis functions */ +{ + int i; + + basis[0] = 1.0; + if (order == 1) + return; + + basis[1] = x; + if (order == 2) + return; + + for (i = 2; i < order; i++) + basis[i] = x * basis[i-1]; + + return; +} + + +/* wf_gsb1leg -- procedure to evaluate all the non-zero legendre functions for + a single point and given order. */ + +static void +wf_gsb1leg (x, order, k1, k2, basis) + +double x; /*i data point */ +int order; /*i order of polynomial, order = 1, constant */ +double k1, k2; /*i normalizing constants */ +double *basis; /*o basis functions */ +{ + int i; + double ri, xnorm; + + basis[0] = 1.0; + if (order == 1) + return; + + xnorm = (x + k1) * k2 ; + basis[1] = xnorm; + if (order == 2) + return; + + for (i = 2; i < order; i++) { + ri = i; + basis[i] = ((2.0 * ri - 1.0) * xnorm * basis[i-1] - + (ri - 1.0) * basis[i-2]) / ri; + } + + return; +} + + +/* wf_gsb1cheb -- procedure to evaluate all the non-zero chebyshev function + coefficients for a given x and order. */ + +static void +wf_gsb1cheb (x, order, k1, k2, basis) + +double x; /*i number of data points */ +int order; /*i order of polynomial, 1 is a constant */ +double k1, k2; /*i normalizing constants */ +double *basis; /*o array of basis functions */ +{ + int i; + double xnorm; + + basis[0] = 1.0; + if (order == 1) + return; + + xnorm = (x + k1) * k2; + basis[1] = xnorm; + if (order == 2) + return; + + for (i = 2; i < order; i++) + basis[i] = 2. * xnorm * basis[i-1] - basis[i-2]; + + return; +} + +/* Set surface polynomial from arguments */ + +int +tnxpset (wcs, xorder, yorder, xterms, coeff) + +struct WorldCoor *wcs; /* World coordinate system structure */ +int xorder; /* Number of x coefficients (same for x and y) */ +int yorder; /* Number of y coefficients (same for x and y) */ +int xterms; /* Number of xy coefficients (same for x and y) */ +double *coeff; /* Plate fit coefficients */ + +{ + double *ycoeff; + struct IRAFsurface *wf_gspset (); + + wcs->prjcode = WCS_TNX; + + wcs->lngcor = wf_gspset (xorder, yorder, xterms, coeff); + ycoeff = coeff + wcs->lngcor->ncoeff; + wcs->latcor = wf_gspset (xorder, yorder, xterms, ycoeff); + + return 0; +} + + +/* wf_gspset -- procedure to set the surface descriptor for use by the + evaluating routines. from arguments. The surface parameters are + surface type, xorder (number of polynomial terms in x), yorder (number + of polynomial terms in y), xterms, and the surface coefficients. + */ + +struct IRAFsurface * +wf_gspset (xorder, yorder, xterms, coeff) + +int xorder; +int yorder; +int xterms; +double *coeff; +{ + struct IRAFsurface *sf; /* surface descriptor */ + int surface_type, order, i; + double xmin, xmax; + double ymin, ymax; + + surface_type = TNX_POLYNOMIAL; + xmin = 0.0; + xmax = 0.0; + ymin = 0.0; + ymax = 0.0; + + if (surface_type == TNX_LEGENDRE || + surface_type == TNX_CHEBYSHEV || + surface_type == TNX_POLYNOMIAL) { + + /* allocate space for the surface descriptor */ + sf = (struct IRAFsurface *) malloc (sizeof (struct IRAFsurface)); + sf->xorder = xorder; + sf->xrange = 2.0 / (xmax - xmin); + sf->xmaxmin = -(xmax + xmin) / 2.0; + sf->yorder = yorder; + sf->yrange = 2.0 / (ymax - ymin); + sf->ymaxmin = - (ymax + ymin) / 2.0; + sf->xterms = xterms; + switch (sf->xterms) { + case TNX_XNONE: + sf->ncoeff = sf->xorder + sf->yorder - 1; + break; + case TNX_XHALF: + order = MIN (xorder, yorder); + sf->ncoeff = sf->xorder * sf->yorder - order * (order-1) / 2; + break; + case TNX_XFULL: + sf->ncoeff = sf->xorder * sf->yorder; + break; + } + } + else { + fprintf (stderr, "TNX_GSSET: unknown surface type %d\n", surface_type); + return (NULL); + } + + /* Set remaining curve parameters */ + sf->type = surface_type; + + /* Restore coefficient array */ + sf->coeff = (double *) malloc (sf->ncoeff*sizeof (double)); + for (i = 0; i < sf->ncoeff; i++) + sf->coeff[i] = coeff[i]; + + /* Allocate space for basis vectors */ + sf->xbasis = (double *) malloc (sf->xorder*sizeof (double)); + sf->ybasis = (double *) malloc (sf->yorder*sizeof (double)); + + return (sf); +} + +/* Mar 26 1998 New subroutines, translated from SPP + * Apr 28 1998 Change all local flags to TNX_* and projection flag to WCS_TNX + * May 11 1998 Fix use of pole longitude default + * Sep 4 1998 Fix missed assignment in tnxpos from Allen Harris, SAO + * Sep 10 1998 Fix bugs in tnxpix() + * Sep 10 1998 Fix missed assignment in tnxpix from Allen Harris, SAO + * + * Oct 22 1999 Drop unused variables, fix case statements after lint + * Dec 10 1999 Fix bug in gsder() which failed to allocate enough memory + * Dec 10 1999 Compute wcs->rot using wcsrotset() in tnxinit() + * + * Feb 14 2001 Fixed off-by-one bug in legendre evaluation (Mike Jarvis) + * + * Apr 11 2002 Fix bug when .-terminated substring in wf_gsopen() + * Apr 29 2002 Clean up code + * Jun 26 2002 Increase size of WAT strings from 500 to 2000 + * + * Jun 27 2005 Drop unused arguments k1 and k2 from wf_gsb1pol() + * + * Jan 8 2007 Drop unused variable ncoeff in wf_gsder() + * Jan 9 2007 Declare header const char in tnxinit() + * Apr 3 2007 Fix offsets to hit last cooefficient in wf_gsder() + * + * Sep 5 2008 Fix wf_gseval() call in tnxpos() so unmodified x and y are used + * Sep 9 2008 Fix loop in TNX_XFULL section of wf_gsder() + * (last two bugs found by Ed Los) + * Sep 17 2008 Fix tnxpos for null correction case (fix by Ed Los) + */ |