/*============================================================================ WCSLIB 4.3 - an implementation of the FITS WCS standard. Copyright (C) 1995-2007, Mark Calabretta This file is part of WCSLIB. WCSLIB 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 3 of the License, or (at your option) any later version. WCSLIB 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 WCSLIB. If not, see . Correspondence concerning WCSLIB may be directed to: Internet email: mcalabre@atnf.csiro.au Postal address: Dr. Mark Calabretta Australia Telescope National Facility, CSIRO PO Box 76 Epping NSW 1710 AUSTRALIA Author: Mark Calabretta, Australia Telescope National Facility http://www.atnf.csiro.au/~mcalabre/index.html $Id: spc.c,v 4.3 2007/12/27 05:41:36 cal103 Exp $ *===========================================================================*/ #include #include #include #include "wcsmath.h" #include "wcstrig.h" #include "spc.h" #include "spx.h" /* Spectral algorithm codes. */ #define F2S 100; /* Axis linear in frequency. */ #define W2S 200; /* Axis linear in vacuum wavelengths. */ #define A2S 300; /* Axis linear in air wavelengths. */ #define V2S 400; /* Axis linear in velocity. */ #define GRI 500; /* Grism in vacuum. */ #define GRA 600; /* Grism in air. */ /* S-type spectral variables. */ #define FREQ 0; /* Frequency-like. */ #define AFRQ 1; /* Frequency-like. */ #define ENER 2; /* Frequency-like. */ #define WAVN 3; /* Frequency-like. */ #define VRAD 4; /* Frequency-like. */ #define WAVE 10; /* Vacuum wavelength-like. */ #define VOPT 11; /* Vacuum wavelength-like. */ #define ZOPT 12; /* Vacuum wavelength-like. */ #define AWAV 20; /* Air wavelength-like. */ #define VELO 30; /* Velocity-like. */ #define BETA 31; /* Velocity-like. */ /* Map status return value to message. */ const char *spc_errmsg[] = { "Success", "Null spcprm pointer passed", "Invalid spectral parameters", "One or more of x coordinates were invalid", "One or more of the spec coordinates were invalid"}; #define C 2.99792458e8 /*--------------------------------------------------------------------------*/ int spcini(struct spcprm *spc) { register int k; if (spc == 0x0) return 1; spc->flag = 0; strcpy(spc->type, " "); strcpy(spc->code, " "); spc->crval = UNDEFINED; spc->restfrq = 0.0; spc->restwav = 0.0; for (k = 0; k < 7; k++) { spc->pv[k] = UNDEFINED; } for (k = 0; k < 6; k++) { spc->w[k] = 0.0; } spc->isGrism = 0; spc->spxX2P = 0x0; spc->spxP2S = 0x0; spc->spxS2P = 0x0; spc->spxP2X = 0x0; return 0; } /*--------------------------------------------------------------------------*/ int spcprt(const struct spcprm *spc) { int i; if (spc == 0x0) return 1; printf(" flag: %d\n", spc->flag); printf(" type: \"%s\"\n", spc->type); printf(" code: \"%s\"\n", spc->code); if (undefined(spc->crval)) { printf(" crval: UNDEFINED\n"); } else { printf(" crval: %- 11.4g\n", spc->crval); } printf(" restfrq: %f\n", spc->restfrq); printf(" restwav: %f\n", spc->restwav); printf(" pv:"); if (spc->isGrism) { for (i = 0; i < 5; i++) { if (undefined(spc->pv[i])) { printf(" UNDEFINED "); } else { printf(" %- 11.4g", spc->pv[i]); } } printf("\n "); for (i = 5; i < 7; i++) { if (undefined(spc->pv[i])) { printf(" UNDEFINED "); } else { printf(" %- 11.4g", spc->pv[i]); } } printf("\n"); } else { printf(" (not used)\n"); } printf(" w:"); for (i = 0; i < 3; i++) { printf(" %- 11.4g", spc->w[i]); } if (spc->isGrism) { printf("\n "); for (i = 3; i < 6; i++) { printf(" %- 11.4g", spc->w[i]); } printf("\n"); } else { printf(" (remainder unused)\n"); } printf(" isGrism: %d\n", spc->isGrism); printf(" spxX2P: %p\n", (void *)spc->spxX2P); printf(" spxP2S: %p\n", (void *)spc->spxP2S); printf(" spxS2P: %p\n", (void *)spc->spxS2P); printf(" spxP2X: %p\n", (void *)spc->spxP2X); return 0; } /*--------------------------------------------------------------------------*/ int spcset(struct spcprm *spc) { char ctype[9], ptype, xtype; int restreq, status; double alpha, beta_r, crvalX, dn_r, dXdS, epsilon, G, m, lambda_r, n_r, t, restfrq, restwav, theta; if (spc == 0x0) return 1; if (undefined(spc->crval)) { return 2; } spc->type[4] = '\0'; spc->code[3] = '\0'; spc->w[0] = 0.0; /* Analyse the spectral axis type. */ sprintf(ctype, "%s-%s", spc->type, spc->code); restfrq = spc->restfrq; restwav = spc->restwav; if ((status = spcspx(ctype, spc->crval, restfrq, restwav, &ptype, &xtype, &restreq, &crvalX, &dXdS))) { return status; } /* Satisfy rest frequency/wavelength requirements. */ if (restreq) { if (restreq == 3 && restfrq == 0.0 && restwav == 0.0) { /* VRAD-V2F, VOPT-V2W, and ZOPT-V2W require the rest frequency or */ /* wavelength for the S-P and P-X transformations but not for S-X */ /* so supply a phoney value. */ restwav = 1.0; } if (restfrq == 0.0) { restfrq = C/restwav; } else { restwav = C/restfrq; } if (ptype == 'F') { spc->w[0] = restfrq; } else if (ptype != 'V') { spc->w[0] = restwav; } else { if (xtype == 'F') { spc->w[0] = restfrq; } else { spc->w[0] = restwav; } } } spc->w[1] = crvalX; spc->w[2] = dXdS; /* Set pointers-to-functions for the linear part of the transformation. */ if (ptype == 'F') { if (strcmp(spc->type, "FREQ") == 0) { /* Frequency. */ spc->flag = FREQ; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "AFRQ") == 0) { /* Angular frequency. */ spc->flag = AFRQ; spc->spxP2S = freqafrq; spc->spxS2P = afrqfreq; } else if (strcmp(spc->type, "ENER") == 0) { /* Photon energy. */ spc->flag = ENER; spc->spxP2S = freqener; spc->spxS2P = enerfreq; } else if (strcmp(spc->type, "WAVN") == 0) { /* Wave number. */ spc->flag = WAVN; spc->spxP2S = freqwavn; spc->spxS2P = wavnfreq; } else if (strcmp(spc->type, "VRAD") == 0) { /* Radio velocity. */ spc->flag = VRAD; spc->spxP2S = freqvrad; spc->spxS2P = vradfreq; } } else if (ptype == 'W') { if (strcmp(spc->type, "WAVE") == 0) { /* Vacuum wavelengths. */ spc->flag = WAVE; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "VOPT") == 0) { /* Optical velocity. */ spc->flag = VOPT; spc->spxP2S = wavevopt; spc->spxS2P = voptwave; } else if (strcmp(spc->type, "ZOPT") == 0) { /* Redshift. */ spc->flag = ZOPT; spc->spxP2S = wavezopt; spc->spxS2P = zoptwave; } } else if (ptype == 'A') { if (strcmp(spc->type, "AWAV") == 0) { /* Air wavelengths. */ spc->flag = AWAV; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } } else if (ptype == 'V') { if (strcmp(spc->type, "VELO") == 0) { /* Relativistic velocity. */ spc->flag = VELO; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "BETA") == 0) { /* Velocity ratio (v/c). */ spc->flag = BETA; spc->spxP2S = velobeta; spc->spxS2P = betavelo; } } /* Set pointers-to-functions for the non-linear part of the spectral */ /* transformation. */ spc->isGrism = 0; if (xtype == 'F') { /* Axis is linear in frequency. */ if (ptype == 'F') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; } else if (ptype == 'W') { spc->spxX2P = freqwave; spc->spxP2X = wavefreq; } else if (ptype == 'A') { spc->spxX2P = freqawav; spc->spxP2X = awavfreq; } else if (ptype == 'V') { spc->spxX2P = freqvelo; spc->spxP2X = velofreq; } spc->flag += F2S; } else if (xtype == 'W' || xtype == 'w') { /* Axis is linear in vacuum wavelengths. */ if (ptype == 'F') { spc->spxX2P = wavefreq; spc->spxP2X = freqwave; } else if (ptype == 'W') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; } else if (ptype == 'A') { spc->spxX2P = waveawav; spc->spxP2X = awavwave; } else if (ptype == 'V') { spc->spxX2P = wavevelo; spc->spxP2X = velowave; } if (xtype == 'W') { spc->flag += W2S; } else { /* Grism in vacuum. */ spc->isGrism = 1; spc->flag += GRI; } } else if (xtype == 'A' || xtype == 'a') { /* Axis is linear in air wavelengths. */ if (ptype == 'F') { spc->spxX2P = awavfreq; spc->spxP2X = freqawav; } else if (ptype == 'W') { spc->spxX2P = awavwave; spc->spxP2X = waveawav; } else if (ptype == 'A') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; } else if (ptype == 'V') { spc->spxX2P = awavvelo; spc->spxP2X = veloawav; } if (xtype == 'A') { spc->flag += A2S; } else { /* Grism in air. */ spc->isGrism = 2; spc->flag += GRA; } } else if (xtype == 'V') { /* Axis is linear in relativistic velocity. */ if (ptype == 'F') { spc->spxX2P = velofreq; spc->spxP2X = freqvelo; } else if (ptype == 'W') { spc->spxX2P = velowave; spc->spxP2X = wavevelo; } else if (ptype == 'A') { spc->spxX2P = veloawav; spc->spxP2X = awavvelo; } else if (ptype == 'V') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; } spc->flag += V2S; } /* Check for grism axes. */ if (spc->isGrism) { /* Axis is linear in "grism parameter"; work in wavelength. */ lambda_r = crvalX; /* Set defaults. */ if (undefined(spc->pv[0])) spc->pv[0] = 0.0; if (undefined(spc->pv[1])) spc->pv[1] = 0.0; if (undefined(spc->pv[2])) spc->pv[2] = 0.0; if (undefined(spc->pv[3])) spc->pv[3] = 1.0; if (undefined(spc->pv[4])) spc->pv[4] = 0.0; if (undefined(spc->pv[5])) spc->pv[5] = 0.0; if (undefined(spc->pv[6])) spc->pv[6] = 0.0; /* Compute intermediaries. */ G = spc->pv[0]; m = spc->pv[1]; alpha = spc->pv[2]; n_r = spc->pv[3]; dn_r = spc->pv[4]; epsilon = spc->pv[5]; theta = spc->pv[6]; t = G*m/cosd(epsilon); beta_r = asind(t*lambda_r - n_r*sind(alpha)); t -= dn_r*sind(alpha); spc->w[1] = -tand(theta); spc->w[2] *= t / (cosd(beta_r)*cosd(theta)*cosd(theta)); spc->w[3] = beta_r + theta; spc->w[4] = (n_r - dn_r*lambda_r)*sind(alpha); spc->w[5] = 1.0 / t; } return 0; } /*--------------------------------------------------------------------------*/ int spcx2s( struct spcprm *spc, int nx, int sx, int sspec, const double x[], double spec[], int stat[]) { int statP2S, status = 0, statX2P; double beta; register int ix; register int *statp; register const double *xp; register double *specp; /* Initialize. */ if (spc == 0x0) return 1; if (spc->flag == 0) { if (spcset(spc)) return 2; } /* Convert intermediate world coordinate x to X. */ xp = x; specp = spec; statp = stat; for (ix = 0; ix < nx; ix++, xp += sx, specp += sspec) { *specp = spc->w[1] + (*xp)*spc->w[2]; *(statp++) = 0; } /* If X is the grism parameter then convert it to wavelength. */ if (spc->isGrism) { specp = spec; for (ix = 0; ix < nx; ix++, specp += sspec) { beta = atand(*specp) + spc->w[3]; *specp = (sind(beta) + spc->w[4]) * spc->w[5]; } } /* Apply the non-linear step of the algorithm chain to convert the */ /* X-type spectral variable to P-type intermediate spectral variable. */ if (spc->spxX2P) { if ((statX2P = spc->spxX2P(spc->w[0], nx, sspec, sspec, spec, spec, stat))) { if (statX2P == 4) { status = 3; } else { return statX2P; } } } /* Apply the linear step of the algorithm chain to convert P-type */ /* intermediate spectral variable to the required S-type variable. */ if (spc->spxP2S) { if ((statP2S = spc->spxP2S(spc->w[0], nx, sspec, sspec, spec, spec, stat))) { if (statP2S == 4) { status = 3; } else { return statP2S; } } } return status; } /*--------------------------------------------------------------------------*/ int spcs2x( struct spcprm *spc, int nspec, int sspec, int sx, const double spec[], double x[], int stat[]) { int statP2X, status = 0, statS2P; double beta, s; register int ispec; register int *statp; register const double *specp; register double *xp; /* Initialize. */ if (spc == 0x0) return 1; if (spc->flag == 0) { if (spcset(spc)) return 2; } /* Apply the linear step of the algorithm chain to convert the S-type */ /* spectral variable to P-type intermediate spectral variable. */ if (spc->spxS2P) { if ((statS2P = spc->spxS2P(spc->w[0], nspec, sspec, sx, spec, x, stat))) { if (statS2P == 4) { status = 4; } else { return statS2P; } } } else { /* Just a copy. */ xp = x; specp = spec; statp = stat; for (ispec = 0; ispec < nspec; ispec++, specp += sspec, xp += sx) { *xp = *specp; *(statp++) = 0; } } /* Apply the non-linear step of the algorithm chain to convert P-type */ /* intermediate spectral variable to X-type spectral variable. */ if (spc->spxP2X) { if ((statP2X = spc->spxP2X(spc->w[0], nspec, sx, sx, x, x, stat))) { if (statP2X == 4) { status = 4; } else { return statP2X; } } } if (spc->isGrism) { /* Convert X-type spectral variable (wavelength) to grism parameter. */ xp = x; statp = stat; for (ispec = 0; ispec < nspec; ispec++, xp += sx, statp++) { if (*statp) continue; s = *xp/spc->w[5] - spc->w[4]; if (fabs(s) <= 1.0) { beta = asind(s); *xp = tand(beta - spc->w[3]); } else { *statp = 1; } } } /* Convert X-type spectral variable to intermediate world coordinate x. */ xp = x; statp = stat; for (ispec = 0; ispec < nspec; ispec++, xp += sx) { if (*(statp++)) continue; *xp -= spc->w[1]; *xp /= spc->w[2]; } return status; } /*--------------------------------------------------------------------------*/ int spctyp( const char ctypei[9], char stype[], char scode[], char sname[], char units[], char *ptype, char *xtype, int *restreq) { char ctype[9], ptype_t, sname_t[32], units_t[8], xtype_t; int restreq_t = 0; /* Copy with blank padding. */ sprintf(ctype, "%-8s", ctypei); ctype[8] = '\0'; /* Do alias translation for AIPS spectral types. */ if (ctype[4] == '-') { if (strcmp(ctype+5, "LSR") == 0 || strcmp(ctype+5, "HEL") == 0 || strcmp(ctype+5, "OBS") == 0) { if (strncmp(ctype, "FREQ", 4) == 0 || strncmp(ctype, "VELO", 4) == 0) { strcpy(ctype+4, " "); } else if (strncmp(ctype, "FELO", 4) == 0) { strcpy(ctype, "VOPT-F2W"); } } } /* Validate the S-type spectral variable. */ if (strncmp(ctype, "FREQ", 4) == 0) { strcpy(sname_t, "Frequency"); strcpy(units_t, "Hz"); ptype_t = 'F'; } else if (strncmp(ctype, "AFRQ", 4) == 0) { strcpy(sname_t, "Angular frequency"); strcpy(units_t, "deg/s"); ptype_t = 'F'; } else if (strncmp(ctype, "ENER", 4) == 0) { strcpy(sname_t, "Photon energy"); strcpy(units_t, "J"); ptype_t = 'F'; } else if (strncmp(ctype, "WAVN", 4) == 0) { strcpy(sname_t, "Wavenumber"); strcpy(units_t, "1/m"); ptype_t = 'F'; } else if (strncmp(ctype, "VRAD", 4) == 0) { strcpy(sname_t, "Radio velocity"); strcpy(units_t, "m/s"); ptype_t = 'F'; restreq_t = 1; } else if (strncmp(ctype, "WAVE", 4) == 0) { strcpy(sname_t, "Vacuum wavelength"); strcpy(units_t, "m"); ptype_t = 'W'; } else if (strncmp(ctype, "VOPT", 4) == 0) { strcpy(sname_t, "Optical velocity"); strcpy(units_t, "m/s"); ptype_t = 'W'; restreq_t = 1; } else if (strncmp(ctype, "ZOPT", 4) == 0) { strcpy(sname_t, "Redshift"); strcpy(units_t, ""); ptype_t = 'W'; restreq_t = 1; } else if (strncmp(ctype, "AWAV", 4) == 0) { strcpy(sname_t, "Air wavelength"); strcpy(units_t, "m"); ptype_t = 'A'; } else if (strncmp(ctype, "VELO", 4) == 0) { strcpy(sname_t, "Relativistic velocity"); strcpy(units_t, "m/s"); ptype_t = 'V'; } else if (strncmp(ctype, "BETA", 4) == 0) { strcpy(sname_t, "Velocity ratio (v/c)"); strcpy(units_t, ""); ptype_t = 'V'; } else { return 2; } /* Determine X-type and validate the spectral algorithm code. */ if ((xtype_t = ctype[5]) == ' ') { /* The algorithm code must be completely blank. */ if (strcmp(ctype+4, " ") != 0) { return 2; } xtype_t = ptype_t; } else if (ctype[4] != '-') { return 2; } else if (strcmp(ctype+5, "LOG") == 0 || strcmp(ctype+5, "TAB") == 0) { /* Logarithmic or tabular axis, not linear in any spectral type. */ } else if (xtype_t == 'G') { /* Validate the algorithm code. */ if (ctype[6] != 'R') { return 2; } /* Grism coordinates... */ if (ctype[7] == 'I') { /* ...in vacuum. */ xtype_t = 'w'; } else if (ctype[7] == 'A') { /* ...in air. */ xtype_t = 'a'; } else { return 2; } } else if (ctype[6] != '2') { /* Algorithm code has invalid syntax. */ return 2; } else if (ctype[7] != ptype_t && ctype[7] != '?') { /* The P-, and S-type variables are inconsistent. */ return 2; } else if (ctype[7] == ctype[5]) { /* Degenerate algorithm code. */ sprintf(ctype+4, " "); } /* Rest freq/wavelength required for transformation between P and X? */ if (strchr("FWAwa", (int)xtype_t)) { if (ptype_t == 'V') { restreq_t += 2; } } else if (xtype_t == 'V') { if (strchr("FWAwa", (int)ptype_t)) { restreq_t += 2; } } else if (strchr("LT", (int)xtype_t) == 0) { /* Invalid X-type variable code. */ return 2; } /* Copy results. */ if (stype) { strncpy(stype, ctype, 4); stype[4] = '\0'; } if (scode) strcpy(scode, ctype+5); if (sname) strcpy(sname, sname_t); if (units) strcpy(units, units_t); if (ptype) *ptype = ptype_t; if (xtype) *xtype = xtype_t; if (restreq) *restreq = restreq_t; return 0; } /*--------------------------------------------------------------------------*/ int spcspx( const char ctypeS[9], double crvalS, double restfrq, double restwav, char *ptype, char *xtype, int *restreq, double *crvalX, double *dXdS) { char scode[4], stype[5], type[8]; int status; double dPdS, dXdP; struct spxprm spx; /* Analyse the spectral axis code. */ if (spctyp(ctypeS, stype, scode, 0x0, 0x0, ptype, xtype, restreq)) { return 2; } if (strstr("LT", xtype)) { /* Can't handle logarithmic or tabular coordinates. */ return 2; } /* Do we have rest frequency and/or wavelength as required? */ if ((*restreq)%3 && restfrq == 0.0 && restwav == 0.0) { return 2; } /* Compute all spectral parameters and their derivatives. */ strcpy(type, stype); if ((status = specx(type, crvalS, restfrq, restwav, &spx))) { return 2; } /* Transform S-P (linear) and P-X (non-linear). */ dPdS = 0.0; dXdP = 0.0; if (*ptype == 'F') { if (strcmp(stype, "FREQ") == 0) { dPdS = 1.0; } else if (strcmp(stype, "AFRQ") == 0) { dPdS = spx.dfreqafrq; } else if (strcmp(stype, "ENER") == 0) { dPdS = spx.dfreqener; } else if (strcmp(stype, "WAVN") == 0) { dPdS = spx.dfreqwavn; } else if (strcmp(stype, "VRAD") == 0) { dPdS = spx.dfreqvrad; } if (*xtype == 'F') { *crvalX = spx.freq; dXdP = 1.0; } else if (*xtype == 'W' || *xtype == 'w') { *crvalX = spx.wave; dXdP = spx.dwavefreq; } else if (*xtype == 'A' || *xtype == 'a') { *crvalX = spx.awav; dXdP = spx.dawavfreq; } else if (*xtype == 'V') { *crvalX = spx.velo; dXdP = spx.dvelofreq; } } else if (*ptype == 'W' || *ptype == 'w') { if (strcmp(stype, "WAVE") == 0) { dPdS = 1.0; } else if (strcmp(stype, "VOPT") == 0) { dPdS = spx.dwavevopt; } else if (strcmp(stype, "ZOPT") == 0) { dPdS = spx.dwavezopt; } if (*xtype == 'F') { *crvalX = spx.freq; dXdP = spx.dfreqwave; } else if (*xtype == 'W' || *xtype == 'w') { *crvalX = spx.wave; dXdP = 1.0; } else if (*xtype == 'A' || *xtype == 'a') { *crvalX = spx.awav; dXdP = spx.dawavwave; } else if (*xtype == 'V') { *crvalX = spx.velo; dXdP = spx.dvelowave; } } else if (*ptype == 'A' || *ptype == 'a') { if (strcmp(stype, "AWAV") == 0) { dPdS = 1.0; } if (*xtype == 'F') { *crvalX = spx.freq; dXdP = spx.dfreqawav; } else if (*xtype == 'W' || *xtype == 'w') { *crvalX = spx.wave; dXdP = spx.dwaveawav; } else if (*xtype == 'A' || *xtype == 'a') { *crvalX = spx.awav; dXdP = 1.0; } else if (*xtype == 'V') { *crvalX = spx.velo; dXdP = spx.dveloawav; } } else if (*ptype == 'V') { if (strcmp(stype, "VELO") == 0) { dPdS = 1.0; } else if (strcmp(stype, "BETA") == 0) { dPdS = spx.dvelobeta; } if (*xtype == 'F') { *crvalX = spx.freq; dXdP = spx.dfreqvelo; } else if (*xtype == 'W' || *xtype == 'w') { *crvalX = spx.wave; dXdP = spx.dwavevelo; } else if (*xtype == 'A' || *xtype == 'a') { *crvalX = spx.awav; dXdP = spx.dawavvelo; } else if (*xtype == 'V') { *crvalX = spx.velo; dXdP = 1.0; } } *dXdS = dXdP * dPdS; return 0; } /*--------------------------------------------------------------------------*/ int spcxps( const char ctypeS[9], double crvalX, double restfrq, double restwav, char *ptype, char *xtype, int *restreq, double *crvalS, double *dSdX) { char scode[4], stype[5], type[8]; int status; double dPdX, dSdP; struct spxprm spx; /* Analyse the spectral axis type. */ if (spctyp(ctypeS, stype, scode, 0x0, 0x0, ptype, xtype, restreq)) { return 2; } if (strstr("LT", xtype)) { /* Can't handle logarithmic or tabular coordinates. */ return 2; } /* Do we have rest frequency and/or wavelength as required? */ if ((*restreq)%3 && restfrq == 0.0 && restwav == 0.0) { return 2; } /* Compute all spectral parameters and their derivatives. */ if (*xtype == 'F') { strcpy(type, "FREQ"); } else if (*xtype == 'W' || *xtype == 'w') { strcpy(type, "WAVE"); } else if (*xtype == 'A' || *xtype == 'a') { strcpy(type, "AWAV"); } else if (*xtype == 'V') { strcpy(type, "VELO"); } if ((status = specx(type, crvalX, restfrq, restwav, &spx))) { return 2; } /* Transform X-P (non-linear) and P-S (linear). */ dPdX = 0.0; dSdP = 0.0; if (*ptype == 'F') { if (*xtype == 'F') { dPdX = 1.0; } else if (*xtype == 'W' || *xtype == 'w') { dPdX = spx.dfreqwave; } else if (*xtype == 'A' || *xtype == 'a') { dPdX = spx.dfreqawav; } else if (*xtype == 'V') { dPdX = spx.dfreqvelo; } if (strcmp(stype, "FREQ") == 0) { *crvalS = spx.freq; dSdP = 1.0; } else if (strcmp(stype, "AFRQ") == 0) { *crvalS = spx.afrq; dSdP = spx.dafrqfreq; } else if (strcmp(stype, "ENER") == 0) { *crvalS = spx.ener; dSdP = spx.denerfreq; } else if (strcmp(stype, "WAVN") == 0) { *crvalS = spx.wavn; dSdP = spx.dwavnfreq; } else if (strcmp(stype, "VRAD") == 0) { *crvalS = spx.vrad; dSdP = spx.dvradfreq; } } else if (*ptype == 'W') { if (*xtype == 'F') { dPdX = spx.dwavefreq; } else if (*xtype == 'W' || *xtype == 'w') { dPdX = 1.0; } else if (*xtype == 'A' || *xtype == 'a') { dPdX = spx.dwaveawav; } else if (*xtype == 'V') { dPdX = spx.dwavevelo; } if (strcmp(stype, "WAVE") == 0) { *crvalS = spx.wave; dSdP = 1.0; } else if (strcmp(stype, "VOPT") == 0) { *crvalS = spx.vopt; dSdP = spx.dvoptwave; } else if (strcmp(stype, "ZOPT") == 0) { *crvalS = spx.zopt; dSdP = spx.dzoptwave; } } else if (*ptype == 'A') { if (*xtype == 'F') { dPdX = spx.dawavfreq; } else if (*xtype == 'W' || *xtype == 'w') { dPdX = spx.dawavwave; } else if (*xtype == 'A' || *xtype == 'a') { dPdX = 1.0; } else if (*xtype == 'V') { dPdX = spx.dawavvelo; } if (strcmp(stype, "AWAV") == 0) { *crvalS = spx.awav; dSdP = 1.0; } } else if (*ptype == 'V') { if (*xtype == 'F') { dPdX = spx.dvelofreq; } else if (*xtype == 'W' || *xtype == 'w') { dPdX = spx.dvelowave; } else if (*xtype == 'A' || *xtype == 'a') { dPdX = spx.dveloawav; } else if (*xtype == 'V') { dPdX = 1.0; } if (strcmp(stype, "VELO") == 0) { *crvalS = spx.velo; dSdP = 1.0; } else if (strcmp(stype, "BETA") == 0) { *crvalS = spx.beta; dSdP = spx.dbetavelo; } } *dSdX = dSdP * dPdX; return 0; } /*--------------------------------------------------------------------------*/ int spctrn( const char ctypeS1[9], double crvalS1, double cdeltS1, double restfrq, double restwav, char ctypeS2[9], double *crvalS2, double *cdeltS2) { char *cp, ptype1, ptype2, xtype1, xtype2; int restreq, status; double crvalX, dS2dX, dXdS1; if ((status = spcspx(ctypeS1, crvalS1, restfrq, restwav, &ptype1, &xtype1, &restreq, &crvalX, &dXdS1))) { return status; } /* Blank fill. */ ctypeS2[8] = '\0'; for (cp = ctypeS2; *cp; cp++); while (cp < ctypeS2+8) *(cp++) = ' '; if (strncmp(ctypeS2+5, "???", 3) == 0) { /* Set the algorithm code if required. */ if (xtype1 == 'w') { strcpy(ctypeS2+5, "GRI"); } else if (xtype1 == 'a') { strcpy(ctypeS2+5, "GRA"); } else { ctypeS2[5] = xtype1; ctypeS2[6] = '2'; } } if ((status = spcxps(ctypeS2, crvalX, restfrq, restwav, &ptype2, &xtype2, &restreq, crvalS2, &dS2dX))) { return status; } /* Are the X-types compatible? */ if (xtype2 != xtype1) { return 2; } if (ctypeS2[7] == '?') { if (ptype2 == xtype2) { strcpy(ctypeS2+4, " "); } else { ctypeS2[7] = ptype2; } } *cdeltS2 = dS2dX * dXdS1 * cdeltS1; return 0; }