summaryrefslogtreecommitdiffstats
path: root/ast/specmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'ast/specmap.c')
-rw-r--r--ast/specmap.c4671
1 files changed, 4671 insertions, 0 deletions
diff --git a/ast/specmap.c b/ast/specmap.c
new file mode 100644
index 0000000..164179a
--- /dev/null
+++ b/ast/specmap.c
@@ -0,0 +1,4671 @@
+/*
+*class++
+* Name:
+* SpecMap
+
+* Purpose:
+* Sequence of spectral coordinate conversions.
+
+* Constructor Function:
+c astSpecMap (also see astSpecAdd)
+f AST_SPECMAP (also see AST_SPECADD)
+
+* Description:
+* A SpecMap is a specialised form of Mapping which can be used to
+* represent a sequence of conversions between standard spectral
+* coordinate systems.
+*
+* When an SpecMap is first created, it simply performs a unit
+c (null) Mapping. Using the astSpecAdd
+f (null) Mapping. Using the AST_SPECADD
+c function, a series of coordinate conversion steps may then be
+f routine, a series of coordinate conversion steps may then be
+* added. This allows multi-step conversions between a variety of
+* spectral coordinate systems to be assembled out of a set of building
+* blocks.
+*
+* Conversions are available to transform between standards of rest.
+* Such conversions need to know the source position as an RA and DEC.
+* This information can be supplied in the form of parameters for
+* the relevant conversions, in which case the SpecMap is 1-dimensional,
+* simply transforming the spectral axis values. This means that the
+* same source position will always be used by the SpecMap. However, this
+* may not be appropriate for an accurate description of a 3-D spectral
+* cube, where changes of spatial position can produce significant
+* changes in the Doppler shift introduced when transforming between
+* standards of rest. For this situation, a 3-dimensional SpecMap can
+* be created in which axes 2 and 3 correspond to the source RA and DEC
+* The SpecMap simply copies values for axes 2 and 3 from input to
+* output), but modifies axis 1 values (the spectral axis) appropriately.
+*
+* For details of the individual coordinate conversions available,
+c see the description of the astSpecAdd function.
+f see the description of the AST_SPECADD routine.
+
+* Inheritance:
+* The SpecMap class inherits from the Mapping class.
+
+* Attributes:
+* The SpecMap class does not define any new attributes beyond those
+* which are applicable to all Mappings.
+
+* Functions:
+c In addition to those functions applicable to all Mappings, the
+c following function may also be applied to all SpecMaps:
+f In addition to those routines applicable to all Mappings, the
+f following routine may also be applied to all SpecMaps:
+*
+c - astSpecAdd: Add a spectral coordinate conversion to an SpecMap
+f - AST_SPECADD: Add a spectral coordinate conversion to an SpecMap
+
+* Copyright:
+* Copyright (C) 1997-2006 Council for the Central Laboratory of the
+* Research Councils
+* Copyright (C) 2009 Science & Technology Facilities Council.
+* All Rights Reserved.
+
+* Licence:
+* This program 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.
+*
+* This program 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
+* License along with this program. If not, see
+* <http://www.gnu.org/licenses/>.
+
+* Authors:
+* DSB: David S. Berry (Starlink)
+
+* History:
+* 6-NOV-2002 (DSB):
+* Original version.
+* 14-JUL-2003 (DSB):
+* Added checks for NAN values produced by transformation functions.
+* 17-SEP-2003 (DSB):
+* - Improve FRTOAW accuracy by iterating.
+* - Changed Refrac to use algorithm given in FITS-WCS paper 3
+* version dated 21/9/03.
+* 14-FEB-2006 (DSB):
+* Override astGetObjSize.
+* 10-MAY-2006 (DSB):
+* Override astEqual.
+* 15-NOV-2006 (DSB):
+* Guard against division by zero when converting freq to wave in
+* SystemChange.
+* 18-JUN-2009 (DSB):
+* Add OBSALT argument to TPF2HL and HLF2TP conversions.
+* Change GEOLON/LAT to OBSLON/LAT for consistency with other
+* classes.
+* 2-OCT-2012 (DSB):
+* Check for Infs as well as NaNs.
+*class--
+*/
+
+/* Module Macros. */
+/* ============== */
+/* Set the name of the class we are implementing. This indicates to
+ the header files that define class interfaces that they should make
+ "protected" symbols available. */
+#define astCLASS SpecMap
+
+/* Codes to identify spectral coordinate conversions. */
+#define AST__SPEC_NULL 0 /* Null value */
+#define AST__FRTOVL 1 /* Frequency to relativistic velocity */
+#define AST__VLTOFR 2 /* Relativistic velocity to Frequency */
+#define AST__ENTOFR 3 /* Energy to frequency */
+#define AST__FRTOEN 4 /* Frequency to energy */
+#define AST__WNTOFR 5 /* Wave number to frequency */
+#define AST__FRTOWN 6 /* Frequency to wave number */
+#define AST__WVTOFR 7 /* Wavelength (vacuum) to frequency */
+#define AST__FRTOWV 8 /* Frequency to wavelength (vacuum) */
+#define AST__AWTOFR 9 /* Wavelength (air) to frequency */
+#define AST__FRTOAW 10 /* Frequency to wavelength (air) */
+#define AST__VRTOVL 11 /* Radio to relativistic velocity */
+#define AST__VLTOVR 12 /* Relativistic to radio velocity */
+#define AST__VOTOVL 13 /* Optical to relativistic velocity */
+#define AST__VLTOVO 14 /* Relativistic to optical velocity */
+#define AST__ZOTOVL 15 /* Redshift to relativistic velocity */
+#define AST__VLTOZO 16 /* Relativistic velocity to redshift */
+#define AST__BTTOVL 17 /* Beta factor to relativistic velocity */
+#define AST__VLTOBT 18 /* Relativistic velocity to beta factor */
+#define AST__USF2HL 19 /* User-defined to heliocentric frequency */
+#define AST__HLF2US 20 /* Heliocentric to user-defined frequency */
+#define AST__TPF2HL 21 /* Topocentric to heliocentric frequency */
+#define AST__HLF2TP 22 /* Heliocentric to topocentric frequency */
+#define AST__GEF2HL 23 /* Geocentric to heliocentric frequency */
+#define AST__HLF2GE 24 /* Heliocentric to geocentric frequency */
+#define AST__BYF2HL 25 /* Barycentric to heliocentric frequency */
+#define AST__HLF2BY 26 /* Heliocentric to barycentric frequency */
+#define AST__LKF2HL 27 /* LSRK to heliocentric frequency */
+#define AST__HLF2LK 28 /* Heliocentric to LSRK frequency */
+#define AST__LDF2HL 29 /* LSRD to heliocentric frequency */
+#define AST__HLF2LD 30 /* Heliocentric to LSRD frequency */
+#define AST__LGF2HL 31 /* Local group to heliocentric frequency */
+#define AST__HLF2LG 32 /* Heliocentric to local group frequency */
+#define AST__GLF2HL 33 /* Galactic to heliocentric frequency */
+#define AST__HLF2GL 34 /* Heliocentric to galactic frequency */
+
+/* Maximum number of arguments required by a conversion. */
+#define MAX_ARGS 7
+
+/* The alphabet (used for generating keywords for arguments). */
+#define ALPHABET "abcdefghijklmnopqrstuvwxyz"
+
+/* Angle conversion */
+#define PI 3.141592653589793238462643
+#define PIBY2 (PI/2.0)
+#define D2R (PI/180.0)
+#define R2D (180.0/PI)
+
+/* Include files. */
+/* ============== */
+/* Interface definitions. */
+/* ---------------------- */
+#include "pal.h" /* SLALIB interface */
+
+#include "globals.h" /* Thread-safe global data access */
+#include "error.h" /* Error reporting facilities */
+#include "memory.h" /* Memory allocation facilities */
+#include "object.h" /* Base Object class */
+#include "pointset.h" /* Sets of points/coordinates */
+#include "mapping.h" /* Coordinate Mappings (parent class) */
+#include "unitmap.h" /* Unit (null) Mappings */
+#include "specmap.h" /* Interface definition for this class */
+
+/* Error code definitions. */
+/* ----------------------- */
+#include "ast_err.h" /* AST error codes */
+
+/* C header files. */
+/* --------------- */
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+/* Module Variables. */
+/* ================= */
+
+/* Address of this static variable is used as a unique identifier for
+ member of this class. */
+static int class_check;
+
+/* Pointers to parent class methods which are extended by this class. */
+static int (* parent_getobjsize)( AstObject *, int * );
+static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
+static double (* parent_rate)( AstMapping *, double *, int, int, int * );
+
+
+#ifdef THREAD_SAFE
+/* Define how to initialise thread-specific globals. */
+#define GLOBAL_inits \
+ globals->Class_Init = 0;
+
+/* Create the function that initialises global data for this module. */
+astMAKE_INITGLOBALS(SpecMap)
+
+/* Define macros for accessing each item of thread specific global data. */
+#define class_init astGLOBAL(SpecMap,Class_Init)
+#define class_vtab astGLOBAL(SpecMap,Class_Vtab)
+
+
+#include <pthread.h>
+
+
+#else
+
+
+/* Define the class virtual function table and its initialisation flag
+ as static variables. */
+static AstSpecMapVtab class_vtab; /* Virtual function table */
+static int class_init = 0; /* Virtual function table initialised? */
+
+#endif
+
+/* Structure to hold parameters and intermediate values describing a
+ reference frame */
+typedef struct FrameDef {
+ double obsalt; /* Observers geodetic altitude (m) */
+ double obslat; /* Observers geodetic latitude (rads) */
+ double obslon; /* Observers geodetic longitude (rads, +ve east) */
+ double epoch; /* Julian epoch of observation */
+ double refdec; /* RA of reference point (FK5 J2000) */
+ double refra; /* DEC of reference point (FK5 J2000) */
+ double veluser; /* Heliocentric velocity of user-defined system (m/s) */
+ double last; /* Local apparent sideral time */
+ double amprms[21]; /* Mean to apparent parameters */
+ double vuser[3]; /* Used-defined velocity as a FK5 J2000 vector */
+ double dvh[3]; /* Earth-sun velocity */
+ double dvb[3]; /* Barycentre-sun velocity */
+} FrameDef;
+
+/* External Interface Function Prototypes. */
+/* ======================================= */
+/* The following functions have public prototypes only (i.e. no
+ protected prototypes), so we must provide local prototypes for use
+ within this module. */
+AstSpecMap *astSpecMapId_( int, int, const char *, ... );
+
+/* Prototypes for Private Member Functions. */
+/* ======================================== */
+static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
+static const char *CvtString( int, const char **, int *, int *, int *, int *, const char *[ MAX_ARGS ], int * );
+static double BaryVel( double, double, FrameDef *, int * );
+static double GalVel( double, double, FrameDef *, int * );
+static double GeoVel( double, double, FrameDef *, int * );
+static double LgVel( double, double, FrameDef *, int * );
+static double LsrdVel( double, double, FrameDef *, int * );
+static double LsrkVel( double, double, FrameDef *, int * );
+static double Rate( AstMapping *, double *, int, int, int * );
+static double Refrac( double, int * );
+static double Rverot( double, double, double, double, double, int * );
+static double TopoVel( double, double, FrameDef *, int * );
+static double UserVel( double, double, FrameDef *, int * );
+static int CvtCode( const char *, int * );
+static int Equal( AstObject *, AstObject *, int * );
+static int FrameChange( int, int, double *, double *, double *, double *, int, int * );
+static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
+static int SystemChange( int, int, double *, double *, int, int * );
+static void AddSpecCvt( AstSpecMap *, int, const double *, int * );
+static void Copy( const AstObject *, AstObject *, int * );
+static void Delete( AstObject *, int * );
+static void Dump( AstObject *, AstChannel *, int * );
+static void SpecAdd( AstSpecMap *, const char *, const double[], int * );
+
+static int GetObjSize( AstObject *, int * );
+/* Member functions. */
+/* ================= */
+static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
+/*
+* Name:
+* Equal
+
+* Purpose:
+* Test if two SpecMaps are equivalent.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* int Equal( AstObject *this, AstObject *that, int *status )
+
+* Class Membership:
+* SpecMap member function (over-rides the astEqual protected
+* method inherited from the astMapping class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* two SpecMaps are equivalent.
+
+* Parameters:
+* this
+* Pointer to the first Object (a SpecMap).
+* that
+* Pointer to the second Object.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* One if the SpecMaps are equivalent, zero otherwise.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global status set, or if it should fail for any reason.
+*/
+
+/* Local Variables: */
+ AstSpecMap *that;
+ AstSpecMap *this;
+ const char *argdesc[ MAX_ARGS ];
+ const char *comment;
+ int argdec;
+ int argra;
+ int i, j;
+ int nargs;
+ int nin;
+ int nout;
+ int result;
+ int szargs;
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain pointers to the two SpecMap structures. */
+ this = (AstSpecMap *) this_object;
+ that = (AstSpecMap *) that_object;
+
+/* Check the second object is a SpecMap. We know the first is a
+ SpecMap since we have arrived at this implementation of the virtual
+ function. */
+ if( astIsASpecMap( that ) ) {
+
+/* Get the number of inputs and outputs and check they are the same for both. */
+ nin = astGetNin( this );
+ nout = astGetNout( this );
+ if( astGetNin( that ) == nin && astGetNout( that ) == nout ) {
+
+/* If the Invert flags for the two SpecMaps differ, it may still be possible
+ for them to be equivalent. First compare the SpecMaps if their Invert
+ flags are the same. In this case all the attributes of the two SpecMaps
+ must be identical. */
+ if( astGetInvert( this ) == astGetInvert( that ) ) {
+ if( this->ncvt == that->ncvt ) {
+ result = 1;
+ for( i = 0; i < this->ncvt && result; i++ ) {
+ if( this->cvttype[ i ] != that->cvttype[ i ] ) {
+ result = 0;
+ } else {
+ CvtString( this->cvttype[ i ], &comment, &argra,
+ &argdec, &nargs, &szargs, argdesc, status );
+ for( j = 0; j < nargs; j++ ) {
+ if( !astEQUAL( this->cvtargs[ i ][ j ],
+ that->cvtargs[ i ][ j ] ) ){
+ result = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+/* If the Invert flags for the two SpecMaps differ, the attributes of the two
+ SpecMaps must be inversely related to each other. */
+ } else {
+
+/* In the specific case of a SpecMap, Invert flags must be equal. */
+ result = 0;
+
+ }
+ }
+ }
+
+/* If an error occurred, clear the result value. */
+ if ( !astOK ) result = 0;
+
+/* Return the result, */
+ return result;
+}
+
+static int GetObjSize( AstObject *this_object, int *status ) {
+/*
+* Name:
+* GetObjSize
+
+* Purpose:
+* Return the in-memory size of an Object.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* int GetObjSize( AstObject *this, int *status )
+
+* Class Membership:
+* SpecMap member function (over-rides the astGetObjSize protected
+* method inherited from the parent class).
+
+* Description:
+* This function returns the in-memory size of the supplied SpecMap,
+* in bytes.
+
+* Parameters:
+* this
+* Pointer to the SpecMap.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The Object size, in bytes.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global status set, or if it should fail for any reason.
+*/
+
+/* Local Variables: */
+ AstSpecMap *this; /* Pointer to SpecMap structure */
+ int result; /* Result value to return */
+ int cvt; /* Loop counter for coordinate conversions */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointers to the SpecMap structure. */
+ this = (AstSpecMap *) this_object;
+
+/* Invoke the GetObjSize method inherited from the parent class, and then
+ add on any components of the class structure defined by thsi class
+ which are stored in dynamically allocated memory. */
+ result = (*parent_getobjsize)( this_object, status );
+
+ for ( cvt = 0; cvt < this->ncvt; cvt++ ) {
+ result += astTSizeOf( this->cvtargs[ cvt ] );
+ }
+
+ result += astTSizeOf( this->cvtargs );
+ result += astTSizeOf( this->cvttype );
+
+/* If an error occurred, clear the result value. */
+ if ( !astOK ) result = 0;
+
+/* Return the result, */
+ return result;
+}
+
+static void AddSpecCvt( AstSpecMap *this, int cvttype, const double *args, int *status ) {
+/*
+* Name:
+* AddSpecCvt
+
+* Purpose:
+* Add a coordinate conversion step to an SpecMap.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* void AddSpecCvt( AstSpecMap *this, int cvttype, const double *args )
+
+* Class Membership:
+* SpecMap member function.
+
+* Description:
+* This function allows one of the supported spectral coordinate
+* conversions to be appended to a SpecMap. When a SpecMap is first
+* created (using astSpecMap), it simply performs a unit mapping. By
+* using AddSpecCvt repeatedly, a series of coordinate conversions may
+* then be specified which the SpecMap will subsequently perform in
+* sequence. This allows a complex coordinate conversion to be
+* assembled out of the basic building blocks. The SpecMap will also
+* perform the inverse coordinate conversion (applying the individual
+* conversion steps in reverse) if required.
+
+* Parameters:
+* this
+* Pointer to the SpecMap.
+* cvttype
+* A code to identify which spectral coordinate conversion is to be
+* appended. See the "Coordinate Conversions" section for details
+* of those available.
+* args
+* Pointer to an array of double containing the argument values
+* required to fully specify the required coordinate
+* conversion. The number of arguments depends on the conversion
+* (see the "Coordinate Conversions" section for details). This
+* value is ignored and may be NULL if no arguments are required.
+
+* Returned Value:
+* void.
+
+* Coordinate Conversions:
+* The following values may be supplied for the "cvttype" parameter
+* in order to specify the coordinate conversion to be performed.
+* The argument(s) required to fully specify each conversion are
+* indicated in parentheses after each value, and described at the end
+* of the list. Values for these should be given in the array pointed
+* at by "args".
+*
+* AST__FRTOVL( RF )
+* Convert frequency to relativistic velocity.
+* AST__VLTOFR( RF )
+* Convert relativistic velocity to Frequency.
+* AST__ENTOFR
+* Convert energy to frequency.
+* AST__FRTOEN
+* Convert frequency to energy.
+* AST__WNTOFR
+* Convert wave number to frequency.
+* AST__FRTOWN
+* Convert frequency to wave number.
+* AST__WVTOFR
+* Convert wavelength (vacuum) to frequency.
+* AST__FRTOWV
+* Convert frequency to wavelength (vacuum).
+* AST__AWTOFR
+* Convert wavelength (air) to frequency.
+* AST__FRTOAW
+* Convert frequency to wavelength (air).
+* AST__VRTOVL
+* Convert radio to relativistic velocity.
+* AST__VLTOVR
+* Convert relativistic to radio velocity.
+* AST__VOTOVL
+* Convert optical to relativistic velocity.
+* AST__VLTOVO
+* Convert relativistic to optical velocity.
+* AST__ZOTOVL
+* Convert redshift to relativistic velocity.
+* AST__VLTOZO
+* Convert relativistic velocity to redshift.
+* AST__BTTOVL
+* Convert beta factor to relativistic velocity.
+* AST__VLTOBT
+* Convert relativistic velocity to beta factor.
+* AST_USF2HL( VOFF, RA, DEC )
+* Convert frequency from a user-defined reference frame to
+* heliocentric.
+* AST__HLF2US( VOFF, RA, DEC )
+* Convert frequency from heliocentric reference frame to
+* user-defined.
+* AST__TPF2HL( OBSLON, OBSLAT, OBSALT, EPOCH, RA, DEC )
+* Convert from Topocentric to heliocentric frequency
+* AST__HLF2TP( OBSLON, OBSLAT, OBSALT, EPOCH, RA, DEC )
+* Convert from Heliocentric to topocentric frequency.
+* AST__GEF2HL( EPOCH, RA, DEC )
+* Convert from Geocentric to heliocentric frequency.
+* AST__HLF2GE( EPOCH, RA, DEC )
+* Convert from Heliocentric to geocentric frequency.
+* AST__BYF2HL( EPOCH, RA, DEC )
+* Convert from Barycentric to heliocentric frequency.
+* AST__HLF2BY( EPOCH, RA, DEC )
+* Convert from Heliocentric to barycentric frequency.
+* AST__LKF2HL( RA, DEC )
+* Convert from LSRK to heliocentric frequency.
+* AST__HLF2LK( RA, DEC )
+* Convert from Heliocentric to LSRK frequency.
+* AST__LDF2HL( RA, DEC )
+* Convert from LSRD to heliocentric frequency.
+* AST__HLF2LD( RA, DEC )
+* Convert from Heliocentric to LSRD frequency.
+* AST__LGF2HL( RA, DEC )
+* Convert from Local group to heliocentric frequency.
+* AST__HLF2LG( RA, DEC )
+* Convert from Heliocentric to local group frequency.
+* AST__GLF2HL( RA, DEC )
+* Convert from Galactic to heliocentric frequency.
+* AST__HLF2GL( RA, DEC )
+* Convert from Heliocentric to galactic frequency.
+*
+* The units for the values processed by the above conversions are as
+* follows:
+*
+* - all velocities: metres per second.
+* - frequency: Hertz.
+* - all wavelengths: metres.
+* - energy: Joules.
+* - wave number: cycles per metre.
+*
+* The arguments used in the above conversions are as follows:
+*
+* - RF: Rest frequency (Hz).
+* - OBSALT: Geodetic altitude of observer (IAU 1975, metres).
+* - OBSLAT: Geodetic latitude of observer (IAU 1975, radians).
+* - OBSLON: Longitude of observer (radians, positive eastwards).
+* - EPOCH: Epoch of observation (UT1 expressed as a Modified Julian Date).
+* - RA: Right Ascension of source (radians, FK5 J2000).
+* - DEC: Declination of source (radians, FK5 J2000).
+* - VOFF: Velocity of the user-defined reference frame, towards the
+* position given by RA and DEC, measured in the heliocentric
+* reference frame.
+*
+* If the SpecMap is 3-dimensional, source positions are provided by the
+* values supplied to inputs 2 and 3 of the SpecMap (which are simply
+* copied to outputs 2 and 3). Note, usable values are still required
+* for the RA and DEC arguments in order to define the "user-defined"
+* reference frame used by USF2HL and HLF2US. However, AST__BAD can be
+* supplied for RA and DEC if the user-defined reference frame is not
+* required.
+
+* Notes:
+* - The specified conversion is appended only if the SpecMap's
+* Invert attribute is zero. If it is non-zero, this function
+* effectively prefixes the inverse of the conversion specified
+* instead.
+*/
+
+/* Local Variables: */
+ const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */
+ const char *comment; /* Pointer to comment string */
+ const char *cvt_string; /* Pointer to conversion type string */
+ int argdec; /* Index of DEC argument */
+ int argra; /* Index of RA argument */
+ int i; /* Argument index */
+ int nargs; /* Number of user-supplied arguments */
+ int ncvt; /* Number of coordinate conversions */
+ int szargs; /* Size of arguments array */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Validate the coordinate conversion type and obtain the number of
+ required user-supplied arguments, and the size of the array in which
+ to put the user-supplied arguments (the array meay leave room after
+ the user-supplied arguments for various useful pre-calculated values). */
+ cvt_string = CvtString( cvttype, &comment, &argra, &argdec, &nargs,
+ &szargs, argdesc, status );
+
+/* If the coordinate conversion type was not valid, then report an
+ error. */
+ if ( astOK && !cvt_string ) {
+ astError( AST__SPCIN, "AddSpecCvt(%s): Invalid spectral coordinate "
+ "conversion type (%d).", status, astGetClass( this ),
+ (int) cvttype );
+ }
+
+/* Note the number of coordinate conversions already stored in the SpecMap. */
+ if ( astOK ) {
+ ncvt = this->ncvt;
+
+/* Extend the array of conversion types and the array of pointers to
+ their argument lists to accommodate the new one. */
+ this->cvttype = (int *) astGrow( this->cvttype, ncvt + 1,
+ sizeof( int ) );
+ this->cvtargs = (double **) astGrow( this->cvtargs, ncvt + 1,
+ sizeof( double * ) );
+
+/* If OK, allocate memory and store a copy of the argument list,
+ putting a pointer to the copy into the SpecMap. */
+ if ( astOK ) {
+ this->cvtargs[ ncvt ] = astStore( NULL, args,
+ sizeof( double ) * (size_t) szargs );
+ }
+
+/* Store the conversion type and increment the conversion count. Also put
+ AST__BAD in any elements of the argument array which are beyond the
+ end of the user-supplied arguments. These will be used to hold
+ intermediate values calculated on the basis of the user-supplied
+ arguments. */
+ if ( astOK ) {
+ this->cvttype[ ncvt ] = cvttype;
+ this->ncvt++;
+ for( i = nargs; i < szargs; i++ ) this->cvtargs[ ncvt ][ i ] = AST__BAD;
+ }
+ }
+}
+
+static double BaryVel( double ra, double dec, FrameDef *def, int *status ) {
+/*
+* Name:
+* BaryVel
+
+* Purpose:
+* Find the velocity of the earth-sun barycentre away from the source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double BaryVel( double ra, double dec, FrameDef *def, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function finds the component of the velocity of the earth-sun
+* barycentre away from a specified source position, at a given epoch, in
+* the frame of rest of the centre of the Sun.
+
+* Parameters:
+* ra
+* The RA (rads, FK5 J2000) of the source.
+* dec
+* The Dec (rads, FK5 J2000) of the source.
+* def
+* Pointer to a FrameDef structure which holds the parameters which
+* define the frame, together with cached intermediate results.
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the frame's velocity away from the position given by
+* "ra" and "dec", in m/s, measured within the Heliographic frame of
+* rest. Zero is returned if an error has already occurred.
+
+*/
+
+/* Local Variables: */
+ double dpb[ 3 ]; /* Barycentric earth position vector */
+ double dph[ 3 ]; /* Heliocentric earth position vector */
+ double dvh[ 3 ]; /* Heliocentric earth velocity vector */
+ double v[ 3 ]; /* Source direction vector */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* Get the Cartesian vector towards the source, in the Cartesian FK5
+ J2000 system. */
+ palDcs2c( ra, dec, v );
+
+/* If not already done so, get the Earth/Sun velocity and position vectors in
+ the same system. Speed is returned in units of AU/s. Store in the supplied
+ frame definition structure. */
+ if( def->dvb[ 0 ] == AST__BAD ) {
+ palEvp( def->epoch, 2000.0, def->dvb, dpb, dvh, dph );
+
+/* Change the barycentric velocity of the earth into the heliocentric
+ velocity of the barycentre. */
+ def->dvb[ 0 ] = dvh[ 0 ] - def->dvb[ 0 ];
+ def->dvb[ 1 ] = dvh[ 1 ] - def->dvb[ 1 ];
+ def->dvb[ 2 ] = dvh[ 2 ] - def->dvb[ 2 ];
+ }
+
+/* Return the component away from the source, of the velocity of the
+ barycentre relative to the sun (in m/s). */
+ return -palDvdv( v, def->dvb )*149.597870E9;
+
+}
+
+static int CvtCode( const char *cvt_string, int *status ) {
+/*
+* Name:
+* CvtCode
+
+* Purpose:
+* Convert a conversion type from a string representation to a code value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* int CvtCode( const char *cvt_string, int *status )
+
+* Class Membership:
+* SpecMap member function.
+
+* Description:
+* This function accepts a string used to repersent one of the
+* SpecMap coordinate conversions and converts it into a code
+* value for internal use.
+
+* Parameters:
+* cvt_string
+* Pointer to a constant null-terminated string representing a
+* spectral coordinate conversion. This is case sensitive and should
+* contain no unnecessary white space.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The equivalent conversion code. If the string was not
+* recognised, the code AST__SPEC_NULL is returned, without error.
+
+* Notes:
+* - A value of AST__SPEC_NULL will be returned if this function is
+* invoked with the global error status set, or if it should fail
+* for any reason.
+*/
+
+/* Local Variables: */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = AST__SPEC_NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Test the string against each recognised value in turn and assign
+ the result. */
+ if ( astChrMatch( cvt_string, "FRTOVL" ) ) {
+ result = AST__FRTOVL;
+
+ } else if ( astChrMatch( cvt_string, "VLTOFR" ) ) {
+ result = AST__VLTOFR;
+
+ } else if ( astChrMatch( cvt_string, "VLTOFR" ) ) {
+ result = AST__VLTOFR;
+
+ } else if ( astChrMatch( cvt_string, "ENTOFR" ) ) {
+ result = AST__ENTOFR;
+
+ } else if ( astChrMatch( cvt_string, "FRTOEN" ) ) {
+ result = AST__FRTOEN;
+
+ } else if ( astChrMatch( cvt_string, "WNTOFR" ) ) {
+ result = AST__WNTOFR;
+
+ } else if ( astChrMatch( cvt_string, "FRTOWN" ) ) {
+ result = AST__FRTOWN;
+
+ } else if ( astChrMatch( cvt_string, "WVTOFR" ) ) {
+ result = AST__WVTOFR;
+
+ } else if ( astChrMatch( cvt_string, "FRTOWV" ) ) {
+ result = AST__FRTOWV;
+
+ } else if ( astChrMatch( cvt_string, "AWTOFR" ) ) {
+ result = AST__AWTOFR;
+
+ } else if ( astChrMatch( cvt_string, "FRTOAW" ) ) {
+ result = AST__FRTOAW;
+
+ } else if ( astChrMatch( cvt_string, "VRTOVL" ) ) {
+ result = AST__VRTOVL;
+
+ } else if ( astChrMatch( cvt_string, "VLTOVR" ) ) {
+ result = AST__VLTOVR;
+
+ } else if ( astChrMatch( cvt_string, "VOTOVL" ) ) {
+ result = AST__VOTOVL;
+
+ } else if ( astChrMatch( cvt_string, "VLTOVO" ) ) {
+ result = AST__VLTOVO;
+
+ } else if ( astChrMatch( cvt_string, "ZOTOVL" ) ) {
+ result = AST__ZOTOVL;
+
+ } else if ( astChrMatch( cvt_string, "VLTOZO" ) ) {
+ result = AST__VLTOZO;
+
+ } else if ( astChrMatch( cvt_string, "BTTOVL" ) ) {
+ result = AST__BTTOVL;
+
+ } else if ( astChrMatch( cvt_string, "VLTOBT" ) ) {
+ result = AST__VLTOBT;
+
+ } else if ( astChrMatch( cvt_string, "USF2HL" ) ) {
+ result = AST__USF2HL;
+
+ } else if ( astChrMatch( cvt_string, "HLF2US" ) ) {
+ result = AST__HLF2US;
+
+ } else if ( astChrMatch( cvt_string, "TPF2HL" ) ) {
+ result = AST__TPF2HL;
+
+ } else if ( astChrMatch( cvt_string, "HLF2TP" ) ) {
+ result = AST__HLF2TP;
+
+ } else if ( astChrMatch( cvt_string, "GEF2HL" ) ) {
+ result = AST__GEF2HL;
+
+ } else if ( astChrMatch( cvt_string, "HLF2GE" ) ) {
+ result = AST__HLF2GE;
+
+ } else if ( astChrMatch( cvt_string, "BYF2HL" ) ) {
+ result = AST__BYF2HL;
+
+ } else if ( astChrMatch( cvt_string, "HLF2BY" ) ) {
+ result = AST__HLF2BY;
+
+ } else if ( astChrMatch( cvt_string, "LKF2HL" ) ) {
+ result = AST__LKF2HL;
+
+ } else if ( astChrMatch( cvt_string, "HLF2LK" ) ) {
+ result = AST__HLF2LK;
+
+ } else if ( astChrMatch( cvt_string, "LDF2HL" ) ) {
+ result = AST__LDF2HL;
+
+ } else if ( astChrMatch( cvt_string, "HLF2LD" ) ) {
+ result = AST__HLF2LD;
+
+ } else if ( astChrMatch( cvt_string, "LGF2HL" ) ) {
+ result = AST__LGF2HL;
+
+ } else if ( astChrMatch( cvt_string, "HLF2LG" ) ) {
+ result = AST__HLF2LG;
+
+ } else if ( astChrMatch( cvt_string, "GLF2HL" ) ) {
+ result = AST__GLF2HL;
+
+ } else if ( astChrMatch( cvt_string, "HLF2GL" ) ) {
+ result = AST__HLF2GL;
+
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static const char *CvtString( int cvt_code, const char **comment,
+ int *argra, int *argdec, int *nargs, int *szargs,
+ const char *arg[ MAX_ARGS ], int *status ) {
+/*
+* Name:
+* CvtString
+
+* Purpose:
+* Convert a conversion type from a code value to a string representation.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* const char *CvtString( int cvt_code, const char **comment,
+* int *argra, int *argdec, int *nargs,
+* int *szargs, const char *arg[ MAX_ARGS ], int *status )
+
+* Class Membership:
+* SpecMap member function.
+
+* Description:
+* This function accepts a code value used to represent one of the
+* SpecMap coordinate conversions and converts it into an
+* equivalent string representation. It also returns a descriptive
+* comment and information about the arguments required in order to
+* perform the conversion.
+
+* Parameters:
+* cvt_code
+* The conversion code.
+* comment
+* Address of a location to return a pointer to a constant
+* null-terminated string containing a description of the
+* conversion.
+* argra
+* Address of an int in which to return the index of the argument
+* corresponding to the source RA. Returned equal to -1 if the
+* conversion does not have a source RA argument.
+* argdec
+* Address of an int in which to return the index of the argument
+* corresponding to the source DEC. Returned equal to -1 if the
+* conversion does not have a source DEC argument.
+* nargs
+* Address of an int in which to return the number of arguments
+* required from the user in order to perform the conversion (may
+* be zero).
+* szargs
+* Address of an int in which to return the number of arguments
+* associated with the conversion. This may be bigger than "nargs"
+* if the conversion can pre-calculate useful values on the basis
+* of the user-supplied values. Such precalculated values are
+* stored after the last user-supplied argument.
+* arg
+* An array in which to return a pointer to a constant
+* null-terminated string for each argument (above) containing a
+* description of what each argument represents. This includes both
+* user-supplied arguments and pre-calculated values.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to a constant null-terminated string representation of
+* the conversion code value supplied. If the code supplied is not
+* valid, a NULL pointer will be returned, without error.
+
+* Notes:
+* - A NULL pointer value will be returned if this function is
+* invoked with the global error status set, or if it should fail
+* for any reason.
+*/
+
+/* Local Variables: */
+ const char *result; /* Result pointer to return */
+
+/* Initialise the returned values. */
+ *comment = NULL;
+ *nargs = 0;
+ *argra = -1;
+ *argdec = -1;
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Test for each valid code value in turn and assign the appropriate
+ return values. */
+ switch ( cvt_code ) {
+
+ case AST__FRTOVL:
+ *comment = "Convert frequency to rel. velocity";
+ result = "FRTOVL";
+ *nargs = 1;
+ *szargs = 1;
+ arg[ 0 ] = "Rest frequency (Hz)";
+ break;
+
+ case AST__VLTOFR:
+ *comment = "Convert rel. velocity to frequency";
+ result = "VLTOFR";
+ *nargs = 1;
+ *szargs = 1;
+ arg[ 0 ] = "Rest frequency (Hz)";
+ break;
+
+ case AST__ENTOFR:
+ *comment = "Convert energy to frequency";
+ result = "ENTOFR";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__FRTOEN:
+ *comment = "Convert frequency to energy";
+ result = "FRTOEN";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__WNTOFR:
+ *comment = "Convert wave number to frequency";
+ result = "WNTOFR";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__FRTOWN:
+ *comment = "Convert frequency to wave number";
+ result = "FRTOWN";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__WVTOFR:
+ *comment = "Convert wavelength (vacuum) to frequency";
+ result = "WVTOFR";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__FRTOWV:
+ *comment = "Convert frequency to wavelength (vacuum)";
+ result = "FRTOWV";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__AWTOFR:
+ *comment = "Convert wavelength (air) to frequency";
+ result = "AWTOFR";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__FRTOAW:
+ *comment = "Convert frequency to wavelength (air)";
+ result = "FRTOAW";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__VRTOVL:
+ *comment = "Convert radio to rel. velocity";
+ result = "VRTOVL";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__VLTOVR:
+ *comment = "Convert relativistic to radio velocity";
+ result = "VLTOVR";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__VOTOVL:
+ *comment = "Convert optical to rel. velocity";
+ result = "VOTOVL";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__VLTOVO:
+ *comment = "Convert relativistic to optical velocity";
+ result = "VLTOVO";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__ZOTOVL:
+ *comment = "Convert redshift to rel. velocity";
+ result = "ZOTOVL";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__VLTOZO:
+ *comment = "Convert rel. velocity to redshift";
+ result = "VLTOZO";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__BTTOVL:
+ *comment = "Convert beta factor to rel. velocity";
+ result = "BTTOVL";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__VLTOBT:
+ *comment = "Convert rel. velocity to beta factor";
+ result = "VLTOBT";
+ *nargs = 0;
+ *szargs = 0;
+ break;
+
+ case AST__USF2HL:
+ *comment = "Convert from user-defined to heliocentric frequency";
+ result = "USF2HL";
+ *argra = 1;
+ *argdec = 2;
+ *nargs = 3;
+ *szargs = 4;
+ arg[ 0 ] = "Velocity offset (m/s)";
+ arg[ 1 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 2 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 3 ] = "Frequency correction factor";
+ break;
+
+ case AST__HLF2US:
+ *comment = "Convert from heliocentric to user-defined frequency";
+ result = "HLF2US";
+ *argra = 1;
+ *argdec = 2;
+ *nargs = 3;
+ *szargs = 4;
+ arg[ 0 ] = "Velocity offset (m/s)";
+ arg[ 1 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 2 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 3 ] = "Frequency correction factor";
+ break;
+
+ case AST__TPF2HL:
+ *comment = "Convert from Topocentric to heliocentric frequency";
+ result = "TPF2HL";
+ *argra = 4;
+ *argdec = 5;
+ *nargs = 6;
+ *szargs = 7;
+ arg[ 0 ] = "Longitude (positive eastwards, radians)";
+ arg[ 1 ] = "Latitude (geodetic, radians)";
+ arg[ 2 ] = "Altitude (geodetic, metres)";
+ arg[ 3 ] = "UT1 epoch of observaton (Modified Julian Date)";
+ arg[ 4 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 5 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 6 ] = "Frequency correction factor";
+ break;
+
+ case AST__HLF2TP:
+ *comment = "Convert from Heliocentric to topocentric frequency";
+ result = "HLF2TP";
+ *argra = 4;
+ *argdec = 5;
+ *nargs = 6;
+ *szargs = 7;
+ arg[ 0 ] = "Longitude (positive eastwards, radians)";
+ arg[ 1 ] = "Latitude (geodetic, radians)";
+ arg[ 2 ] = "Altitude (geodetic, metres)";
+ arg[ 3 ] = "UT1 epoch of observaton (Modified Julian Date)";
+ arg[ 4 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 5 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 6 ] = "Frequency correction factor";
+ break;
+
+ case AST__GEF2HL:
+ *comment = "Convert from Geocentric to heliocentric frequency";
+ result = "GEF2HL";
+ *argra = 1;
+ *argdec = 2;
+ *nargs = 3;
+ *szargs = 4;
+ arg[ 0 ] = "UT1 epoch of observaton (Modified Julian Date)";
+ arg[ 1 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 2 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 3 ] = "Frequency correction factor";
+ break;
+
+ case AST__HLF2GE:
+ *comment = "Convert from Heliocentric to geocentric frequency";
+ result = "HLF2GE";
+ *argra = 1;
+ *argdec = 2;
+ *nargs = 3;
+ *szargs = 4;
+ arg[ 0 ] = "UT1 epoch of observaton (Modified Julian Date)";
+ arg[ 1 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 2 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 3 ] = "Frequency correction factor";
+ break;
+
+ case AST__BYF2HL:
+ *comment = "Convert from Barycentric to heliocentric frequency";
+ result = "BYF2HL";
+ *argra = 1;
+ *argdec = 2;
+ *nargs = 3;
+ *szargs = 4;
+ arg[ 0 ] = "UT1 epoch of observaton (Modified Julian Date)";
+ arg[ 1 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 2 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 3 ] = "Frequency correction factor";
+ break;
+
+ case AST__HLF2BY:
+ *comment = "Convert from Heliocentric to barycentric frequency";
+ result = "HLF2BY";
+ *argra = 1;
+ *argdec = 2;
+ *nargs = 3;
+ *szargs = 4;
+ arg[ 0 ] = "UT1 epoch of observaton (Modified Julian Date)";
+ arg[ 1 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 2 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 3 ] = "Frequency correction factor";
+ break;
+
+ case AST__LKF2HL:
+ *comment = "Convert from LSRK to heliocentric frequency";
+ result = "LKF2HL";
+ *argra = 0;
+ *argdec = 1;
+ *nargs = 2;
+ *szargs = 3;
+ arg[ 0 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 1 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 2 ] = "Frequency correction factor";
+ break;
+
+ case AST__HLF2LK:
+ *comment = "Convert from Heliocentric to LSRK frequency";
+ result = "HLF2LK";
+ *argra = 0;
+ *argdec = 1;
+ *nargs = 2;
+ *szargs = 3;
+ arg[ 0 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 1 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 2 ] = "Frequency correction factor";
+ break;
+
+ case AST__LDF2HL:
+ *comment = "Convert from LSRD to heliocentric frequency";
+ result = "LDF2HL";
+ *argra = 0;
+ *argdec = 1;
+ *nargs = 2;
+ *szargs = 3;
+ arg[ 0 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 1 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 2 ] = "Frequency correction factor";
+ break;
+
+ case AST__HLF2LD:
+ *comment = "Convert from Heliocentric to LSRD frequency";
+ result = "HLF2LD";
+ *argra = 0;
+ *argdec = 1;
+ *nargs = 2;
+ *szargs = 3;
+ arg[ 0 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 1 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 2 ] = "Frequency correction factor";
+ break;
+
+ case AST__LGF2HL:
+ *comment = "Convert from Local group to heliocentric frequency";
+ result = "LGF2HL";
+ *argra = 0;
+ *argdec = 1;
+ *nargs = 2;
+ *szargs = 3;
+ arg[ 0 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 1 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 2 ] = "Frequency correction factor";
+ break;
+
+ case AST__HLF2LG:
+ *comment = "Convert from Heliocentric to local group frequency";
+ result = "HLF2LG";
+ *argra = 0;
+ *argdec = 1;
+ *nargs = 2;
+ *szargs = 3;
+ arg[ 0 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 1 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 2 ] = "Frequency correction factor";
+ break;
+
+ case AST__GLF2HL:
+ *comment = "Convert from Galactic to heliocentric frequency";
+ result = "GLF2HL";
+ *argra = 0;
+ *argdec = 1;
+ *nargs = 2;
+ *szargs = 3;
+ arg[ 0 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 1 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 2 ] = "Frequency correction factor";
+ break;
+
+ case AST__HLF2GL:
+ *comment = "Convert from Heliocentric to galactic frequency";
+ *argra = 0;
+ *argdec = 1;
+ result = "HLF2GL";
+ *nargs = 2;
+ *szargs = 3;
+ arg[ 0 ] = "RA of source (FK5 J2000, radians)";
+ arg[ 1 ] = "DEC of source (FK5 J2000, radians)";
+ arg[ 2 ] = "Frequency correction factor";
+ break;
+
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int FrameChange( int cvt_code, int np, double *ra, double *dec, double *freq,
+ double *args, int forward, int *status ){
+/*
+* Name:
+* FrameChange
+
+* Purpose:
+* Apply a doppler shift caused by a change of reference frame.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* int FrameChange( int cvt_code, int np, double *ra, double *dec,
+* double *freq, double *args, int forward, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function modifies the supplied frequency values in order to
+* apply a doppler shift caused by a change of the observers rest-frame.
+
+* Parameters:
+* cvt_code
+* A code indicating the conversion to be applied. If the code does
+* not correspond to a change of rest-frame, then the supplied
+* frequencies are left unchanged and zero is returned as the
+* function value.
+* np
+* The number of frequency values to transform.
+* ra
+* Pointer to an array of "np" RA (J2000 FK5) values at which the
+* "np" frequencies are observed. These are unchanged on exit. If a
+* NULL pointer is supplied, then all frequencies are assumed to be
+* observed at the single RA value given by "refra"
+* dec
+* Pointer to an array of "np" Dec (J2000 FK5) values at which the
+* "np" frequencies are observed. These are unchanged on exit. If a
+* NULL pointer is supplied, then all frequencies are assumed to be
+* observed at the single Dec value given by "refdec"
+* freq
+* Pointer to an array of "np" frequency values, measured in the
+* input rest-frame. These are modified on return to hold the
+* corresponding values measured in the output rest-frame.
+* args
+* Pointer to an array holding the conversion arguments. The number
+* of arguments expected depends on the particular conversion being
+* used.
+* forward
+* Should the conversion be applied in the forward or inverse
+* direction? Non-zero for forward, zero for inverse.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Non-zero if the supplied conversion code corresponds to a change of
+* reference frame. Zoer otherwise (in which case the upplied values
+* will not have been changed).
+
+* Notes:
+* - The "args" array contains RA and DEC values which give the "source"
+* position (FK5 J2000). If a NULL value is supplied for the "ra"
+* parameter, then these args define the position of all the frequency
+* values. In addition they also define the direction of motion of
+* the "user-defined" rest-frame (see "veluser"). Thus they should still
+* be supplied even if "ra" is NULL.
+
+*/
+
+/* Local Variables: */
+ FrameDef def; /* Structure holding frame parameters */
+ double (* cvtFunc)( double, double, FrameDef *, int * ); /* Pointer to conversion function */
+ double *fcorr; /* Pointer to frequency correction factor */
+ double *pdec; /* Pointer to next Dec value */
+ double *pf; /* Pointer to next frequency value */
+ double *pra; /* Pointer to next RA value */
+ double factor; /* Frequency correction factor */
+ double s; /* Velocity correction (m/s) */
+ int i; /* Loop index */
+ int result; /* Returned value */
+ int sign; /* Sign for velocity correction */
+
+/* Check inherited status. */
+ if( !astOK ) return 0;
+
+/* Initialise */
+ cvtFunc = NULL;
+ fcorr = NULL;
+ sign = 0;
+
+/* Set the return value to indicate that the supplied conversion code
+ represents a change of rest-frame. */
+ result = 1;
+
+/* Initialise a structure which stores parameters which define the
+ transformation. */
+ def.obsalt = AST__BAD;
+ def.obslat = AST__BAD;
+ def.obslon = AST__BAD;
+ def.epoch = AST__BAD;
+ def.refdec = AST__BAD;
+ def.refra = AST__BAD;
+ def.veluser = AST__BAD;
+ def.last = AST__BAD;
+ def.amprms[ 0 ] = AST__BAD;
+ def.vuser[ 0 ] = AST__BAD;
+ def.dvh[ 0 ] = AST__BAD;
+ def.dvb[ 0 ] = AST__BAD;
+
+/* Test for each rest-frame code value in turn and assign the appropriate
+ values. */
+ switch ( cvt_code ) {
+
+ case AST__USF2HL:
+ cvtFunc = UserVel;
+ def.veluser = args[ 0 ];
+ def.refra = args[ 1 ];
+ def.refdec = args[ 2 ];
+ fcorr = args + 3;
+ sign = -1;
+ break;
+
+ case AST__HLF2US:
+ cvtFunc = UserVel;
+ def.veluser = args[ 0 ];
+ def.refra = args[ 1 ];
+ def.refdec = args[ 2 ];
+ fcorr = args + 3;
+ sign = +1;
+ break;
+
+ case AST__TPF2HL:
+ cvtFunc = TopoVel;
+ def.obslon = args[ 0 ];
+ def.obslat = args[ 1 ];
+ def.obsalt = args[ 2 ];
+ def.epoch = args[ 3 ];
+ def.refra = args[ 4 ];
+ def.refdec = args[ 5 ];
+ fcorr = args + 6;
+ sign = -1;
+ break;
+
+ case AST__HLF2TP:
+ cvtFunc = TopoVel;
+ def.obslon = args[ 0 ];
+ def.obslat = args[ 1 ];
+ def.obsalt = args[ 2 ];
+ def.epoch = args[ 3 ];
+ def.refra = args[ 4 ];
+ def.refdec = args[ 5 ];
+ fcorr = args + 6;
+ sign = +1;
+ break;
+
+ case AST__GEF2HL:
+ cvtFunc = GeoVel;
+ def.epoch = args[ 0 ];
+ def.refra = args[ 1 ];
+ def.refdec = args[ 2 ];
+ fcorr = args + 3;
+ sign = -1;
+ break;
+
+ case AST__HLF2GE:
+ cvtFunc = GeoVel;
+ def.epoch = args[ 0 ];
+ def.refra = args[ 1 ];
+ def.refdec = args[ 2 ];
+ fcorr = args + 3;
+ sign = +1;
+ break;
+
+ case AST__BYF2HL:
+ cvtFunc = BaryVel;
+ def.epoch = args[ 0 ];
+ def.refra = args[ 1 ];
+ def.refdec = args[ 2 ];
+ fcorr = args + 3;
+ sign = -1;
+ break;
+
+ case AST__HLF2BY:
+ cvtFunc = BaryVel;
+ def.epoch = args[ 0 ];
+ def.refra = args[ 1 ];
+ def.refdec = args[ 2 ];
+ fcorr = args + 3;
+ sign = +1;
+ break;
+
+ case AST__LKF2HL:
+ cvtFunc = LsrkVel;
+ def.refra = args[ 0 ];
+ def.refdec = args[ 1 ];
+ fcorr = args + 2;
+ sign = -1;
+ break;
+
+ case AST__HLF2LK:
+ cvtFunc = LsrkVel;
+ def.refra = args[ 0 ];
+ def.refdec = args[ 1 ];
+ fcorr = args + 2;
+ sign = +1;
+ break;
+
+ case AST__LDF2HL:
+ cvtFunc = LsrdVel;
+ def.refra = args[ 0 ];
+ def.refdec = args[ 1 ];
+ fcorr = args + 2;
+ sign = -1;
+ break;
+
+ case AST__HLF2LD:
+ cvtFunc = LsrdVel;
+ def.refra = args[ 0 ];
+ def.refdec = args[ 1 ];
+ fcorr = args + 2;
+ sign = +1;
+ break;
+
+ case AST__LGF2HL:
+ cvtFunc = LgVel;
+ def.refra = args[ 0 ];
+ def.refdec = args[ 1 ];
+ fcorr = args + 2;
+ sign = -1;
+ break;
+
+ case AST__HLF2LG:
+ cvtFunc = LgVel;
+ def.refra = args[ 0 ];
+ def.refdec = args[ 1 ];
+ fcorr = args + 2;
+ sign = +1;
+ break;
+
+ case AST__GLF2HL:
+ cvtFunc = GalVel;
+ def.refra = args[ 0 ];
+ def.refdec = args[ 1 ];
+ fcorr = args + 2;
+ sign = -1;
+ break;
+
+ case AST__HLF2GL:
+ cvtFunc = GalVel;
+ def.refra = args[ 0 ];
+ def.refdec = args[ 1 ];
+ fcorr = args + 2;
+ sign = +1;
+ break;
+
+/* If the supplied code does not represent a change of rest-frame, clear
+ the returned flag. */
+ default:
+ result = 0;
+ }
+
+/* Check we have a rest-frame code. */
+ if( result ) {
+
+/* First deal with cases where we have a single source position (given by
+ refra and refdec). */
+ if( !ra ) {
+
+/* If the frequency correction factor has not been found, find it now. */
+ if( *fcorr == AST__BAD ) {
+
+/* Get the velocity correction. This is the component of the velocity of the
+ output system, away from the source, as measured in the input system. */
+ s = sign*cvtFunc( def.refra, def.refdec, &def, status );
+
+/* Find the factor by which to correct supplied frequencies. If the
+ velocity correction is positive, the output frequency wil be lower than
+ the input frequency. */
+ if( s < AST__C && s > -AST__C ) {
+ *fcorr = sqrt( ( AST__C - s )/( AST__C + s ) );
+ }
+ }
+
+/* Correct each supplied frequency. */
+ if( *fcorr != AST__BAD && *fcorr != 0.0 ) {
+ factor = forward ? *fcorr : 1.0 / ( *fcorr );
+ pf = freq;
+ for( i = 0; i < np; i++, pf++ ) {
+ if( *pf != AST__BAD ) *pf *= factor;
+ }
+
+/* Set returned values bad if the velocity correction is un-physical. */
+ } else {
+ pf = freq;
+ for( i = 0; i < np; i++ ) *(pf++) = AST__BAD;
+ }
+
+/* Now deal with cases where each frequency value has its own source
+ position. */
+ } else {
+
+/* Invert the sign if we are doing a inverse transformation. */
+ if( !forward ) sign = -sign;
+
+/* Loop round each value. */
+ pf = freq;
+ pra = ra;
+ pdec = dec;
+ for( i = 0; i < np; i++ ) {
+
+/* If the ra or dec is bad, store a bad frequency. */
+ if( *pra == AST__BAD || *pdec == AST__BAD || *pf == AST__BAD ) {
+ *pf = AST__BAD;
+
+/* Otherwise, produce a corrected frequency. */
+ } else {
+
+/* Get the velocity correction. */
+ s = sign*cvtFunc( *pra, *pdec, &def, status );
+
+/* Correct this frequency, if possible. Otherwise set bad. */
+ if( s < AST__C && s > -AST__C ) {
+ *pf *= sqrt( ( AST__C - s )/( AST__C + s ) );
+ } else {
+ *pf = AST__BAD;
+ }
+ }
+
+/* Move on to the next position. */
+ pf++;
+ pra++;
+ pdec++;
+ }
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static double GalVel( double ra, double dec, FrameDef *def, int *status ) {
+/*
+* Name:
+* GalVel
+
+* Purpose:
+* Find the velocity of the galactic centre away from the source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double GalVel( double ra, double dec, FrameDef *def, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function finds the component of the velocity of the galactic
+* centre away from a specified source position, in the frame of rest
+* of the Sun.
+
+* Parameters:
+* ra
+* The RA (rads, FK5 J2000) of the source.
+* dec
+* The Dec (rads, FK5 J2000) of the source.
+* def
+* Pointer to a FrameDef structure which holds the parameters which
+* define the frame, together with cached intermediate results.
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the frame's velocity away from the position given by
+* "ra" and "dec", in m/s, measured within the Heliographic frame of
+* rest. Zero is returned if an error has already occurred.
+
+*/
+
+/* Local Variables: */
+ double s1, s2;
+
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* Get the component away from the source, of the velocity of the sun
+ relative to the dynamic LSR (in km/s). */
+ s1 = (double) palRvlsrd( (float) ra, (float) dec );
+
+/* Get the component away from the source, of the velocity of the
+ dynamic LSR relative to the galactic centre (in km/s). */
+ s2 = (double) palRvgalc( (float) ra, (float) dec );
+
+/* Return the total velocity of the galactic centre away from the source,
+ relative to the sun, in m/s. */
+ return -1000.0*( s1 + s2 );
+}
+
+static double GeoVel( double ra, double dec, FrameDef *def, int *status ) {
+/*
+* Name:
+* GeoVel
+
+* Purpose:
+* Find the velocity of the earth away from the source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double GeoVel( double ra, double dec, FrameDef *def, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function finds the component of the velocity of the earth away
+* from a specified source position, at a given epoch, in the frame of
+* rest of the Sun.
+
+* Parameters:
+* ra
+* The RA (rads, FK5 J2000) of the source.
+* dec
+* The Dec (rads, FK5 J2000) of the source.
+* def
+* Pointer to a FrameDef structure which holds the parameters which
+* define the frame, together with cached intermediate results.
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the frame's velocity away from the position given by
+* "ra" and "dec", in m/s, measured within the Heliographic frame of
+* rest. Zero is returned if an error has already occurred.
+
+*/
+
+/* Local Variables: */
+ double dpb[ 3 ]; /* Barycentric earth position vector */
+ double dph[ 3 ]; /* Heliocentric earth position vector */
+ double dvb[ 3 ]; /* Barycentric earth velocity vector */
+ double v[ 3 ]; /* Source direction vector */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* Get the Cartesian vector towards the source, in the Cartesian FK5
+ J2000 system. */
+ palDcs2c( ra, dec, v );
+
+/* If not already done so, get the Earth/Sun velocity and position vectors in
+ the same system. Speed is returned in units of AU/s. Store in the supplied
+ frame definition structure. */
+ if( def->dvh[ 0 ] == AST__BAD ) palEvp( def->epoch, 2000.0, dvb, dpb,
+ def->dvh, dph );
+
+/* Return the component away from the source, of the velocity of the earths
+ centre relative to the sun (in m/s). */
+ return -palDvdv( v, def->dvh )*149.597870E9;
+}
+
+void astInitSpecMapVtab_( AstSpecMapVtab *vtab, const char *name, int *status ) {
+/*
+*+
+* Name:
+* astInitSpecMapVtab
+
+* Purpose:
+* Initialise a virtual function table for a SpecMap.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "specmap.h"
+* void astInitSpecMapVtab( AstSpecMapVtab *vtab, const char *name )
+
+* Class Membership:
+* SpecMap vtab initialiser.
+
+* Description:
+* This function initialises the component of a virtual function
+* table which is used by the SpecMap class.
+
+* Parameters:
+* vtab
+* Pointer to the virtual function table. The components used by
+* all ancestral classes will be initialised if they have not already
+* been initialised.
+* name
+* Pointer to a constant null-terminated character string which contains
+* the name of the class to which the virtual function table belongs (it
+* is this pointer value that will subsequently be returned by the Object
+* astClass function).
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstObjectVtab *object; /* Pointer to Object component of Vtab */
+ AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
+
+/* Check the local error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Initialize the component of the virtual function table used by the
+ parent class. */
+ astInitMappingVtab( (AstMappingVtab *) vtab, name );
+
+/* Store a unique "magic" value in the virtual function table. This
+ will be used (by astIsASpecMap) to determine if an object belongs to
+ this class. We can conveniently use the address of the (static)
+ class_check variable to generate this unique value. */
+ vtab->id.check = &class_check;
+ vtab->id.parent = &(((AstMappingVtab *) vtab)->id);
+
+/* Initialise member function pointers. */
+/* ------------------------------------ */
+/* Store pointers to the member functions (implemented here) that
+ provide virtual methods for this class. */
+ vtab->SpecAdd = SpecAdd;
+
+/* Save the inherited pointers to methods that will be extended, and
+ replace them with pointers to the new member functions. */
+ object = (AstObjectVtab *) vtab;
+ mapping = (AstMappingVtab *) vtab;
+ parent_getobjsize = object->GetObjSize;
+ object->GetObjSize = GetObjSize;
+
+ parent_transform = mapping->Transform;
+ mapping->Transform = Transform;
+
+ parent_rate = mapping->Rate;
+ mapping->Rate = Rate;
+
+/* Store replacement pointers for methods which will be over-ridden by
+ new member functions implemented here. */
+ object->Equal = Equal;
+ mapping->MapMerge = MapMerge;
+
+/* Declare the copy constructor, destructor and class dump
+ function. */
+ astSetCopy( vtab, Copy );
+ astSetDelete( vtab, Delete );
+ astSetDump( vtab, Dump, "SpecMap",
+ "Conversion between spectral coordinate systems" );
+
+/* If we have just initialised the vtab for the current class, indicate
+ that the vtab is now initialised, and store a pointer to the class
+ identifier in the base "object" level of the vtab. */
+ if( vtab == &class_vtab ) {
+ class_init = 1;
+ astSetVtabClassIdentifier( vtab, &(vtab->id) );
+ }
+}
+
+static double LgVel( double ra, double dec, FrameDef *def, int *status ) {
+/*
+* Name:
+* LgVel
+
+* Purpose:
+* Find the velocity of the Local Group away from the source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double LgVel( double ra, double dec, FrameDef *def, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function finds the component of the Local Group velocity away
+* from a specified source position, in the frame of rest of the Sun.
+
+* Parameters:
+* ra
+* The RA (rads, FK5 J2000) of the source.
+* dec
+* The Dec (rads, FK5 J2000) of the source.
+* def
+* Pointer to a FrameDef structure which holds the parameters which
+* define the frame, together with cached intermediate results.
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the frame's velocity away from the position given by
+* "ra" and "dec", in m/s, measured within the Heliographic frame of
+* rest. Zero is returned if an error has already occurred.
+
+*/
+
+/* Return the component away from the source, of the velocity of the
+ local group relative to the sun (in m/s). */
+ return -1000.0*palRvlg( (float) ra, (float) dec );
+}
+
+static double LsrdVel( double ra, double dec, FrameDef *def, int *status ) {
+/*
+* Name:
+* LsrdVel
+
+* Purpose:
+* Find the velocity of the Dynamical LSR away from the source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double LsrdVel( double ra, double dec, FrameDef *def, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function finds the component of the velocity of the Dynamical
+* LSR away from a specified source position, in the frame of rest of
+* the Sun.
+
+* Parameters:
+* ra
+* The RA (rads, FK5 J2000) of the source.
+* dec
+* The Dec (rads, FK5 J2000) of the source.
+* def
+* Pointer to a FrameDef structure which holds the parameters which
+* define the frame, together with cached intermediate results.
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the frame's velocity away from the position given by
+* "ra" and "dec", in m/s, measured within the Heliographic frame of
+* rest. Zero is returned if an error has already occurred.
+
+*/
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* Get the component away from the source, of the velocity of the sun
+ relative to the dynamical LSR (in m/s). This can also be thought of as the
+ velocity of the LSR towards the source relative to the sun. Return the
+ negated value (i.e. velocity of lsrd *away from* the source. */
+ return -1000.0*palRvlsrd( (float) ra, (float) dec );
+}
+
+static double LsrkVel( double ra, double dec, FrameDef *def, int *status ) {
+/*
+* Name:
+* LsrkVel
+
+* Purpose:
+* Find the velocity of the Kinematic LSR away from the source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double LsrkVel( double ra, double dec, FrameDef *def, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function finds the component of the velocity of the Kinematic
+* LSR away from a specified source position, in the frame of rest of
+* the Sun.
+
+* Parameters:
+* ra
+* The RA (rads, FK5 J2000) of the source.
+* dec
+* The Dec (rads, FK5 J2000) of the source.
+* def
+* Pointer to a FrameDef structure which holds the parameters which
+* define the frame, together with cached intermediate results.
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the frame's velocity away from the position given by
+* "ra" and "dec", in m/s, measured within the Heliographic frame of
+* rest. Zero is returned if an error has already occurred.
+
+*/
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* Get the component away from the source, of the velocity of the sun
+ relative to the kinematic LSR (in m/s). This can also be thought of as the
+ velocity of the LSR towards the source relative to the sun. Return the
+ negated value (i.e. velocity of lsrk *away from* the source. */
+ return -1000.0*palRvlsrk( (float) ra, (float) dec );
+}
+
+static int MapMerge( AstMapping *this, int where, int series, int *nmap,
+ AstMapping ***map_list, int **invert_list, int *status ) {
+/*
+* Name:
+* MapMerge
+
+* Purpose:
+* Simplify a sequence of Mappings containing a SpecMap.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "mapping.h
+* int MapMerge( AstMapping *this, int where, int series, int *nmap,
+* AstMapping ***map_list, int **invert_list, int *status )
+
+* Class Membership:
+* SpecMap method (over-rides the protected astMapMerge method
+* inherited from the Mapping class).
+
+* Description:
+* This function attempts to simplify a sequence of Mappings by
+* merging a nominated SpecMap in the sequence with its neighbours,
+* so as to shorten the sequence if possible.
+*
+* In many cases, simplification will not be possible and the
+* function will return -1 to indicate this, without further
+* action.
+*
+* In most cases of interest, however, this function will either
+* attempt to replace the nominated SpecMap with one which it
+* considers simpler, or to merge it with the Mappings which
+* immediately precede it or follow it in the sequence (both will
+* normally be considered). This is sufficient to ensure the
+* eventual simplification of most Mapping sequences by repeated
+* application of this function.
+*
+* In some cases, the function may attempt more elaborate
+* simplification, involving any number of other Mappings in the
+* sequence. It is not restricted in the type or scope of
+* simplification it may perform, but will normally only attempt
+* elaborate simplification in cases where a more straightforward
+* approach is not adequate.
+
+* Parameters:
+* this
+* Pointer to the nominated SpecMap which is to be merged with
+* its neighbours. This should be a cloned copy of the SpecMap
+* pointer contained in the array element "(*map_list)[where]"
+* (see below). This pointer will not be annulled, and the
+* SpecMap it identifies will not be modified by this function.
+* where
+* Index in the "*map_list" array (below) at which the pointer
+* to the nominated SpecMap resides.
+* series
+* A non-zero value indicates that the sequence of Mappings to
+* be simplified will be applied in series (i.e. one after the
+* other), whereas a zero value indicates that they will be
+* applied in parallel (i.e. on successive sub-sets of the
+* input/output coordinates).
+* nmap
+* Address of an int which counts the number of Mappings in the
+* sequence. On entry this should be set to the initial number
+* of Mappings. On exit it will be updated to record the number
+* of Mappings remaining after simplification.
+* map_list
+* Address of a pointer to a dynamically allocated array of
+* Mapping pointers (produced, for example, by the astMapList
+* method) which identifies the sequence of Mappings. On entry,
+* the initial sequence of Mappings to be simplified should be
+* supplied.
+*
+* On exit, the contents of this array will be modified to
+* reflect any simplification carried out. Any form of
+* simplification may be performed. This may involve any of: (a)
+* removing Mappings by annulling any of the pointers supplied,
+* (b) replacing them with pointers to new Mappings, (c)
+* inserting additional Mappings and (d) changing their order.
+*
+* The intention is to reduce the number of Mappings in the
+* sequence, if possible, and any reduction will be reflected in
+* the value of "*nmap" returned. However, simplifications which
+* do not reduce the length of the sequence (but improve its
+* execution time, for example) may also be performed, and the
+* sequence might conceivably increase in length (but normally
+* only in order to split up a Mapping into pieces that can be
+* more easily merged with their neighbours on subsequent
+* invocations of this function).
+*
+* If Mappings are removed from the sequence, any gaps that
+* remain will be closed up, by moving subsequent Mapping
+* pointers along in the array, so that vacated elements occur
+* at the end. If the sequence increases in length, the array
+* will be extended (and its pointer updated) if necessary to
+* accommodate any new elements.
+*
+* Note that any (or all) of the Mapping pointers supplied in
+* this array may be annulled by this function, but the Mappings
+* to which they refer are not modified in any way (although
+* they may, of course, be deleted if the annulled pointer is
+* the final one).
+* invert_list
+* Address of a pointer to a dynamically allocated array which,
+* on entry, should contain values to be assigned to the Invert
+* attributes of the Mappings identified in the "*map_list"
+* array before they are applied (this array might have been
+* produced, for example, by the astMapList method). These
+* values will be used by this function instead of the actual
+* Invert attributes of the Mappings supplied, which are
+* ignored.
+*
+* On exit, the contents of this array will be updated to
+* correspond with the possibly modified contents of the
+* "*map_list" array. If the Mapping sequence increases in
+* length, the "*invert_list" array will be extended (and its
+* pointer updated) if necessary to accommodate any new
+* elements.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* If simplification was possible, the function returns the index
+* in the "map_list" array of the first element which was
+* modified. Otherwise, it returns -1 (and makes no changes to the
+* arrays supplied).
+
+* Notes:
+* - A value of -1 will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*/
+
+/* Local Variables: */
+ AstMapping *new; /* Pointer to replacement Mapping */
+ AstSpecMap *specmap; /* Pointer to SpecMap */
+ const char *argdesc[ MAX_ARGS ]; /* Argument descriptions (junk) */
+ const char *class; /* Pointer to Mapping class string */
+ const char *comment; /* Pointer to comment string (junk) */
+ double (*cvtargs)[ MAX_ARGS ]; /* Pointer to argument arrays */
+ double tmp; /* Temporary storage */
+ int *cvttype; /* Pointer to transformation type codes */
+ int *szarg; /* Pointer to argument count array */
+ int argdec; /* Index of DEC argument */
+ int argra; /* Index of RA argument */
+ int done; /* Finished (no further simplification)? */
+ int iarg; /* Loop counter for arguments */
+ int icvt1; /* Loop initial value */
+ int icvt2; /* Loop final value */
+ int icvt; /* Loop counter for transformation steps */
+ int ikeep; /* Index to store step being kept */
+ int imap1; /* Index of first SpecMap to merge */
+ int imap2; /* Index of last SpecMap to merge */
+ int imap; /* Loop counter for Mappings */
+ int inc; /* Increment for transformation step loop */
+ int invert; /* SpecMap applied in inverse direction? */
+ int istep; /* Loop counter for transformation steps */
+ int keep; /* Keep transformation step? */
+ int narg; /* Number of user-supplied arguments */
+ int ngone; /* Number of Mappings eliminated */
+ int nin; /* Numbr of axes for SpecMaps being merged */
+ int nstep0; /* Original number of transformation steps */
+ int nstep; /* Total number of transformation steps */
+ int result; /* Result value to return */
+ int simpler; /* Simplification possible? */
+ int unit; /* Replacement Mapping is a UnitMap? */
+
+/* Initialise. */
+ result = -1;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* SpecMaps can only be merged if they are in series (or if there is
+ only one Mapping present, in which case it makes no difference), so
+ do nothing if they are not. */
+ if ( series || ( *nmap == 1 ) ) {
+
+/* Save the number of inputs for the SpecMap. */
+ nin = astGetNin( this );
+
+/* Initialise the number of transformation steps to be merged to equal
+ the number in the nominated SpecMap. */
+ nstep = ( (AstSpecMap *) ( *map_list )[ where ] )->ncvt;
+
+/* Search adjacent lower-numbered Mappings until one is found which is
+ not a SpecMap, or is a SpecMap with a different number of axes. Accumulate
+ the number of transformation steps involved in any SpecMaps found. */
+ imap1 = where;
+ while ( ( imap1 - 1 >= 0 ) && astOK ) {
+ class = astGetClass( ( *map_list )[ imap1 - 1 ] );
+ if ( !astOK || strcmp( class, "SpecMap" ) ||
+ astGetNin( ( *map_list )[ imap1 - 1 ] ) != nin ) break;
+ nstep += ( (AstSpecMap *) ( *map_list )[ imap1 - 1 ] )->ncvt;
+ imap1--;
+ }
+
+/* Similarly search adjacent higher-numbered Mappings. */
+ imap2 = where;
+ while ( ( imap2 + 1 < *nmap ) && astOK ) {
+ class = astGetClass( ( *map_list )[ imap2 + 1 ] );
+ if ( !astOK || strcmp( class, "SpecMap" ) ||
+ astGetNin( ( *map_list )[ imap2 + 1 ] ) != nin ) break;
+ nstep += ( (AstSpecMap *) ( *map_list )[ imap2 + 1 ] )->ncvt;
+ imap2++;
+ }
+
+/* Remember the initial number of transformation steps. */
+ nstep0 = nstep;
+
+/* Allocate memory for accumulating a list of all the transformation
+ steps involved in all the SpecMaps found. */
+ cvttype = astMalloc( sizeof( int ) * (size_t) nstep );
+ cvtargs = astMalloc( sizeof( double[ MAX_ARGS ] ) * (size_t) nstep );
+ szarg = astMalloc( sizeof( int ) * (size_t) nstep );
+
+/* Loop to obtain the transformation data for each SpecMap being merged. */
+ nstep = 0;
+ for ( imap = imap1; astOK && ( imap <= imap2 ); imap++ ) {
+
+/* Obtain a pointer to the SpecMap and note if it is being applied in
+ its inverse direction. */
+ specmap = (AstSpecMap *) ( *map_list )[ imap ];
+ invert = ( *invert_list )[ imap ];
+
+/* Set up loop limits and an increment to scan the transformation
+ steps in each SpecMap in either the forward or reverse direction, as
+ dictated by the associated "invert" value. */
+ icvt1 = invert ? specmap->ncvt - 1 : 0;
+ icvt2 = invert ? -1 : specmap->ncvt;
+ inc = invert ? -1 : 1;
+
+/* Loop through each transformation step in the SpecMap. */
+ for ( icvt = icvt1; icvt != icvt2; icvt += inc ) {
+
+/* Store the transformation type code and use "CvtString" to determine
+ the associated number of arguments. Then store these arguments. */
+ cvttype[ nstep ] = specmap->cvttype[ icvt ];
+ (void) CvtString( cvttype[ nstep ], &comment, &argra, &argdec,
+ &narg, szarg + nstep, argdesc, status );
+ if ( !astOK ) break;
+ for ( iarg = 0; iarg < szarg[ nstep ]; iarg++ ) {
+ cvtargs[ nstep ][ iarg ] = specmap->cvtargs[ icvt ][ iarg ];
+ }
+
+/* If the SpecMap is inverted, we must not only accumulate its
+ transformation steps in reverse, but also apply them in
+ reverse. For some steps this means changing arguments, for some it
+ means changing the transformation type code to a complementary
+ value, and for others it means both. Define macros to perform each
+ of the required changes. */
+
+/* Macro to exchange a transformation type code for its inverse (and
+ vice versa). */
+#define SWAP_CODES( code1, code2 ) \
+ if ( cvttype[ nstep ] == code1 ) { \
+ cvttype[ nstep ] = code2; \
+ } else if ( cvttype[ nstep ] == code2 ) { \
+ cvttype[ nstep ] = code1; \
+ }
+
+/* Macro to exchange a transformation type code for its inverse (and
+ vice versa), and reciprocate a specified argument. */
+#define SWAP_CODES2( code1, code2, jarg ) \
+ if ( cvttype[ nstep ] == code1 ) { \
+ cvttype[ nstep ] = code2; \
+ tmp = cvtargs[ nstep ][ jarg ]; \
+ if( tmp != AST__BAD && tmp != 0.0 ) { \
+ cvtargs[ nstep ][ jarg ] = 1.0/tmp; \
+ } else { \
+ cvtargs[ nstep ][ jarg ] = AST__BAD; \
+ } \
+ } else if ( cvttype[ nstep ] == code2 ) { \
+ cvttype[ nstep ] = code1; \
+ tmp = cvtargs[ nstep ][ jarg ]; \
+ if( tmp != AST__BAD && tmp != 0.0 ) { \
+ cvtargs[ nstep ][ jarg ] = 1.0/tmp; \
+ } else { \
+ cvtargs[ nstep ][ jarg ] = AST__BAD; \
+ } \
+ }
+
+/* Macro to exchange a transformation type code for its inverse (and
+ vice versa), and negate a specified argument. */
+#define SWAP_CODES3( code1, code2, jarg ) \
+ if ( cvttype[ nstep ] == code1 ) { \
+ cvttype[ nstep ] = code2; \
+ tmp = cvtargs[ nstep ][ jarg ]; \
+ if( tmp != AST__BAD ) { \
+ cvtargs[ nstep ][ jarg ] = -tmp; \
+ } \
+ } else if ( cvttype[ nstep ] == code2 ) { \
+ cvttype[ nstep ] = code1; \
+ tmp = cvtargs[ nstep ][ jarg ]; \
+ if( tmp != AST__BAD ) { \
+ cvtargs[ nstep ][ jarg ] = -tmp; \
+ } \
+ }
+
+/* Use these macros to apply the changes where needed. */
+ if ( invert ) {
+
+/* Exchange transformation codes for their inverses. */
+ SWAP_CODES( AST__FRTOVL, AST__VLTOFR )
+ SWAP_CODES( AST__ENTOFR, AST__FRTOEN )
+ SWAP_CODES( AST__WNTOFR, AST__FRTOWN )
+ SWAP_CODES( AST__WVTOFR, AST__FRTOWV )
+ SWAP_CODES( AST__AWTOFR, AST__FRTOAW )
+ SWAP_CODES( AST__VRTOVL, AST__VLTOVR )
+ SWAP_CODES( AST__VOTOVL, AST__VLTOVO )
+ SWAP_CODES( AST__ZOTOVL, AST__VLTOZO )
+ SWAP_CODES( AST__BTTOVL, AST__VLTOBT )
+
+/* Exchange transformation codes for their inverses, and reciprocate the
+ frequency correction factor. */
+ SWAP_CODES2( AST__TPF2HL, AST__HLF2TP, 6 )
+ SWAP_CODES2( AST__USF2HL, AST__HLF2US, 3 )
+ SWAP_CODES2( AST__GEF2HL, AST__HLF2GE, 3 )
+ SWAP_CODES2( AST__BYF2HL, AST__HLF2BY, 3 )
+ SWAP_CODES2( AST__LKF2HL, AST__HLF2LK, 2 )
+ SWAP_CODES2( AST__LDF2HL, AST__HLF2LD, 2 )
+ SWAP_CODES2( AST__LGF2HL, AST__HLF2LG, 2 )
+ SWAP_CODES2( AST__GLF2HL, AST__HLF2GL, 2 )
+
+ }
+
+/* Undefine the local macros. */
+#undef SWAP_CODES
+#undef SWAP_CODES2
+#undef SWAP_CODES3
+
+/* Count the transformation steps. */
+ nstep++;
+ }
+ }
+
+/* Loop to simplify the sequence of transformation steps until no
+ further improvement is possible. */
+ done = 0;
+ while ( astOK && !done ) {
+
+/* Examine each remaining transformation step in turn. */
+ ikeep = -1;
+ for ( istep = 0; istep < nstep; istep++ ) {
+
+/* Initially assume we will retain the current step. */
+ keep = 1;
+
+/* The only simplifications for the conversions currently in this class act
+ to combine adjacent transformation steps, so only apply them while there
+ are at least 2 steps left. */
+ if ( istep < ( nstep - 1 ) ) {
+
+/* Define a macro to test if two adjacent transformation type codes
+ have specified values. */
+#define PAIR_CVT( code1, code2 ) \
+ ( ( cvttype[ istep ] == code1 ) && \
+ ( cvttype[ istep + 1 ] == code2 ) )
+
+/* Define a macro to test if two adjacent transformation type codes
+ have specified values, either way round. */
+#define PAIR_CVT2( code1, code2 ) \
+ ( ( PAIR_CVT( code1, code2 ) ) || \
+ ( PAIR_CVT( code2, code1 ) ) )
+
+/* If a correction is followed by its inverse, and the user-supplied argument
+ values are unchanged (we do not need to test values stored in the
+ argument array which were not supplied by the user), we can eliminate them.
+ First check for conversions which have no user-supplied arguments. */
+ if ( PAIR_CVT2( AST__ENTOFR, AST__FRTOEN ) ||
+ PAIR_CVT2( AST__WNTOFR, AST__FRTOWN ) ||
+ PAIR_CVT2( AST__WVTOFR, AST__FRTOWV ) ||
+ PAIR_CVT2( AST__AWTOFR, AST__FRTOAW ) ||
+ PAIR_CVT2( AST__VRTOVL, AST__VLTOVR ) ||
+ PAIR_CVT2( AST__VOTOVL, AST__VLTOVO ) ||
+ PAIR_CVT2( AST__ZOTOVL, AST__VLTOZO ) ||
+ PAIR_CVT2( AST__BTTOVL, AST__VLTOBT ) ) {
+ istep++;
+ keep = 0;
+
+/* Now check for conversions which have a single user-supplied argument. */
+ } else if( PAIR_CVT2( AST__FRTOVL, AST__VLTOFR ) &&
+ astEQUAL( cvtargs[ istep ][ 0 ],
+ cvtargs[ istep + 1 ][ 0 ] ) ) {
+ istep++;
+ keep = 0;
+
+/* Now check for conversions which have two user-supplied arguments. */
+ } else if( ( PAIR_CVT2( AST__LKF2HL, AST__HLF2LK ) ||
+ PAIR_CVT2( AST__LDF2HL, AST__HLF2LD ) ||
+ PAIR_CVT2( AST__LGF2HL, AST__HLF2LG ) ||
+ PAIR_CVT2( AST__GLF2HL, AST__HLF2GL ) ) &&
+ astEQUAL( cvtargs[ istep ][ 0 ],
+ cvtargs[ istep + 1 ][ 0 ] ) &&
+ astEQUAL( cvtargs[ istep ][ 1 ],
+ cvtargs[ istep + 1 ][ 1 ] ) ) {
+ istep++;
+ keep = 0;
+
+/* Now check for conversions which have three user-supplied arguments. */
+ } else if( ( PAIR_CVT2( AST__GEF2HL, AST__HLF2GE ) ||
+ PAIR_CVT2( AST__BYF2HL, AST__HLF2BY ) ||
+ PAIR_CVT2( AST__USF2HL, AST__HLF2US ) ) &&
+ astEQUAL( cvtargs[ istep ][ 0 ],
+ cvtargs[ istep + 1 ][ 0 ] ) &&
+ astEQUAL( cvtargs[ istep ][ 1 ],
+ cvtargs[ istep + 1 ][ 1 ] ) &&
+ astEQUAL( cvtargs[ istep ][ 2 ],
+ cvtargs[ istep + 1 ][ 2 ] ) ) {
+ istep++;
+ keep = 0;
+
+/* Now check for conversions which have six user-supplied arguments (currently
+ no conversions have four or five user-supplied arguments). */
+ } else if( ( PAIR_CVT2( AST__TPF2HL, AST__HLF2TP ) ) &&
+ astEQUAL( cvtargs[ istep ][ 0 ],
+ cvtargs[ istep + 1 ][ 0 ] ) &&
+ astEQUAL( cvtargs[ istep ][ 1 ],
+ cvtargs[ istep + 1 ][ 1 ] ) &&
+ astEQUAL( cvtargs[ istep ][ 2 ],
+ cvtargs[ istep + 1 ][ 2 ] ) &&
+ astEQUAL( cvtargs[ istep ][ 3 ],
+ cvtargs[ istep + 1 ][ 3 ] ) &&
+ astEQUAL( cvtargs[ istep ][ 4 ],
+ cvtargs[ istep + 1 ][ 4 ] ) &&
+ astEQUAL( cvtargs[ istep ][ 5 ],
+ cvtargs[ istep + 1 ][ 5 ] ) ) {
+ istep++;
+ keep = 0;
+
+ }
+
+/* Undefine the local macros. */
+#undef PAIR_CVT
+#undef PAIR_CVT2
+ }
+
+/* If the current transformation (possibly modified above) is being
+ kept, then increment the index that identifies its new location in
+ the list of transformation steps. */
+ if ( keep ) {
+ ikeep++;
+
+/* If the new location is different to its current location, copy the
+ transformation data into the new location. */
+ if ( ikeep != istep ) {
+ cvttype[ ikeep ] = cvttype[ istep ];
+ for ( iarg = 0; iarg < szarg[ istep ]; iarg++ ) {
+ cvtargs[ ikeep ][ iarg ] = cvtargs[ istep ][ iarg ];
+ }
+ szarg[ ikeep ] = szarg[ istep ];
+ }
+ }
+ }
+
+/* Note if no simplification was achieved on this iteration (i.e. the
+ number of transformation steps was not reduced). This is the signal
+ to quit. */
+ done = ( ( ikeep + 1 ) >= nstep );
+
+/* Note how many transformation steps now remain. */
+ nstep = ikeep + 1;
+ }
+
+/* Determine how many Mappings can be eliminated by condensing all
+ those considered above into a single Mapping. */
+ if ( astOK ) {
+ ngone = imap2 - imap1;
+
+/* Determine if the replacement Mapping can be a UnitMap (a null
+ Mapping). This will only be the case if all the transformation
+ steps were eliminated above. */
+ unit = ( nstep == 0 );
+
+/* Determine if simplification is possible. This will be the case if
+ (a) Mappings were eliminated ("ngone" is non-zero), or (b) the
+ number of transformation steps was reduced, or (c) the SpecMap(s)
+ can be replaced by a UnitMap, or (d) if there was initially only
+ one SpecMap present, its invert flag was set (this flag will always
+ be cleared in the replacement Mapping). */
+ simpler = ngone || ( nstep < nstep0 ) || unit ||
+ ( *invert_list )[ where ];
+
+/* Do nothing more unless simplification is possible. */
+ if ( simpler ) {
+
+/* If the replacement Mapping is a UnitMap, then create it. */
+ if ( unit ) {
+ new = (AstMapping *)
+ astUnitMap( astGetNin( ( *map_list )[ where ] ), "", status );
+
+/* Otherwise, create a replacement SpecMap and add each of the
+ remaining transformation steps to it. */
+ } else {
+ new = (AstMapping *) astSpecMap( nin, 0, "", status );
+ for ( istep = 0; istep < nstep; istep++ ) {
+ AddSpecCvt( (AstSpecMap *) new, cvttype[ istep ],
+ cvtargs[ istep ], status );
+ }
+ }
+
+/* Annul the pointers to the Mappings being eliminated. */
+ if ( astOK ) {
+ for ( imap = imap1; imap <= imap2; imap++ ) {
+ ( *map_list )[ imap ] = astAnnul( ( *map_list )[ imap ] );
+ }
+
+/* Insert the pointer and invert value for the new Mapping. */
+ ( *map_list )[ imap1 ] = new;
+ ( *invert_list )[ imap1 ] = 0;
+
+/* Move any subsequent Mapping information down to close the gap. */
+ for ( imap = imap2 + 1; imap < *nmap; imap++ ) {
+ ( *map_list )[ imap - ngone ] = ( *map_list )[ imap ];
+ ( *invert_list )[ imap - ngone ] = ( *invert_list )[ imap ];
+ }
+
+/* Blank out any information remaining at the end of the arrays. */
+ for ( imap = ( *nmap - ngone ); imap < *nmap; imap++ ) {
+ ( *map_list )[ imap ] = NULL;
+ ( *invert_list )[ imap ] = 0;
+ }
+
+/* Decrement the Mapping count and return the index of the first
+ Mapping which was eliminated. */
+ ( *nmap ) -= ngone;
+ result = imap1;
+
+/* If an error occurred, annul the new Mapping pointer. */
+ } else {
+ new = astAnnul( new );
+ }
+ }
+ }
+
+/* Free the memory used for the transformation steps. */
+ cvttype = astFree( cvttype );
+ cvtargs = astFree( cvtargs );
+ szarg = astFree( szarg );
+ }
+
+/* If an error occurred, clear the returned value. */
+ if ( !astOK ) result = -1;
+
+/* Return the result. */
+ return result;
+}
+
+static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){
+/*
+* Name:
+* Rate
+
+* Purpose:
+* Calculate the rate of change of a Mapping output.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status )
+
+* Class Membership:
+* SpecMap member function (overrides the astRate method inherited
+* from the Mapping class ).
+
+* Description:
+* This function returns the rate of change of a specified output of
+* the supplied Mapping with respect to a specified input, at a
+* specified input position.
+
+* Parameters:
+* this
+* Pointer to the Mapping to be applied.
+* at
+* The address of an array holding the axis values at the position
+* at which the rate of change is to be evaluated. The number of
+* elements in this array should equal the number of inputs to the
+* Mapping.
+* ax1
+* The index of the Mapping output for which the rate of change is to
+* be found (output numbering starts at 0 for the first output).
+* ax2
+* The index of the Mapping input which is to be varied in order to
+* find the rate of change (input numbering starts at 0 for the first
+* input).
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The rate of change of Mapping output "ax1" with respect to input
+* "ax2", evaluated at "at", or AST__BAD if the value cannot be
+* calculated.
+
+* Implementation Deficiencies:
+* The initial version of this implementation only deals with
+* frequency->wavelength conversions. This is because the slowness of
+* the numerical differentiation implemented by the astRate method in
+* the parent Mapping class is cripples conversion between SpecFluxFrames.
+* Such conversions only rely on rate of change of wavelength with
+* respect to frequency. This implementation should be extended when
+* needed.
+
+*/
+
+/* Local Variables: */
+ AstSpecMap *map;
+ double result;
+ int cvt;
+
+/* Check inherited status */
+ if( !astOK ) return AST__BAD;
+
+/* Get a pointer to the SpecMap structure. */
+ map = (AstSpecMap *) this;
+
+/* Return 1.0 if the SpecMap has no conversions. */
+ if( map->ncvt == 0 ) return 1.0;
+
+/* Store the type of the first conversion.*/
+ cvt = map->cvttype[ 0 ];
+
+/* If this is a 3D SpecMap or if it has more than one component, or if
+ that conversion is not between frequency and wavelength, use the
+ astRate method inherited form the parent Mapping class. */
+ if( astGetNin( map ) != 1 || map->ncvt != 1 ||
+ ( cvt != AST__WVTOFR && cvt != AST__FRTOWV ) ) {
+ result = (*parent_rate)( this, at, ax1, ax2, status );
+
+/* Otherwise, evaluate the known analytical expressions for the rate of
+ change of frequency with respect to wavelength or wavelength with
+ respect to frequency. */
+ } else {
+ result = ( *at != AST__BAD ) ? -AST__C/((*at)*(*at)) : AST__BAD;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static double Refrac( double wavelen, int *status ){
+/*
+* Name:
+* Refrac
+
+* Purpose:
+* Returns the refractive index of dry air at a given wavelength.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double Refrac( double wavelen, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function returns the refractive index of dry air at standard
+* temperature and pressure, at a given wavelength. The formula is
+* taken from the paper "Representation of Spectral Coordinates in FITS"
+* (Greisen et al).
+
+* Parameters:
+* wavelen
+* The wavelength, in metres. This should be the air wavelength,
+* but supplying the vacuum wavelength will make no significant
+* difference.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The refractive index. A value of 1.0 is returned if an error
+* occurs, or has already occurred.
+
+*/
+
+/* Local Variables: */
+ double w2; /* Wavenumber squared */
+
+/* Check the global error status. */
+ if ( !astOK || wavelen == 0.0 ) return 1.0;
+
+/* Find the squared wave number in units of "(per um)**2". */
+ w2 = 1.0E-12/( wavelen * wavelen );
+
+/* Apply the rest of the algorithm as described in the FITS WCS
+ paper III. */
+ return 1.0 + 1.0E-6*( 287.6155 + 1.62887*w2 + 0.01360*w2*w2 );
+}
+
+static double Rverot( double phi, double h, double ra, double da,
+ double st, int *status ) {
+/*
+* Name:
+* Rverot
+
+* Purpose:
+* Find the velocity component in a given direction due to Earth rotation.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double Rverot( double phi, double h, double ra, double da,
+* double st, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function is like slaRverot, except that it takes account of the
+* observers height (h), and does all calculations in double precision.
+
+* Parameters:
+* phi
+* The geodetic latitude of the observer (radians, IAU 1976).
+* h
+* The geodetic height above the reference spheroid of the observer
+* (metres, IAU 1976).
+* ra
+* The geocentric apparent RA (rads) of the source.
+* da
+* The geocentric apparent Dec (rads) of the source.
+* st
+* The local apparent sidereal time (radians).
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the Earth rotation in direction [RA,DA] (km/s).
+* The result is positive when the observer is receding from the
+* given point on the sky. Zero is returned if an error has already
+* occurred.
+
+*/
+
+/* Local Variables: */
+ double pv[ 6 ]; /* Observer position and velocity */
+ double v[ 3 ]; /* Source direction vector */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* Get the Cartesian coordinates of the unit vector pointing towards the
+ given sky position. */
+ palDcs2c( ra, da, v );
+
+/* Get velocity and position of the observer. */
+ palPvobs( phi, h, st, pv );
+
+/* Return the component of the observer's velocity away from the sky
+ position, and convert from AU/s to km/s. */
+ return -palDvdv( v, pv + 3 )*149.597870E6;
+}
+
+static void SpecAdd( AstSpecMap *this, const char *cvt, const double args[], int *status ) {
+/*
+*++
+* Name:
+c astSpecAdd
+f AST_SPECADD
+
+* Purpose:
+* Add a spectral coordinate conversion to a SpecMap.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "specmap.h"
+c void astSpecAdd( AstSpecMap *this, const char *cvt, const double args[] )
+f CALL AST_SPECADD( THIS, CVT, ARGS, STATUS )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+c This function adds one of the standard spectral coordinate
+f This routine adds one of the standard spectral coordinate
+* system conversions listed below to an existing SpecMap.
+*
+c When a SpecMap is first created (using astSpecMap), it simply
+f When a SpecMap is first created (using AST_SPECMAP), it simply
+c performs a unit (null) Mapping. By using astSpecAdd (repeatedly
+f performs a unit (null) Mapping. By using AST_SPECADD (repeatedly
+* if necessary), one or more coordinate conversion steps may then
+* be added, which the SpecMap will perform in sequence. This allows
+* multi-step conversions between a variety of spectral coordinate
+* systems to be assembled out of the building blocks provided by
+* this class.
+*
+* Normally, if a SpecMap's Invert attribute is zero (the default),
+* then its forward transformation is performed by carrying out
+* each of the individual coordinate conversions specified by
+c astSpecAdd in the order given (i.e. with the most recently added
+f AST_SPECADD in the order given (i.e. with the most recently added
+* conversion applied last).
+*
+* This order is reversed if the SpecMap's Invert attribute is
+* non-zero (or if the inverse transformation is requested by any
+* other means) and each individual coordinate conversion is also
+* replaced by its own inverse. This process inverts the overall
+* effect of the SpecMap. In this case, the first conversion to be
+* applied would be the inverse of the one most recently added.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the SpecMap.
+c cvt
+f CVT = CHARACTER * ( * ) (Given)
+c Pointer to a null-terminated string which identifies the
+f A character string which identifies the
+* spectral coordinate conversion to be added to the
+* SpecMap. See the "Available Conversions" section for details of
+* those available.
+c args
+f ARGS( * ) = DOUBLE PRECISION (Given)
+* An array containing argument values for the spectral
+* coordinate conversion. The number of arguments required, and
+* hence the number of array elements used, depends on the
+* conversion specified (see the "Available Conversions"
+* section). This array is ignored
+c and a NULL pointer may be supplied
+* if no arguments are needed.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Notes:
+* - When assembling a multi-stage conversion, it can sometimes be
+* difficult to determine the most economical conversion path. For
+* example, when converting between reference frames, converting first
+* to the heliographic reference frame as an intermediate stage is often
+* sensible in formulating the problem, but may introduce unnecessary
+* extra conversion steps. A solution to this is to include all the steps
+* which are (logically) necessary, but then to use
+c astSimplify to simplify the resulting
+f AST_SIMPLIFY to simplify the resulting
+* SpecMap. The simplification process will eliminate any steps
+* which turn out not to be needed.
+c - This function does not check to ensure that the sequence of
+f - This routine does not check to ensure that the sequence of
+* coordinate conversions added to a SpecMap is physically
+* meaningful.
+
+* Available Conversions:
+* The following strings (which are case-insensitive) may be supplied
+c via the "cvt" parameter to indicate which spectral coordinate
+f via the CVT argument to indicate which spectral coordinate
+* conversion is to be added to the SpecMap. Where arguments are needed by
+* the conversion, they are listed in parentheses. Values for
+c these arguments should be given, via the "args" array, in the
+f these arguments should be given, via the ARGS array, in the
+* order indicated. Units and argument names are described at the end of
+* the list of conversions.
+
+* - "FRTOVL" (RF): Convert frequency to relativistic velocity.
+* - "VLTOFR" (RF): Convert relativistic velocity to Frequency.
+* - "ENTOFR": Convert energy to frequency.
+* - "FRTOEN": Convert frequency to energy.
+* - "WNTOFR": Convert wave number to frequency.
+* - "FRTOWN": Convert frequency to wave number.
+* - "WVTOFR": Convert wavelength (vacuum) to frequency.
+* - "FRTOWV": Convert frequency to wavelength (vacuum).
+* - "AWTOFR": Convert wavelength (air) to frequency.
+* - "FRTOAW": Convert frequency to wavelength (air).
+* - "VRTOVL": Convert radio to relativistic velocity.
+* - "VLTOVR": Convert relativistic to radio velocity.
+* - "VOTOVL": Convert optical to relativistic velocity.
+* - "VLTOVO": Convert relativistic to optical velocity.
+* - "ZOTOVL": Convert redshift to relativistic velocity.
+* - "VLTOZO": Convert relativistic velocity to redshift.
+* - "BTTOVL": Convert beta factor to relativistic velocity.
+* - "VLTOBT": Convert relativistic velocity to beta factor.
+* - "USF2HL" (VOFF,RA,DEC): Convert frequency from a user-defined
+* reference frame to heliocentric.
+* - "HLF2US" (VOFF,RA,DEC): Convert frequency from heliocentric
+* reference frame to user-defined.
+* - "TPF2HL" (OBSLON,OBSLAT,OBSALT,EPOCH,RA,DEC): Convert frequency from
+* topocentric reference frame to heliocentric.
+* - "HLF2TP" (OBSLON,OBSLAT,OBSALT,EPOCH,RA,DEC): Convert frequency from
+* heliocentric reference frame to topocentric.
+* - "GEF2HL" (EPOCH,RA,DEC): Convert frequency from geocentric
+* reference frame to heliocentric.
+* - "HLF2GE" (EPOCH,RA,DEC): Convert frequency from
+* heliocentric reference frame to geocentric.
+* - "BYF2HL" (EPOCH,RA,DEC): Convert frequency from
+* barycentric reference frame to heliocentric.
+* - "HLF2BY" (EPOCH,RA,DEC): Convert frequency from
+* heliocentric reference frame to barycentric.
+* - "LKF2HL" (RA,DEC): Convert frequency from kinematic LSR
+* reference frame to heliocentric.
+* - "HLF2LK" (RA,DEC): Convert frequency from heliocentric
+* reference frame to kinematic LSR.
+* - "LDF2HL" (RA,DEC): Convert frequency from dynamical LSR
+* reference frame to heliocentric.
+* - "HLF2LD" (RA,DEC): Convert frequency from heliocentric
+* reference frame to dynamical LSR.
+* - "LGF2HL" (RA,DEC): Convert frequency from local group
+* reference frame to heliocentric.
+* - "HLF2LG" (RA,DEC): Convert frequency from heliocentric
+* reference frame to local group.
+* - "GLF2HL" (RA,DEC): Convert frequency from galactic
+* reference frame to heliocentric.
+* - "HLF2GL" (RA,DEC): Convert frequency from heliocentric
+* reference frame to galactic.
+
+* The units for the values processed by the above conversions are as
+* follows:
+*
+* - all velocities: metres per second (positive if the source receeds from
+* the observer).
+* - frequency: Hertz.
+* - all wavelengths: metres.
+* - energy: Joules.
+* - wave number: cycles per metre.
+*
+* The arguments used in the above conversions are as follows:
+*
+* - RF: Rest frequency (Hz).
+* - OBSALT: Geodetic altitude of observer (IAU 1975, metres).
+* - OBSLAT: Geodetic latitude of observer (IAU 1975, radians).
+* - OBSLON: Longitude of observer (radians - positive eastwards).
+* - EPOCH: Epoch of observation (UT1 expressed as a Modified Julian Date).
+* - RA: Right Ascension of source (radians, FK5 J2000).
+* - DEC: Declination of source (radians, FK5 J2000).
+* - VOFF: Velocity of the user-defined reference frame, towards the
+* position given by RA and DEC, measured in the heliocentric
+* reference frame.
+*
+* If the SpecMap is 3-dimensional, source positions are provided by the
+* values supplied to inputs 2 and 3 of the SpecMap (which are simply
+* copied to outputs 2 and 3). Note, usable values are still required
+* for the RA and DEC arguments in order to define the "user-defined"
+* reference frame used by USF2HL and HLF2US. However, AST__BAD can be
+* supplied for RA and DEC if the user-defined reference frame is not
+* required.
+*
+*--
+*/
+
+/* Local Variables: */
+ int cvttype; /* Conversion type code */
+
+/* Check the inherited status. */
+ if ( !astOK ) return;
+
+/* Validate the type string supplied and obtain the equivalent
+ conversion type code. */
+ cvttype = CvtCode( cvt, status );
+
+/* If the string was not recognised, then report an error. */
+ if ( astOK && ( cvttype == AST__SPEC_NULL ) ) {
+ astError( AST__SPCIN,
+ "%s(%s): Invalid SpecMap spectral coordinate "
+ "conversion type \"%s\".", status, "astAddSpec", astGetClass( this ), cvt );
+ }
+
+/* Add the new conversion to the SpecMap. */
+ AddSpecCvt( this, cvttype, args, status );
+}
+
+static int SystemChange( int cvt_code, int np, double *values, double *args,
+ int forward, int *status ){
+/*
+* Name:
+* SystemChange
+
+* Purpose:
+* Change values between two spectral systems.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* int SystemChange( int cvt_code, int np, double *values, double *args,
+* int forward, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function modifies the supplied values in order to change the
+* spectral co-ordinate system (frequency, wavelength, etc) to which
+* they refer.
+
+* Parameters:
+* cvt_code
+* A code indicating the conversion to be applied. If the code does
+* not correspond to a change of system, then the supplied values
+* are left unchanged and zero is returned as the function value.
+* np
+* The number of frequency values to transform.
+* values
+* Pointer to an array of "np" spectral values. These are modified on
+* return to hold the corresponding values measured in the output
+* system.
+* args
+* Pointer to an array holding the conversion arguments. The number
+* of arguments expected depends on the particular conversion being
+* used.
+* forward
+* Should the conversion be applied in the forward or inverse
+* direction? Non-zero for forward, zero for inverse.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Non-zero if the supplied conversion code corresponds to a change of
+* system. Zero otherwise (in which case the upplied values will not
+* have been changed).
+
+*/
+
+/* Local Variables: */
+ double *pv; /* Pointer to next value */
+ double d; /* Intermediate value */
+ double f2; /* Squared frequency */
+ double temp; /* Intermediate value */
+ int i; /* Loop index */
+ int iter; /* Iteration count */
+ int result; /* Returned value */
+
+/* Check inherited status. */
+ if( !astOK ) return 0;
+
+/* Set the return value to indicate that the supplied conversion code
+ represents a change of system. */
+ result = 1;
+
+/* Test for each code value in turn and assign the appropriate values. */
+ switch ( cvt_code ) {
+
+/* Frequency to relativistic velocity. */
+ case AST__FRTOVL:
+ if( forward ) {
+ if( args[ 0 ] != AST__BAD ) {
+ temp = args[ 0 ] * args[ 0 ];
+ pv = values - 1;
+ for( i = 0; i < np; i++ ){
+ pv++;
+ if( *pv != AST__BAD ) {
+ f2 = ( *pv ) * ( *pv );
+ d = temp + f2;
+ if( d > 0.0 ) {
+ *pv = AST__C*( ( temp - f2 )/d );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ }
+ } else {
+ pv = values;
+ for( i = 0; i < np; i++ ) *( pv++ ) = AST__BAD;
+ }
+ } else {
+ SystemChange( AST__VLTOFR, np, values, args, 1, status );
+ }
+ break;
+
+/* Relativistic velocity to frequency. */
+ case AST__VLTOFR:
+ if( forward ) {
+ if( args[ 0 ] != AST__BAD ) {
+ temp = args[ 0 ];
+ pv = values - 1;
+ for( i = 0; i < np; i++ ){
+ pv++;
+ if( *pv != AST__BAD ) {
+ d = AST__C + ( *pv );
+ if( d != 0.0 ) {
+ d = ( AST__C - ( *pv ) )/d;
+ if( d >= 0.0 ) {
+ *pv = temp*sqrt( d );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ } else {
+ *pv = AST__BAD;
+ }
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ }
+ } else {
+ pv = values;
+ for( i = 0; i < np; i++ ) *( pv++ ) = AST__BAD;
+ }
+ } else {
+ SystemChange( AST__FRTOVL, np, values, args, 1, status );
+ }
+ break;
+
+/* Energy to frequency */
+ case AST__ENTOFR:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ *pv /= AST__H;
+ }
+ }
+ } else {
+ SystemChange( AST__FRTOEN, np, values, args, 1, status );
+ }
+ break;
+
+/* Frequency to energy */
+ case AST__FRTOEN:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ *pv *= AST__H;
+ }
+ }
+ } else {
+ SystemChange( AST__ENTOFR, np, values, args, 1, status );
+ }
+ break;
+
+/* Wave number to frequency */
+ case AST__WNTOFR:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ *pv *= AST__C;
+ }
+ }
+ } else {
+ SystemChange( AST__FRTOWN, np, values, args, 1, status );
+ }
+ break;
+
+/* Wave number to frequency */
+ case AST__FRTOWN:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ *pv /= AST__C;
+ }
+ }
+ } else {
+ SystemChange( AST__WNTOFR, np, values, args, 1, status );
+ }
+ break;
+
+/* Wavelength to frequency */
+ case AST__WVTOFR:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD && *pv != 0.0 ) {
+ *pv = AST__C/( *pv );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ } else {
+ SystemChange( AST__FRTOWV, np, values, args, 1, status );
+ }
+ break;
+
+/* Frequency to wavelength. */
+ case AST__FRTOWV:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD && *pv != 0.0 ) {
+ *pv = AST__C/( *pv );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ } else {
+ SystemChange( AST__WVTOFR, np, values, args, 1, status );
+ }
+ break;
+
+/* Wavelength in air to frequency. */
+ case AST__AWTOFR:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD && *pv != 0.0 ) {
+ *pv = AST__C/( ( *pv )*Refrac( *pv, status ) );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ } else {
+ SystemChange( AST__FRTOAW, np, values, args, 1, status );
+ }
+ break;
+
+/* Frequency to wavelength in air. */
+ case AST__FRTOAW:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD && *pv != 0.0 ) {
+
+/* Form the vacuum wavelength. */
+ temp = AST__C/( *pv );
+
+/* The refractive index function "Refrac" requires the wavelength in air
+ as its parameter. Initially assume that the wavelength in air is equal
+ to the vacuum wavelength to get he first estimate of the wavelength in
+ air. Then use this estimate to get a better refractive index in order to
+ form a better estimate of the air wavelength, etc. Iterate in this way a
+ few times. */
+ *pv = temp;
+ for( iter = 0; iter < 3; iter++ ) {
+ *pv = temp/Refrac( *pv, status );
+ if( !astISFINITE( *pv ) ) {
+ *pv = AST__BAD;
+ break;
+ }
+ }
+
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ } else {
+ SystemChange( AST__AWTOFR, np, values, args, 1, status );
+ }
+ break;
+
+/* Radio velocity to relativistic velocity */
+ case AST__VRTOVL:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ temp = 1.0 - ( *pv )/AST__C;
+ temp *= temp;
+ *pv = AST__C*( 1.0 - temp )/( 1.0 + temp );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ }
+ }
+ } else {
+ SystemChange( AST__VLTOVR, np, values, args, 1, status );
+ }
+ break;
+
+/* Relativistic velocity to radio velocity. */
+ case AST__VLTOVR:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ temp = AST__C + ( *pv );
+ if( temp != 0.0 ) {
+ temp = (AST__C - *pv )/temp;
+ if( temp >= 0.0 ) {
+ *pv = AST__C*( 1.0 - sqrt( temp ) );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ } else {
+ *pv = AST__BAD;
+ }
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ }
+ } else {
+ SystemChange( AST__VRTOVL, np, values, args, 1, status );
+ }
+ break;
+
+/* Optical velocity to relativistic velocity */
+ case AST__VOTOVL:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ temp = 1.0 + ( *pv )/AST__C;
+ temp *= temp;
+ *pv = AST__C*( temp - 1.0 )/( temp + 1.0 );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ }
+ }
+ } else {
+ SystemChange( AST__VLTOVO, np, values, args, 1, status );
+ }
+ break;
+
+/* Relativistic velocity to optical velocity. */
+ case AST__VLTOVO:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ temp = AST__C - *pv;
+ if( temp != 0.0 ) {
+ temp = (AST__C + *pv )/temp;
+ if( temp >= 0.0 ) {
+ *pv = AST__C*( sqrt( temp ) - 1.0 );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ } else {
+ *pv = AST__BAD;
+ }
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ }
+ } else {
+ SystemChange( AST__VOTOVL, np, values, args, 1, status );
+ }
+ break;
+
+/* Redshift to relativistic velocity */
+ case AST__ZOTOVL:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ temp = 1.0 + ( *pv );
+ temp *= temp;
+ *pv = AST__C*( temp - 1.0 )/( temp + 1.0 );
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ }
+ }
+ } else {
+ SystemChange( AST__VLTOZO, np, values, args, 1, status );
+ }
+ break;
+
+/* Relativistic velocity to redshift. */
+ case AST__VLTOZO:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ temp = AST__C - *pv;
+ if( temp != 0.0 ) {
+ temp = (AST__C + *pv )/temp;
+ if( temp >= 0.0 ) {
+ *pv = sqrt( temp ) - 1.0;
+ if( !astISFINITE( *pv ) ) *pv = AST__BAD;
+ } else {
+ *pv = AST__BAD;
+ }
+ } else {
+ *pv = AST__BAD;
+ }
+ }
+ }
+ } else {
+ SystemChange( AST__ZOTOVL, np, values, args, 1, status );
+ }
+ break;
+
+/* Beta factor to relativistic velocity */
+ case AST__BTTOVL:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ *pv *= AST__C;
+ }
+ }
+ } else {
+ SystemChange( AST__VLTOBT, np, values, args, 1, status );
+ }
+ break;
+
+/* Relativistic velocity to beta factor. */
+ case AST__VLTOBT:
+ if( forward ) {
+ pv = values - 1;
+ for( i = 0; i < np; i++ ) {
+ pv++;
+ if( *pv != AST__BAD ) {
+ *pv /= AST__C;
+ }
+ }
+ } else {
+ SystemChange( AST__BTTOVL, np, values, args, 1, status );
+ }
+ break;
+
+/* If the supplied code does not represent a change of system, clear
+ the returned flag. */
+ default:
+ result = 0;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static double TopoVel( double ra, double dec, FrameDef *def, int *status ) {
+/*
+* Name:
+* TopoVel
+
+* Purpose:
+* Find the velocity of the observer away from the source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double TopoVel( double ra, double dec, FrameDef *def, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function finds the component of the velocity of the observer away
+* from a specified source position, at a given epoch, in the frame of
+* rest of the Sun.
+
+* Parameters:
+* ra
+* The RA (rads, FK5 J2000) of the source.
+* dec
+* The Dec (rads, FK5 J2000) of the source.
+* def
+* Pointer to a FrameDef structure which holds the parameters which
+* define the frame, together with cached intermediate results.
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the frame's velocity away from the position given by
+* "ra" and "dec", in m/s, measured within the Heliographic frame of
+* rest. Zero is returned if an error has already occurred.
+
+*/
+
+/* Local Variables: */
+ double deca; /* Apparent DEC */
+ double raa; /* Apparent RA */
+ double vobs; /* Velocity of observer relative to earth */
+ double vearth; /* Velocity of earth realtive to sun */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* If not already done so, get the parameters defining the transformation
+ of mean ra and dec to apparent ra and dec, and store in the supplied frame
+ definition structure. */
+ if( def->amprms[ 0 ] == AST__BAD ) palMappa( 2000.0, def->epoch,
+ def->amprms );
+
+/* Convert the source position from mean ra and dec to apparent ra and dec. */
+ palMapqkz( ra, dec, def->amprms, &raa, &deca );
+
+/* If not already done so, get the local apparent siderial time (in radians)
+ and store in the supplied frame definition structure. */
+ if( def->last == AST__BAD ) def->last = palGmst( def->epoch ) +
+ palEqeqx( def->epoch ) +
+ def->obslon;
+
+/* Get the component away from the source, of the velocity of the observer
+ relative to the centre of the earth (in m/s). */
+ vobs = 1000.0*Rverot( def->obslat, def->obsalt, raa, deca, def->last,
+ status );
+
+/* Get the component away from the source, of the velocity of the earth's
+ centre relative to the Sun, in m/s. */
+ vearth = GeoVel( ra, dec, def, status );
+
+/* Return the total velocity of the observer away from the source in the
+ frame of the sun. */
+ return vobs + vearth;
+}
+
+static AstPointSet *Transform( AstMapping *this, AstPointSet *in,
+ int forward, AstPointSet *out, int *status ) {
+/*
+* Name:
+* Transform
+
+* Purpose:
+* Apply a SpecMap to transform a set of points.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
+* int forward, AstPointSet *out, int *status )
+
+* Class Membership:
+* SpecMap member function (over-rides the astTransform method inherited
+* from the Mapping class).
+
+* Description:
+* This function takes a SpecMap and a set of points encapsulated
+* in a PointSet and transforms the points so as to perform the
+* sequence of spectral coordinate conversions specified by
+* previous invocations of astSpecAdd.
+
+* Parameters:
+* this
+* Pointer to the SpecMap.
+* in
+* Pointer to the PointSet holding the input coordinate data.
+* forward
+* A non-zero value indicates that the forward coordinate transformation
+* should be applied, while a zero value requests the inverse
+* transformation.
+* out
+* Pointer to a PointSet which will hold the transformed (output)
+* coordinate values. A NULL value may also be given, in which case a
+* new PointSet will be created by this function.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the output (possibly new) PointSet.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked with the
+* global error status set, or if it should fail for any reason.
+* - The number of coordinate values per point in the input PointSet must
+* match the number of coordinates for the SpecMap being applied.
+* - If an output PointSet is supplied, it must have space for sufficient
+* number of points and coordinate values per point to accommodate the
+* result. Any excess space will be ignored.
+*/
+
+/* Local Variables: */
+ AstPointSet *result; /* Pointer to output PointSet */
+ AstSpecMap *map; /* Pointer to SpecMap to be applied */
+ double **ptr_in; /* Pointer to input coordinate data */
+ double **ptr_out; /* Pointer to output coordinate data */
+ double *spec; /* Pointer to output spectral axis value array */
+ double *alpha; /* Pointer to output RA axis value array */
+ double *beta; /* Pointer to output DEC axis value array */
+ int cvt; /* Loop counter for conversions */
+ int end; /* Termination index for conversion loop */
+ int inc; /* Increment for conversion loop */
+ int map3d; /* Is the SpecMap 3-dimensional? */
+ int ncoord_in; /* Number of coordinates per input point */
+ int npoint; /* Number of points */
+ int start; /* Starting index for conversion loop */
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* Obtain a pointer to the SpecMap. */
+ map = (AstSpecMap *) this;
+
+/* Apply the parent mapping using the stored pointer to the Transform member
+ function inherited from the parent Mapping class. This function validates
+ all arguments and generates an output PointSet if necessary, but does not
+ actually transform any coordinate values. */
+ result = (*parent_transform)( this, in, forward, out, status );
+
+/* We will now extend the parent astTransform method by performing the
+ coordinate conversions needed to generate the output coordinate values. */
+
+/* Determine the numbers of points and coordinates per point from the input
+ PointSet and obtain pointers for accessing the input and output coordinate
+ values. */
+ ncoord_in = astGetNcoord( in );
+ npoint = astGetNpoint( in );
+ ptr_in = astGetPoints( in );
+ ptr_out = astGetPoints( result );
+
+/* Determine whether to apply the forward or inverse transformation, according
+ to the direction specified and whether the mapping has been inverted. */
+ if ( astGetInvert( this ) ) forward = !forward;
+
+/* Transform the coordinate values. */
+/* -------------------------------- */
+/* Use "spec" as a synonym for the array of spectral axis values stored in
+ the output PointSet. */
+ if ( astOK ) {
+ spec = ptr_out[ 0 ];
+
+/* If this is a 3D SpecMap use "alpha" as a synonym for the array of RA axis
+ values and "beta" as a synonym for the array of DEC axis values stored
+ in the output PointSet. */
+ map3d = ( ncoord_in == 3 );
+ if( map3d ) {
+ alpha = ptr_out[ 1 ];
+ beta = ptr_out[ 2 ];
+ } else {
+ alpha = NULL;
+ beta = NULL;
+ }
+
+/* Initialise the output coordinate values by copying the input ones. */
+ (void) memcpy( spec, ptr_in[ 0 ], sizeof( double ) * (size_t) npoint );
+ if( map3d ) {
+ (void) memcpy( alpha, ptr_in[ 1 ], sizeof( double ) * (size_t) npoint );
+ (void) memcpy( beta, ptr_in[ 2 ], sizeof( double ) * (size_t) npoint );
+ }
+
+/* We will loop to apply each spectral coordinate conversion in turn to the
+ (spec) array. However, if the inverse transformation was requested,
+ we must loop through these transformations in reverse order, so set up
+ appropriate limits and an increment to control this loop. */
+ start = forward ? 0 : map->ncvt - 1;
+ end = forward ? map->ncvt : -1;
+ inc = forward ? 1 : -1;
+
+/* Loop through the coordinate conversions in the required order. */
+ for ( cvt = start; cvt != end; cvt += inc ) {
+
+/* Process conversions which correspond to changes of reference frames. */
+ if( !FrameChange( map->cvttype[ cvt ], npoint, alpha, beta, spec,
+ map->cvtargs[ cvt ], forward, status ) ) {
+
+/* If this conversion was not a change of reference frame, it must be a
+ change of system. */
+ SystemChange( map->cvttype[ cvt ], npoint, spec,
+ map->cvtargs[ cvt ], forward, status );
+ }
+ }
+ }
+
+/* If an error has occurred and a new PointSet may have been created, then
+ clean up by annulling it. In any case, ensure that a NULL result is
+ returned.*/
+ if ( !astOK ) {
+ if ( !out ) result = astAnnul( result );
+ result = NULL;
+ }
+
+/* Return a pointer to the output PointSet. */
+ return result;
+
+}
+
+static double UserVel( double ra, double dec, FrameDef *def, int *status ) {
+/*
+* Name:
+* UserVel
+
+* Purpose:
+* Find the component of the velocity of the user-defined rest-frame
+* away from the source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* double UserVel( double ra, double dec, FrameDef *def, int *status )
+
+* Class Membership:
+* SpecMap method.
+
+* Description:
+* This function finds the component of the velocity of the user-defined
+* rest-frame away from a specified position. The magnitude and direction
+* of the rest-frames velocity are defined within the supplied "def"
+* structure. The user-defined rest-frame is typically used to represent
+* the velocity of the source within the heliocentric rest-frame.
+
+* Parameters:
+* ra
+* The RA (rads, FK5 J2000) of the source.
+* dec
+* The Dec (rads, FK5 J2000) of the source.
+* def
+* Pointer to a FrameDef structure which holds the parameters which
+* define the frame, together with cached intermediate results.
+* status
+* Pointer to the inherited status variable.
+
+* Returns:
+* The component of the frame's velocity away from the position given by
+* "ra" and "dec", in m/s, measured within the Heliographic frame of
+* rest. Zero is returned if an error has already occurred.
+
+* Notes:
+* - The direction of the user velocity is given by def->refra and
+* def->refdec (an FK5 J2000 position). The maginitude of the velocity
+* is given by def->veluser, in m/s, positive when the source is moving
+* away from the observer towards def->refra, def->refdec, and given
+* with respect to the heliocentric rest-frame.
+
+*/
+
+/* Local Variables: */
+ double vb[ 3 ]; /* Source position vector */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* If not already done so, express the user velocity in the form of a
+ J2000.0 x,y,z vector. */
+ if( def->vuser[ 0 ] == AST__BAD ) {
+ def->vuser[ 0 ] = def->veluser*cos( def->refra )*cos( def->refdec );
+ def->vuser[ 1 ] = def->veluser*sin( def->refra )*cos( def->refdec );
+ def->vuser[ 2 ] = def->veluser*sin( def->refdec );
+ }
+
+/* Convert given J2000 RA,Dec to x,y,z. */
+ palDcs2c( ra, dec, vb );
+
+/* Return the dot product with the user velocity. Invert it to get the
+ velocity towards the observer (the def->veluser value is supposed to be
+ positive if the source is moving away from the observer). */
+ return -palDvdv( def->vuser, vb );
+}
+
+/* Copy constructor. */
+/* ----------------- */
+static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
+/*
+* Name:
+* Copy
+
+* Purpose:
+* Copy constructor for SpecMap objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Copy( const AstObject *objin, AstObject *objout, int *status )
+
+* Description:
+* This function implements the copy constructor for SpecMap objects.
+
+* Parameters:
+* objin
+* Pointer to the object to be copied.
+* objout
+* Pointer to the object being constructed.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* void
+
+* Notes:
+* - This constructor makes a deep copy.
+*/
+
+/* Local Variables: */
+ AstSpecMap *in; /* Pointer to input SpecMap */
+ AstSpecMap *out; /* Pointer to output SpecMap */
+ int cvt; /* Loop counter for coordinate conversions */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain pointers to the input and output SpecMap structures. */
+ in = (AstSpecMap *) objin;
+ out = (AstSpecMap *) objout;
+
+/* For safety, first clear any references to the input memory from the output
+ SpecMap. */
+ out->cvtargs = NULL;
+ out->cvttype = NULL;
+
+/* Allocate memory for the output array of argument list pointers. */
+ out->cvtargs = astMalloc( sizeof( double * ) * (size_t) in->ncvt );
+
+/* If necessary, allocate memory and make a copy of the input array of
+ coordinate conversion codes. */
+ if ( in->cvttype ) out->cvttype = astStore( NULL, in->cvttype,
+ sizeof( int )
+ * (size_t) in->ncvt );
+
+/* If OK, loop through each conversion in the input SpecMap and make a copy of
+ its argument list, storing the new pointer in the output argument list
+ array. */
+ if ( astOK ) {
+ for ( cvt = 0; cvt < in->ncvt; cvt++ ) {
+ out->cvtargs[ cvt ] = astStore( NULL, in->cvtargs[ cvt ],
+ astSizeOf( in->cvtargs[ cvt ] ) );
+ }
+
+/* If an error occurred while copying the argument lists, loop through the
+ conversions again and clean up by ensuring that the new memory allocated for
+ each argument list is freed. */
+ if ( !astOK ) {
+ for ( cvt = 0; cvt < in->ncvt; cvt++ ) {
+ out->cvtargs[ cvt ] = astFree( out->cvtargs[ cvt ] );
+ }
+ }
+ }
+
+/* If an error occurred, free all other memory allocated above. */
+ if ( !astOK ) {
+ out->cvtargs = astFree( out->cvtargs );
+ out->cvttype = astFree( out->cvttype );
+ }
+}
+
+/* Destructor. */
+/* ----------- */
+static void Delete( AstObject *obj, int *status ) {
+/*
+* Name:
+* Delete
+
+* Purpose:
+* Destructor for SpecMap objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Delete( AstObject *obj, int *status )
+
+* Description:
+* This function implements the destructor for SpecMap objects.
+
+* Parameters:
+* obj
+* Pointer to the object to be deleted.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* void
+
+* Notes:
+* This function attempts to execute even if the global error status is
+* set.
+*/
+
+/* Local Variables: */
+ AstSpecMap *this; /* Pointer to SpecMap */
+ int cvt; /* Loop counter for coordinate conversions */
+
+/* Obtain a pointer to the SpecMap structure. */
+ this = (AstSpecMap *) obj;
+
+/* Loop to free the memory containing the argument list for each coordinate
+ conversion. */
+ for ( cvt = 0; cvt < this->ncvt; cvt++ ) {
+ this->cvtargs[ cvt ] = astFree( this->cvtargs[ cvt ] );
+ }
+
+/* Free the memory holding the array of conversion types and the array of
+ argument list pointers. */
+ this->cvtargs = astFree( this->cvtargs );
+ this->cvttype = astFree( this->cvttype );
+}
+
+/* Dump function. */
+/* -------------- */
+static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
+/*
+* Name:
+* Dump
+
+* Purpose:
+* Dump function for SpecMap objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* void Dump( AstObject *this, AstChannel *channel, int *status )
+
+* Description:
+* This function implements the Dump function which writes out data
+* for the SpecMap class to an output Channel.
+
+* Parameters:
+* this
+* Pointer to the SpecMap whose data are being written.
+* channel
+* Pointer to the Channel to which the data are being written.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Constants: */
+#define KEY_LEN 50 /* Maximum length of a keyword */
+
+/* Local Variables: */
+ AstSpecMap *this; /* Pointer to the SpecMap structure */
+ char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */
+ const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */
+ const char *comment; /* Pointer to comment string */
+ const char *sval; /* Pointer to string value */
+ int argdec; /* Index of DEC argument */
+ int argra; /* Index of RA argument */
+ int iarg; /* Loop counter for arguments */
+ int icvt; /* Loop counter for conversion steps */
+ int ival; /* Integer value */
+ int nargs; /* Number of user-supplied arguments */
+ int szargs; /* Number of stored arguments */
+ int set; /* Attribute value set? */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the SpecMap structure. */
+ this = (AstSpecMap *) this_object;
+
+/* Write out values representing the instance variables for the SpecMap
+ class. Accompany these with appropriate comment strings, possibly
+ depending on the values being written.*/
+
+/* In the case of attributes, we first use the appropriate (private)
+ Test... member function to see if they are set. If so, we then use
+ the (private) Get... function to obtain the value to be written
+ out.
+
+ For attributes which are not set, we use the astGet... method to
+ obtain the value instead. This will supply a default value
+ (possibly provided by a derived class which over-rides this method)
+ which is more useful to a human reader as it corresponds to the
+ actual default attribute value. Since "set" will be zero, these
+ values are for information only and will not be read back. */
+
+/* Number of conversion steps. */
+/* --------------------------- */
+/* Regard this as "set" if it is non-zero. */
+ ival = this->ncvt;
+ set = ( ival != 0 );
+ astWriteInt( channel, "Nspec", set, 0, ival, "Number of conversion steps" );
+
+/* Write out data for each conversion step... */
+ for ( icvt = 0; icvt < this->ncvt; icvt++ ) {
+
+/* Conversion type. */
+/* ---------------- */
+/* Change each conversion type code into an equivalent string and
+ obtain associated descriptive information. If the conversion code
+ was not recognised, report an error and give up. */
+ if ( astOK ) {
+ sval = CvtString( this->cvttype[ icvt ], &comment, &argra, &argdec,
+ &nargs, &szargs, argdesc, status );
+ if ( astOK && !sval ) {
+ astError( AST__SPCIN,
+ "astWrite(%s): Corrupt %s contains invalid SpecMap "
+ "spectral coordinate conversion code (%d).", status,
+ astGetClass( channel ), astGetClass( this ),
+ (int) this->cvttype[ icvt ] );
+ break;
+ }
+
+/* Create an appropriate keyword and write out the conversion code
+ information. */
+ (void) sprintf( key, "Spec%d", icvt + 1 );
+ astWriteString( channel, key, 1, 1, sval, comment );
+
+/* Write out data for each conversion argument... */
+ for ( iarg = 0; iarg < szargs; iarg++ ) {
+
+/* Arguments. */
+/* ---------- */
+/* Create an appropriate keyword and write out the argument value,
+ accompanied by the descriptive comment obtained above. */
+ if( this->cvtargs[ icvt ][ iarg ] != AST__BAD ) {
+ (void) sprintf( key, "Spec%d%c", icvt + 1, ALPHABET[ iarg ] );
+ astWriteDouble( channel, key, 1, 1, this->cvtargs[ icvt ][ iarg ],
+ argdesc[ iarg ] );
+ }
+ }
+
+/* Quit looping if an error occurs. */
+ if ( !astOK ) break;
+ }
+ }
+
+/* Undefine macros local to this function. */
+#undef KEY_LEN
+}
+
+/* Standard class functions. */
+/* ========================= */
+/* Implement the astIsASpecMap and astCheckSpecMap functions using the macros
+ defined for this purpose in the "object.h" header file. */
+astMAKE_ISA(SpecMap,Mapping)
+astMAKE_CHECK(SpecMap)
+
+AstSpecMap *astSpecMap_( int nin, int flags, const char *options, int *status, ...) {
+/*
+*++
+* Name:
+c astSpecMap
+f AST_SPECMAP
+
+* Purpose:
+* Create a SpecMap.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "specmap.h"
+c AstSpecMap *astSpecMap( int nin, int flags, const char *options, ... )
+f RESULT = AST_SPECMAP( NIN, FLAGS, OPTIONS, STATUS )
+
+* Class Membership:
+* SpecMap constructor.
+
+* Description:
+* This function creates a new SpecMap and optionally initialises
+* its attributes.
+*
+* An SpecMap is a specialised form of Mapping which can be used to
+* represent a sequence of conversions between standard spectral
+* coordinate systems. This includes conversions between frequency,
+* wavelength, and various forms of velocity, as well as conversions
+* between different standards of rest.
+*
+* When a SpecMap is first created, it simply performs a unit
+c (null) Mapping. Using the astSpecAdd function,
+f (null) Mapping. Using the AST_SPECADD routine,
+* a series of coordinate conversion steps may then be added, selected
+* from the list of supported conversions. This allows multi-step
+* conversions between a variety of spectral coordinate systems to
+* be assembled out of the building blocks provided by this class.
+*
+* For details of the individual coordinate conversions available,
+c see the description of the astSpecAdd function.
+f see the description of the AST_SPECADD routine.
+*
+* Conversions are available to transform between standards of rest.
+* Such conversions need to know the source position as an RA and DEC.
+* This information can be supplied in the form of parameters for
+* the relevant conversions, in which case the SpecMap is 1-dimensional,
+* simply transforming the spectral axis values. This means that the
+* same source position will always be used by the SpecMap. However, this
+* may not be appropriate for an accurate description of a 3-D spectral
+* cube, where changes of spatial position can produce significant
+* changes in the Doppler shift introduced when transforming between
+* standards of rest. For this situation, a 3-dimensional SpecMap can
+* be created in which axes 2 and 3 correspond to the source RA and DEC
+* The SpecMap simply copies values for axes 2 and 3 from input to
+* output).
+
+* Parameters:
+c nin
+f NIN = INTEGER (Given)
+* The number of inputs to the Mapping (this will also equal the
+* number of outputs). This value must be either 1 or 3. In either
+* case, the first input and output correspoindis the spectral axis.
+* For a 3-axis SpecMap, the second and third axes give the RA and
+* DEC (J2000 FK5) of the source. This positional information is
+* used by conversions which transform between standards of rest,
+* and replaces the "RA" and "DEC" arguments for the individual
+* conversions listed in description of the "SpecAdd"
+c function.
+f routine.
+c flags
+f FLAGS = INTEGER (Given)
+c This parameter is reserved for future use and should currently
+f This argument is reserved for future use and should currently
+* always be set to zero.
+c options
+f OPTIONS = CHARACTER * ( * ) (Given)
+c Pointer to a null-terminated string containing an optional
+c comma-separated list of attribute assignments to be used for
+c initialising the new SpecMap. The syntax used is identical to
+c that for the astSet function and may include "printf" format
+c specifiers identified by "%" symbols in the normal way.
+c If no initialisation is required, a zero-length string may be
+c supplied.
+f A character string containing an optional comma-separated
+f list of attribute assignments to be used for initialising the
+f new SpecMap. The syntax used is identical to that for the
+f AST_SET routine. If no initialisation is required, a blank
+f value may be supplied.
+c ...
+c If the "options" string contains "%" format specifiers, then
+c an optional list of additional arguments may follow it in
+c order to supply values to be substituted for these
+c specifiers. The rules for supplying these are identical to
+c those for the astSet function (and for the C "printf"
+c function).
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astSpecMap()
+f AST_SPECMAP = INTEGER
+* A pointer to the new SpecMap.
+
+* Notes:
+* - The nature and units of the coordinate values supplied for the
+* first input (i.e. the spectral input) of a SpecMap must be appropriate
+* to the first conversion step applied by the SpecMap. For instance, if
+* the first conversion step is "FRTOVL" (frequency to relativistic
+* velocity), then the coordinate values for the first input should
+* be frequency in units of Hz. Similarly, the nature and units of the
+* coordinate values returned by a SpecMap will be determined by the
+* last conversion step applied by the SpecMap. For instance, if the
+* last conversion step is "VLTOVO" (relativistic velocity to optical
+* velocity), then the coordinate values for the first output will be optical
+* velocity in units of metres per second. See the description of the
+c astSpecAdd function for the units expected and returned by each
+f AST_SPECADD routine for the units expected and returned by each
+* conversion.
+* - A null Object pointer (AST__NULL) will be returned if this
+c function is invoked with the AST error status set, or if it
+f function is invoked with STATUS set to an error value, or if it
+* should fail for any reason.
+*--
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstSpecMap *new; /* Pointer to the new SpecMap */
+ va_list args; /* Variable argument list */
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise the SpecMap, allocating memory and initialising the virtual
+ function table as well if necessary. */
+ new = astInitSpecMap( NULL, sizeof( AstSpecMap ), !class_init, &class_vtab,
+ "SpecMap", nin, flags );
+
+/* If successful, note that the virtual function table has been initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the options string
+ to the astVSet method to initialise the new SpecMap's attributes. */
+ va_start( args, status );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return a pointer to the new SpecMap. */
+ return new;
+}
+
+AstSpecMap *astSpecMapId_( int nin, int flags, const char *options, ... ) {
+/*
+* Name:
+* astSpecMapId_
+
+* Purpose:
+* Create a SpecMap.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "specmap.h"
+* AstSpecMap *astSpecMapId_( int nin, int flags, const char *options, ... )
+
+* Class Membership:
+* SpecMap constructor.
+
+* Description:
+* This function implements the external (public) interface to the
+* astSpecMap constructor function. It returns an ID value (instead
+* of a true C pointer) to external users, and must be provided
+* because astSpecMap_ has a variable argument list which cannot be
+* encapsulated in a macro (where this conversion would otherwise
+* occur).
+*
+* The variable argument list also prevents this function from
+* invoking astSpecMap_ directly, so it must be a re-implementation
+* of it in all respects, except for the final conversion of the
+* result to an ID value.
+
+* Parameters:
+* As for astSpecMap_.
+
+* Returned Value:
+* The ID value associated with the new SpecMap.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstSpecMap *new; /* Pointer to the new SpecMap */
+ va_list args; /* Variable argument list */
+
+ int *status; /* Pointer to inherited status value */
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise the SpecMap, allocating memory and initialising the virtual
+ function table as well if necessary. */
+ new = astInitSpecMap( NULL, sizeof( AstSpecMap ), !class_init, &class_vtab,
+ "SpecMap", nin, flags );
+
+/* If successful, note that the virtual function table has been initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the options string
+ to the astVSet method to initialise the new SpecMap's attributes. */
+ va_start( args, options );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return an ID value for the new SpecMap. */
+ return astMakeId( new );
+}
+
+AstSpecMap *astInitSpecMap_( void *mem, size_t size, int init,
+ AstSpecMapVtab *vtab, const char *name,
+ int nin, int flags, int *status ) {
+/*
+*+
+* Name:
+* astInitSpecMap
+
+* Purpose:
+* Initialise a SpecMap.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "specmap.h"
+* AstSpecMap *astInitSpecMap( void *mem, size_t size, int init,
+* AstSpecMapVtab *vtab, const char *name,
+* int nin, int flags )
+
+* Class Membership:
+* SpecMap initialiser.
+
+* Description:
+* This function is provided for use by class implementations to initialise
+* a new SpecMap object. It allocates memory (if necessary) to accommodate
+* the SpecMap plus any additional data associated with the derived class.
+* It then initialises a SpecMap structure at the start of this memory. If
+* the "init" flag is set, it also initialises the contents of a virtual
+* function table for a SpecMap at the start of the memory passed via the
+* "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory in which the SpecMap is to be initialised.
+* This must be of sufficient size to accommodate the SpecMap data
+* (sizeof(SpecMap)) plus any data used by the derived class. If a value
+* of NULL is given, this function will allocate the memory itself using
+* the "size" parameter to determine its size.
+* size
+* The amount of memory used by the SpecMap (plus derived class data).
+* This will be used to allocate memory if a value of NULL is given for
+* the "mem" parameter. This value is also stored in the SpecMap
+* structure, so a valid value must be supplied even if not required for
+* allocating memory.
+* init
+* A logical flag indicating if the SpecMap's virtual function table is
+* to be initialised. If this value is non-zero, the virtual function
+* table will be initialised by this function.
+* vtab
+* Pointer to the start of the virtual function table to be associated
+* with the new SpecMap.
+* name
+* Pointer to a constant null-terminated character string which contains
+* the name of the class to which the new object belongs (it is this
+* pointer value that will subsequently be returned by the astClass
+* method).
+* nin
+* The number of inputs and outputs for the SpecMap (either 1 or 3).
+* flags
+* This parameter is reserved for future use. It is currently ignored.
+
+* Returned Value:
+* A pointer to the new SpecMap.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked with the
+* global error status set, or if it should fail for any reason.
+*-
+*/
+
+/* Local Variables: */
+ AstSpecMap *new; /* Pointer to the new SpecMap */
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Check nin is OK (1 or 3). */
+ if( nin != 1 && nin != 3 ) {
+ astError( AST__BADNI, "astInitSpecMap(SpecMap): Supplied number of "
+ "SpecMap axes (%d) is illegal; it should be 1 or 2. ", status,
+ nin );
+ }
+
+/* If necessary, initialise the virtual function table. */
+ if ( init ) astInitSpecMapVtab( vtab, name );
+
+/* Initialise a 1D Mapping structure (the parent class) as the first component
+ within the SpecMap structure, allocating memory if necessary. Specify that
+ the Mapping should be defined in both the forward and inverse directions. */
+ new = (AstSpecMap *) astInitMapping( mem, size, 0,
+ (AstMappingVtab *) vtab, name,
+ nin, nin, 1, 1 );
+
+ if ( astOK ) {
+
+/* Initialise the SpecMap data. */
+/* --------------------------- */
+/* The initial state is with no conversions set, in which condition the
+ SpecMap simply implements a unit mapping. */
+ new->ncvt = 0;
+ new->cvtargs = NULL;
+ new->cvttype = NULL;
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return a pointer to the new object. */
+ return new;
+}
+
+AstSpecMap *astLoadSpecMap_( void *mem, size_t size,
+ AstSpecMapVtab *vtab, const char *name,
+ AstChannel *channel, int *status ) {
+/*
+*+
+* Name:
+* astLoadSpecMap
+
+* Purpose:
+* Load a SpecMap.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "specmap.h"
+* AstSpecMap *astLoadSpecMap( void *mem, size_t size,
+* AstSpecMapVtab *vtab, const char *name,
+* AstChannel *channel )
+
+* Class Membership:
+* SpecMap loader.
+
+* Description:
+* This function is provided to load a new SpecMap using data read
+* from a Channel. It first loads the data used by the parent class
+* (which allocates memory if necessary) and then initialises a
+* SpecMap structure in this memory, using data read from the input
+* Channel.
+*
+* If the "init" flag is set, it also initialises the contents of a
+* virtual function table for a SpecMap at the start of the memory
+* passed via the "vtab" parameter.
+
+
+* Parameters:
+* mem
+* A pointer to the memory into which the SpecMap is to be
+* loaded. This must be of sufficient size to accommodate the
+* SpecMap data (sizeof(SpecMap)) plus any data used by derived
+* classes. If a value of NULL is given, this function will
+* allocate the memory itself using the "size" parameter to
+* determine its size.
+* size
+* The amount of memory used by the SpecMap (plus derived class
+* data). This will be used to allocate memory if a value of
+* NULL is given for the "mem" parameter. This value is also
+* stored in the SpecMap structure, so a valid value must be
+* supplied even if not required for allocating memory.
+*
+* If the "vtab" parameter is NULL, the "size" value is ignored
+* and sizeof(AstSpecMap) is used instead.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new SpecMap. If this is NULL, a pointer to
+* the (static) virtual function table for the SpecMap class is
+* used instead.
+* name
+* Pointer to a constant null-terminated character string which
+* contains the name of the class to which the new object
+* belongs (it is this pointer value that will subsequently be
+* returned by the astGetClass method).
+*
+* If the "vtab" parameter is NULL, the "name" value is ignored
+* and a pointer to the string "SpecMap" is used instead.
+
+* Returned Value:
+* A pointer to the new SpecMap.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*-
+*/
+
+/* Local Constants: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+#define KEY_LEN 50 /* Maximum length of a keyword */
+
+/* Local Variables: */
+ AstSpecMap *new; /* Pointer to the new SpecMap */
+ char *sval; /* Pointer to string value */
+ char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */
+ const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */
+ const char *comment; /* Pointer to comment string */
+ int argdec; /* Index of DEC argument */
+ int argra; /* Index of RA argument */
+ int iarg; /* Loop counter for arguments */
+ int icvt; /* Loop counter for conversion steps */
+ int nargs; /* Number of user-supplied arguments */
+ int szargs; /* Number of stored arguments */
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(channel);
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* If a NULL virtual function table has been supplied, then this is
+ the first loader to be invoked for this SpecMap. In this case the
+ SpecMap belongs to this class, so supply appropriate values to be
+ passed to the parent class loader (and its parent, etc.). */
+ if ( !vtab ) {
+ size = sizeof( AstSpecMap );
+ vtab = &class_vtab;
+ name = "SpecMap";
+
+/* If required, initialise the virtual function table for this class. */
+ if ( !class_init ) {
+ astInitSpecMapVtab( vtab, name );
+ class_init = 1;
+ }
+ }
+
+/* Invoke the parent class loader to load data for all the ancestral
+ classes of the current one, returning a pointer to the resulting
+ partly-built SpecMap. */
+ new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name,
+ channel );
+
+ if ( astOK ) {
+
+/* Read input data. */
+/* ================ */
+/* Request the input Channel to read all the input data appropriate to
+ this class into the internal "values list". */
+ astReadClassData( channel, "SpecMap" );
+
+/* Now read each individual data item from this list and use it to
+ initialise the appropriate instance variable(s) for this class. */
+
+/* In the case of attributes, we first read the "raw" input value,
+ supplying the "unset" value as the default. If a "set" value is
+ obtained, we then use the appropriate (private) Set... member
+ function to validate and set the value properly. */
+
+/* Number of conversion steps. */
+/* --------------------------- */
+/* Read the number of conversion steps and allocate memory to hold
+ data for each step. */
+ new->ncvt = astReadInt( channel, "nspec", 0 );
+ if ( new->ncvt < 0 ) new->ncvt = 0;
+ new->cvttype = astMalloc( sizeof( int ) * (size_t) new->ncvt );
+ new->cvtargs = astMalloc( sizeof( double * ) * (size_t) new->ncvt );
+
+/* If an error occurred, ensure that all allocated memory is freed. */
+ if ( !astOK ) {
+ new->cvttype = astFree( new->cvttype );
+ new->cvtargs = astFree( new->cvtargs );
+
+/* Otherwise, initialise the argument pointer array. */
+ } else {
+ for ( icvt = 0; icvt < new->ncvt; icvt++ ) {
+ new->cvtargs[ icvt ] = NULL;
+ }
+
+/* Read in data for each conversion step... */
+ for ( icvt = 0; icvt < new->ncvt; icvt++ ) {
+
+/* Conversion type. */
+/* ---------------- */
+/* Create an appropriate keyword and read the string representation of
+ the conversion type. */
+ (void) sprintf( key, "spec%d", icvt + 1 );
+ sval = astReadString( channel, key, NULL );
+
+/* If no value was read, report an error. */
+ if ( astOK ) {
+ if ( !sval ) {
+ astError( AST__BADIN,
+ "astRead(%s): A spectral coordinate conversion "
+ "type is missing from the input SpecMap data.", status,
+ astGetClass( channel ) );
+
+/* Otherwise, convert the string representation into the required
+ conversion type code. */
+ } else {
+ new->cvttype[ icvt ] = CvtCode( sval, status );
+
+/* If the string was not recognised, report an error. */
+ if ( new->cvttype[ icvt ] == AST__SPEC_NULL ) {
+ astError( AST__BADIN,
+ "astRead(%s): Invalid spectral conversion "
+ "type \"%s\" in SpecMap data.", status,
+ astGetClass( channel ), sval );
+ }
+ }
+
+/* Free the memory holding the string value. */
+ sval = astFree( sval );
+ }
+
+/* Obtain the number of arguments associated with the conversion and
+ allocate memory to hold them. */
+ (void) CvtString( new->cvttype[ icvt ], &comment, &argra,
+ &argdec, &nargs, &szargs, argdesc, status );
+ new->cvtargs[ icvt ] = astMalloc( sizeof( double ) *
+ (size_t) szargs );
+
+/* Read in data for each argument... */
+ if ( astOK ) {
+ for ( iarg = 0; iarg < szargs; iarg++ ) {
+
+/* Arguments. */
+/* ---------- */
+/* Create an appropriate keyword and read each argument value. */
+ (void) sprintf( key, "spec%d%c", icvt + 1, ALPHABET[ iarg ] );
+ new->cvtargs[ icvt ][ iarg ] = astReadDouble( channel, key,
+ AST__BAD );
+ }
+ }
+
+/* Quit looping if an error occurs. */
+ if ( !astOK ) break;
+ }
+ }
+
+/* If an error occurred, clean up by deleting the new SpecMap. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return the new SpecMap pointer. */
+ return new;
+
+/* Undefine macros local to this function. */
+#undef KEY_LEN
+}
+
+/* Virtual function interfaces. */
+/* ============================ */
+/* These provide the external interface to the virtual functions defined by
+ this class. Each simply checks the global error status and then locates and
+ executes the appropriate member function, using the function pointer stored
+ in the object's virtual function table (this pointer is located using the
+ astMEMBER macro defined in "object.h").
+
+ Note that the member function may not be the one defined here, as it may
+ have been over-ridden by a derived class. However, it should still have the
+ same interface. */
+void astSpecAdd_( AstSpecMap *this, const char *cvt, const double args[], int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,SpecMap,SpecAdd))( this, cvt, args, status );
+}
+
+
+
+