diff options
Diffstat (limited to 'sys/mwcs/wfmspec.x')
-rw-r--r-- | sys/mwcs/wfmspec.x | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/sys/mwcs/wfmspec.x b/sys/mwcs/wfmspec.x new file mode 100644 index 00000000..2f5b5a91 --- /dev/null +++ b/sys/mwcs/wfmspec.x @@ -0,0 +1,578 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <imhdr.h> +include "mwcs.h" + +.help WFMSPEC +.nf ------------------------------------------------------------------------- +WFMSPEC -- WCS function driver for MULTISPEC spectral format. + +The dispersion coordinate is along image lines and each line has independent +linear or nonlinear dispersion coordinates. The dispersion coordinates are +defined by specn attributes where n is the physical line number. The format +of the attributes is: + + ap beam dtype w1 dw nw z aplow aphigh coeffs... + +where ap is the aperture number (unique within an image), beam is a beam +number (not used by the driver), dtype is the dispersion type with values + + 0 = linear dispersion + 1 = log linear dispersion + 2 = nonlinear dispersion + +w1 is the wavelength of the first physical pixel, dw is the average +increment per pixel between the first and last pixel, nw is the number of +pixels, z is a redshift factor to be applied to the dispersion coordinates, +aplow and aphigh are aperture limits defining the origin of the spectra (not +used by the driver), and coeffs are the nonlinear dispersion coefficients. + +The nonlinear dispersion function coefficients may describe several function +types; chebyshev polynomial, legendre polynomial, linear spline, cubic +spline, linear interpolation in a pixel coordinate array, and linear +interpolation in a sampled array. + +The axes and dispersion parameters are in terms of the physical image. The +aperture number is used for the world coordinate of the line coordinate. +Coordinates outside the valid range are mapped to nearest valid world +coordinate. In application this would give a correct world coordinate graph +for a general WCS blind graphics task (especially if all invalid pixels have +the same value as the last valid pixel). + +Driver routines: + + FN_INIT wf_msp_init (fc, dir) + FN_DESTROY wf_msp_destroy (fc) + FN_FWD wf_msp_fwd (fc, v1, v2) + FN_INV wf_msp_inv (fc, v1, v2) + +In addition the nonlinear dispersion functions use the following routines: + + wf_msp_coeff Convert the attribute string to a coefficient array + wf_msp_eval Evaluate the function (P->W) + wf_msp_evali Evaluate the inverse function (W->P) + +.endhelp -------------------------------------------------------------------- + +# Driver specific fields of function call (FC) descriptor. +define FC_NAPS Memi[$1+FCU] # number of apertures +define FC_APS Memi[$1+FCU+1] # pointer to indep coords +define FC_DTYPE Memi[$1+FCU+2] # pointer to dispersion type +define FC_CRVAL Memi[$1+FCU+3] # pointer to linear origins +define FC_CDELT Memi[$1+FCU+4] # pointer to linear intervals +define FC_NPTS Memi[$1+FCU+5] # pointer to number of points +define FC_Z Memi[$1+FCU+6] # pointer to doppler corrections +define FC_COEFF Memi[$1+FCU+7] # pointer to nonlinear coeffs +define FC_X Memi[$1+FCU+8] # pointer to last phys. coord. +define FC_DYDX Memi[$1+FCU+9] # pointer to last deriv. +define FC_DIR Memi[$1+FCU+10] # direction of transform + +# Function types. +define CHEBYSHEV 1 # CURFIT Chebyshev polynomial +define LEGENDRE 2 # CURFIT Legendre polynomial +define SPLINE3 3 # CURFIT cubic spline +define SPLINE1 4 # CURFIT linear spline +define PIXEL 5 # pixel coordinate array +define SAMPLE 6 # sampled coordinates + +# Dispersion types. +define LINEAR 0 # linear +define LOG 1 # log linear +define NONLINEAR 2 # nonlinear + +# Iterative inversion parameters. +define NALLOC 10 # size of allocation increments +define NIT 10 # max interations in determining inverse +define DX 0.0001 # accuracy limit in pixels for inverse + +# Size limiting definitions. +define DEF_SZATVAL 2048 # dynamically resized if overflow + + +# WF_MSP_INIT -- Initialize the function call descriptor for the indicated +# type of transform (forward or inverse). + +procedure wf_msp_init (fc, dir) + +pointer fc #I pointer to FC descriptor +int dir #I type of transformation + +pointer ct, mw +int sz_atval, naps, ip, i +pointer sp, atkey, atval, aps, dtype, crval, cdelt, npts, z, coeff +int strlen(), ctoi(), ctod() +double x, dval, wf_msp_eval() +errchk malloc, realloc + +begin + # Get pointers. + ct = FC_CT(fc) + mw = CT_MW(ct) + + # Check axes. + if (FC_NAXES(fc) != 2 || CT_AXIS(ct,1) != 1 || CT_AXIS(ct,2) != 2) + call error (1, "WFMSPEC: Wrong axes") + + # Get spectrum information. + call smark (sp) + sz_atval = DEF_SZATVAL + call malloc (atval, sz_atval, TY_CHAR) + call salloc (atkey, SZ_ATNAME, TY_CHAR) + + for (naps=0; ; naps=naps+1) { + call sprintf (Memc[atkey], SZ_ATNAME, "spec%d") + call pargi (naps+1) + iferr (call mw_gwattrs (mw, 2, Memc[atkey], Memc[atval], sz_atval)) + break + + while (strlen (Memc[atval]) == sz_atval) { + sz_atval = 2 * sz_atval + call realloc (atval, sz_atval, TY_CHAR) + call mw_gwattrs (mw, 2, Memc[atkey], Memc[atval], sz_atval) + } + + if (naps == 0) { + call malloc (aps, NALLOC, TY_INT) + call malloc (dtype, NALLOC, TY_INT) + call malloc (crval, NALLOC, TY_DOUBLE) + call malloc (cdelt, NALLOC, TY_DOUBLE) + call malloc (npts, NALLOC, TY_INT) + call malloc (z, NALLOC, TY_DOUBLE) + call malloc (coeff, NALLOC, TY_POINTER) + } else if (mod (naps, NALLOC) == 0) { + call realloc (aps, naps+NALLOC, TY_INT) + call realloc (dtype, naps+NALLOC, TY_INT) + call realloc (crval, naps+NALLOC, TY_DOUBLE) + call realloc (cdelt, naps+NALLOC, TY_DOUBLE) + call realloc (npts, naps+NALLOC, TY_INT) + call realloc (z, naps+NALLOC, TY_DOUBLE) + call realloc (coeff, naps+NALLOC, TY_POINTER) + } + + # Linear dispersion function. + ip = 1 + if (ctoi (Memc[atval], ip, Memi[aps+naps]) <= 0) + next + if (ctoi (Memc[atval], ip, Memi[dtype+naps]) <= 0) + next + if (ctoi (Memc[atval], ip, Memi[dtype+naps]) <= 0) + next + if (ctod (Memc[atval], ip, Memd[crval+naps]) <= 0) + next + if (ctod (Memc[atval], ip, Memd[cdelt+naps]) <= 0) + next + if (ctoi (Memc[atval], ip, Memi[npts+naps]) <= 0) + next + if (ctod (Memc[atval], ip, Memd[z+naps]) <= 0) + next + if (ctod (Memc[atval], ip, dval) <= 0) + next + if (ctod (Memc[atval], ip, dval) <= 0) + next + Memd[z+naps] = Memd[z+naps] + 1 + + # Set nonlinear dispersion function. + if (Memi[dtype+naps] == NONLINEAR) + call wf_msp_coeff (Memc[atval+ip], Memi[coeff+naps], + double (0.5), double (Memi[npts+naps]+0.5)) + } + + if (naps <= 0) + call error (2, "WFMSPEC: No aperture information") + + call realloc (aps, naps, TY_INT) + call realloc (dtype, naps, TY_INT) + call realloc (crval, naps, TY_DOUBLE) + call realloc (cdelt, naps, TY_DOUBLE) + call realloc (npts, naps, TY_INT) + call realloc (z, naps, TY_DOUBLE) + call realloc (coeff, naps, TY_POINTER) + + FC_NAPS(fc) = naps + FC_APS(fc) = aps + FC_DTYPE(fc) = dtype + FC_CRVAL(fc) = crval + FC_CDELT(fc) = cdelt + FC_NPTS(fc) = npts + FC_Z(fc) = z + FC_COEFF(fc) = coeff + FC_DIR(fc) = dir + + # Setup inverse parameters if needed. + # The parameters make the interative inversion more efficient + # when the inverse transformation is evaluated sequentially. + + if (dir == INVERSE) { + call malloc (crval, naps, TY_DOUBLE) + call malloc (cdelt, naps, TY_DOUBLE) + do i = 0, naps-1 { + if (Memi[FC_NPTS(fc)+i] == 0) + next + if (Memi[FC_DTYPE(fc)+i] == NONLINEAR) { + coeff = Memi[FC_COEFF(fc)+i] + x = Memi[FC_NPTS(fc)+i] + Memd[crval+i] = x + Memd[cdelt+i] = wf_msp_eval (Memd[coeff], x) - + wf_msp_eval (Memd[coeff], x - 1) + } + } + FC_X(fc) = crval + FC_DYDX(fc) = cdelt + } else { + FC_X(fc) = NULL + FC_DYDX(fc) = NULL + } + + call mfree (atval, TY_CHAR) + call sfree (sp) +end + + +# WF_MSP_DESTROY -- Free function driver descriptor. + +procedure wf_msp_destroy (fc) + +pointer fc #I pointer to FC descriptor +int i + +begin + do i = 1, FC_NAPS(fc) + if (Memi[FC_DTYPE(fc)+i-1] == NONLINEAR) + call mfree (Memi[FC_COEFF(fc)+i-1], TY_DOUBLE) + + call mfree (FC_APS(fc), TY_INT) + call mfree (FC_DTYPE(fc), TY_INT) + call mfree (FC_CRVAL(fc), TY_DOUBLE) + call mfree (FC_CDELT(fc), TY_DOUBLE) + call mfree (FC_NPTS(fc), TY_INT) + call mfree (FC_Z(fc), TY_DOUBLE) + call mfree (FC_COEFF(fc), TY_POINTER) + call mfree (FC_X(fc), TY_DOUBLE) + call mfree (FC_DYDX(fc), TY_DOUBLE) +end + + +# WF_MSP_FWD -- Evaluate P -> W (physical to world transformation). + +procedure wf_msp_fwd (fc, in, out) + +pointer fc #I pointer to FC descriptor +double in[2] #I point to sample WCS at +double out[2] #O value of WCS at that point + +int i +pointer coeff +double din, wf_msp_eval() + +begin + i = nint (in[2]) - 1 + if (i < 0 || i >= FC_NAPS(fc)) + call error (3, "WFMSPEC: Coordinate out of bounds") + if (Memi[FC_NPTS(fc)+i] == 0) + call error (4, "WFMSPEC: No dispersion function") + + if (Memi[FC_DTYPE(fc)+i] == NONLINEAR) { + coeff = Memi[FC_COEFF(fc)+i] + out[2] = Memi[FC_APS(fc)+i] + out[1] = wf_msp_eval (Memd[coeff], in[1]) + } else { + din = max (0.5D0, min (double (Memi[FC_NPTS(fc)+i]+0.5), in[1])) + out[2] = Memi[FC_APS(fc)+i] + out[1] = Memd[FC_CRVAL(fc)+i] + Memd[FC_CDELT(fc)+i] * (din - 1) + if (Memi[FC_DTYPE(fc)+i] == LOG) + out[1] = 10. ** out[1] + } + + out[1] = out[1] / Memd[FC_Z(fc)+i] +end + + +# WF_MSP_INV -- Evaluate W -> P (world to physical transformation). + +procedure wf_msp_inv (fc, in, out) + +pointer fc #I pointer to FC descriptor +double in[2] #I point to sample WCS at +double out[2] #O value of WCS at that point + +int i +pointer coeff +double din, dinmin +double wf_msp_evali() + +begin + out[2] = 1 + dinmin = abs (in[2] - Memi[FC_APS(fc)]) + do i = 1, FC_NAPS(fc)-1 { + din = abs (in[2] - Memi[FC_APS(fc)+i]) + if (din < dinmin) { + out[2] = i + 1 + dinmin = din + } + } + + i = nint (out[2]) - 1 + if (i < 0 || i >= FC_NAPS(fc)) + call error (5, "WFMSPEC: Coordinate out of bounds") + if (Memi[FC_NPTS(fc)+i] == 0) + call error (6, "WFMSPEC: No dispersion function") + + din = in[1] * Memd[FC_Z(fc)+i] + if (Memi[FC_DTYPE(fc)+i] == NONLINEAR) { + coeff = Memi[FC_COEFF(fc)+i] + out[1] = wf_msp_evali (Memd[coeff], din, Memd[FC_X(fc)+i], + Memd[FC_DYDX(fc)+i]) + } else { + if (Memi[FC_DTYPE(fc)+i] == LOG) + din = log10 (din) + out[1] = (din-Memd[FC_CRVAL(fc)+i]) / Memd[FC_CDELT(fc)+i] + 1 + out[1] = max (0.5D0, min (double(Memi[FC_NPTS(fc)+i]+0.5), out[1])) + } +end + + +# WF_MSP_COEFF -- Initialize nonlinear coefficient array. + +procedure wf_msp_coeff (atval, coeff, xmin, xmax) + +char atval[ARB] #I attribute string +pointer coeff #O coefficient array +double xmin, xmax #I x limits + +double dval, temp +int ncoeff, type, order, ip, i +errchk malloc, realloc +double wf_msp_eval() +int ctod() + +begin + coeff = NULL + ncoeff = 5 + + ip = 1 + while (ctod (atval, ip, dval) > 0) { + if (coeff == NULL) + call malloc (coeff, NALLOC, TY_DOUBLE) + else if (mod (ncoeff, NALLOC) == 0) + call realloc (coeff, ncoeff+NALLOC, TY_DOUBLE) + Memd[coeff+ncoeff] = dval + ncoeff = ncoeff + 1 + } + if (coeff == NULL) + return + + # Convert range elements to a more efficient form. + call realloc (coeff, ncoeff, TY_DOUBLE) + Memd[coeff] = ncoeff + i = 6 + while (i < ncoeff) { + type = nint (Memd[coeff+i+1]) + order = nint (Memd[coeff+i+2]) + switch (type) { + case CHEBYSHEV, LEGENDRE: + dval = 2 / (Memd[coeff+i+4] - Memd[coeff+i+3]) + Memd[coeff+i+3] = (Memd[coeff+i+4] + Memd[coeff+i+3]) / 2 + Memd[coeff+i+4] = dval + i = i + 6 + order + case SPLINE3: + Memd[coeff+i+4] = nint (Memd[coeff+i+2]) / + (Memd[coeff+i+4] - Memd[coeff+i+3]) + i = i + 9 + order + case SPLINE1: + Memd[coeff+i+4] = nint (Memd[coeff+i+2]) / + (Memd[coeff+i+4] - Memd[coeff+i+3]) + i = i + 7 + order + case PIXEL: + i = i + 4 + order + case SAMPLE: + Memd[coeff+i+3] = i + 5 + i = i + 5 + order + } + } + + # Set function limits. + Memd[coeff+1] = xmin + Memd[coeff+2] = xmax + dval = wf_msp_eval (Memd[coeff], xmin) + temp = wf_msp_eval (Memd[coeff], xmax) + Memd[coeff+3] = min (dval, temp) + Memd[coeff+4] = max (dval, temp) +end + + +# WF_MSP_EVAL -- Evaluate nonlinear function. + +double procedure wf_msp_eval (coeff, xin) + +double coeff[ARB] #I coefficients +double xin #I physical coordinate for evaluation + +int i, j, k, ncoeff, type, order +double xval, x, y, w, ysum, wsum, a, b, c + +begin + ncoeff = nint (coeff[1]) + xval = max (coeff[2], min (coeff[3], xin)) + ysum = 0. + wsum = 0. + j = 6 + while (j < ncoeff) { + type = nint (coeff[j+2]) + order = nint (coeff[j+3]) + y = coeff[j+1] + w = coeff[j] + switch (type) { + case CHEBYSHEV: + x = (xval - coeff[j+4]) * coeff[j+5] + y = y + coeff[j+6] + if (order > 1) + y = y + coeff[j+7] * x + if (order > 2) { + k = j + 8 + a = 1 + b = x + do i = 3, order { + c = 2 * x * b - a + y = y + coeff[k] * c + a = b + b = c + k = k + 1 + } + } + j = j + 6 + order + case LEGENDRE: + x = (xval - coeff[j+4]) * coeff[j+5] + y = y + coeff[j+6] + if (order > 1) + y = y + coeff[j+7] * x + if (order > 2) { + k = j + 8 + a = 1 + b = x + do i = 3, order { + c = ((2 * i - 3) * x * b - (i - 2) * a) / (i - 1) + y = y + coeff[k] * c + a = b + b = c + k = k + 1 + } + } + j = j + 6 + order + case SPLINE3: + x = (xval - coeff[j+4]) * coeff[j+5] + i = max (0, min (int (x), order-1)) + k = j + 6 + i + b = x - i + a = 1 - b + c = a * a * a + y = y + c * coeff[k] + c = 1 + 3 * a * (1 + a * b) + y = y + c * coeff[k+1] + c = 1 + 3 * b * (1 + a * b) + y = y + c * coeff[k+2] + c = b * b * b + y = y + c * coeff[k+3] + j = j + 9 + order + case SPLINE1: + x = (xval - coeff[j+4]) * coeff[j+5] + i = max (0, min (int (x), order-1)) + k = j + 6 + i + b = x - i + a = 1 - b + y = y + a * coeff[k] + b * coeff[k+1] + j = j + 7 + order + case PIXEL: + i = max (1, min (int (xval), order-1)) + x = xval - i + y = y + (1 - x) * coeff[j+3+i] + x * coeff[j+4+i] + j = j + 4 + order + case SAMPLE: + i = nint (coeff[j+4]) + for (k=j+2+order; i < k && xval > coeff[i+2]; i=i+2) + ; + for (k=j+5; i > k && xval < coeff[i-2]; i=i-2) + ; + coeff[j+4] = i + x = (xval - coeff[i]) / (coeff[i+2] - coeff[i]) + y = y + (1 - x) * coeff[i+1] + x * coeff[i+3] + j = j + 5 + order + } + ysum = ysum + w * y + wsum = wsum + w + } + ysum = ysum / wsum + + return (ysum) +end + + +# WF_MSP_EVALI -- Evaluate inverse of nonlinear function. + +double procedure wf_msp_evali (coeff, y, x, dydx) + +double coeff[ARB] #I function coefficients +double y #I world coord to invert +double x #U last physical coordinate +double dydx #U last coordinate derivative + +int i +double xval, yval, y1, dx, dy +double wf_msp_eval() +bool fp_equald() + +begin + yval = max (coeff[4], min (coeff[5], y)) + + dx = 0. + dy = 0. + do i = 1, NIT { + y1 = wf_msp_eval (coeff, x) + if (dx > 1.) { + if (x + 1 < coeff[3]) + dy = wf_msp_eval (coeff, x+1.) - y1 + else + dy = y1 - wf_msp_eval (coeff, x-1.) + } else if (dx < -1.) { + if (x - 1 > coeff[2]) + dy = y1 - wf_msp_eval (coeff, x-1.) + else + dy = wf_msp_eval (coeff, x+1.) - y1 + } + if (!fp_equald (dy, 0.0D0)) + dydx = dy + dx = (yval - y1) / dydx + x = x + dx + x = max (coeff[2], min (coeff[3], x)) + if (abs (dx) < DX) + break + } + + if (i > NIT) { + xval = (coeff[2] + coeff[3]) / 2. + yval = abs (wf_msp_eval (coeff, xval) - y) + dx = (coeff[3] - coeff[2]) / 18. + while (dx > DX) { + for (x=max (coeff[2],xval-9*dx); x<=min (coeff[3],xval+9*dx); + x=x+dx) { + dy = abs (wf_msp_eval (coeff, x) - y) + if (dy < yval) { + xval = x + yval = dy + } + } + dx = dx / 10. + } + x = xval + if (x + 1 < coeff[3]) + dy = wf_msp_eval (coeff, x+1.) - wf_msp_eval (coeff, x) + else + dy = wf_msp_eval (coeff, x) - wf_msp_eval (coeff, x-1.) + if (!fp_equald (dy, 0.0D0)) + dydx = dy + } + + yval = int (x) + x = yval + nint ((x-yval) / DX) * DX + + return (x) +end |