diff options
Diffstat (limited to 'ast/fitschan.c')
-rw-r--r-- | ast/fitschan.c | 43747 |
1 files changed, 0 insertions, 43747 deletions
diff --git a/ast/fitschan.c b/ast/fitschan.c deleted file mode 100644 index ae7c36e..0000000 --- a/ast/fitschan.c +++ /dev/null @@ -1,43747 +0,0 @@ -/* -*class++ -* Name: -* FitsChan - -* Purpose: -* I/O Channel using FITS header cards to represent Objects. - -* Constructor Function: -c astFitsChan -f AST_FITSCHAN - -* Description: -* A FitsChan is a specialised form of Channel which supports I/O -* operations involving the use of FITS (Flexible Image Transport -* System) header cards. Writing an Object to a FitsChan (using -c astWrite) will, if the Object is suitable, generate a -f AST_WRITE) will, if the Object is suitable, generate a -* description of that Object composed of FITS header cards, and -* reading from a FitsChan will create a new Object from its FITS -* header card description. -* -* While a FitsChan is active, it represents a buffer which may -* contain zero or more 80-character "header cards" conforming to -* FITS conventions. Any sequence of FITS-conforming header cards -* may be stored, apart from the "END" card whose existence is -* merely implied. The cards may be accessed in any order by using -* the FitsChan's integer Card attribute, which identifies a "current" -* card, to which subsequent operations apply. Searches -c based on keyword may be performed (using astFindFits), new -c cards may be inserted (astPutFits, astPutCards, astSetFits<X>) and -c existing ones may be deleted (astDelFits), extracted (astGetFits<X>), -c or changed (astSetFits<X>). -f based on keyword may be performed (using AST_FINDFITS), new -f cards may be inserted (AST_PUTFITS, AST_PUTCARDS, AST_SETFITS<X>) and -f existing ones may be deleted (AST_DELFITS), extracted -f (AST_GETFITS<X>) or changed (AST_SETFITS<X>). -* -* When you create a FitsChan, you have the option of specifying -* "source" and "sink" functions which connect it to external data -* stores by reading and writing FITS header cards. If you provide -* a source function, it is used to fill the FitsChan with header cards -* when it is accessed for the first time. If you do not provide a -* source function, the FitsChan remains empty until you explicitly enter -c data into it (e.g. using astPutFits, astPutCards, astWrite -f data into it (e.g. using AST_PUTFITS, AST_PUTCARDS, AST_WRITE -* or by using the SourceFile attribute to specifying a text file from -* which headers should be read). When the FitsChan is deleted, any -* remaining header cards in the FitsChan can be saved in either of -* two ways: 1) by specifying a value for the SinkFile attribute (the -* name of a text file to which header cards should be written), or 2) -* by providing a sink function (used to to deliver header cards to an -* external data store). If you do not provide a sink function or a -* value for SinkFile, any header cards remaining when the FitsChan -* is deleted will be lost, so you should arrange to extract them -* first if necessary -c (e.g. using astFindFits or astRead). -f (e.g. using AST_FINDFITS or AST_READ). -* -* Coordinate system information may be described using FITS header -* cards using several different conventions, termed -* "encodings". When an AST Object is written to (or read from) a -* FitsChan, the value of the FitsChan's Encoding attribute -* determines how the Object is converted to (or from) a -* description involving FITS header cards. In general, different -* encodings will result in different sets of header cards to -* describe the same Object. Examples of encodings include the DSS -* encoding (based on conventions used by the STScI Digitised Sky -* Survey data), the FITS-WCS encoding (based on a proposed FITS -* standard) and the NATIVE encoding (a near loss-less way of -* storing AST Objects in FITS headers). -* -* The available encodings differ in the range of Objects they can -* represent, in the number of Object descriptions that can coexist -* in the same FitsChan, and in their accessibility to other -* (external) astronomy applications (see the Encoding attribute -* for details). Encodings are not necessarily mutually exclusive -* and it may sometimes be possible to describe the same Object in -* several ways within a particular set of FITS header cards by -* using several different encodings. -* -c The detailed behaviour of astRead and astWrite, when used with -f The detailed behaviour of AST_READ and AST_WRITE, when used with -* a FitsChan, depends on the encoding in use. In general, however, -c all successful use of astRead is destructive, so that FITS header cards -f all successful use of AST_READ is destructive, so that FITS header cards -* are consumed in the process of reading an Object, and are -* removed from the FitsChan (this deletion can be prevented for -* specific cards by calling the -c astRetainFits function). -f AST_RETAINFITS routine). -* An unsuccessful call of -c astRead -f AST_READ -* (for instance, caused by the FitsChan not containing the necessary -* FITS headers cards needed to create an Object) results in the -* contents of the FitsChan being left unchanged. -* -* If the encoding in use allows only a single Object description -* to be stored in a FitsChan (e.g. the DSS, FITS-WCS and FITS-IRAF -c encodings), then write operations using astWrite will -f encodings), then write operations using AST_WRITE will -* over-write any existing Object description using that -* encoding. Otherwise (e.g. the NATIVE encoding), multiple Object -* descriptions are written sequentially and may later be read -* back in the same sequence. - -* Inheritance: -* The FitsChan class inherits from the Channel class. - -* Attributes: -* In addition to those attributes common to all Channels, every - -* FitsChan also has the following attributes: -* -* - AllWarnings: A list of the available conditions -* - Card: Index of current FITS card in a FitsChan -* - CardComm: The comment of the current FITS card in a FitsChan -* - CardName: The keyword name of the current FITS card in a FitsChan -* - CardType: The data type of the current FITS card in a FitsChan -* - CarLin: Ignore spherical rotations on CAR projections? -* - CDMatrix: Use a CD matrix instead of a PC matrix? -* - Clean: Remove cards used whilst reading even if an error occurs? -* - DefB1950: Use FK4 B1950 as default equatorial coordinates? -* - Encoding: System for encoding Objects as FITS headers -* - FitsAxisOrder: Sets the order of WCS axes within new FITS-WCS headers -* - FitsDigits: Digits of precision for floating-point FITS values -* - Iwc: Add a Frame describing Intermediate World Coords? -* - Ncard: Number of FITS header cards in a FitsChan -* - Nkey: Number of unique keywords in a FitsChan -* - PolyTan: Use PVi_m keywords to define distorted TAN projection? -* - SipReplace: Replace SIP inverse transformation? -* - SipOK: Use Spitzer Space Telescope keywords to define distortion? -* - SipReplace: Replace SIP inverse transformation? -* - TabOK: Should the FITS "-TAB" algorithm be recognised? -* - Warnings: Produces warnings about selected conditions - -* Functions: -c In addition to those functions applicable to all Channels, the -c following functions may also be applied to all FitsChans: -f In addition to those routines applicable to all Channels, the -f following routines may also be applied to all FitsChans: -* -c - astDelFits: Delete the current FITS card in a FitsChan -c - astEmptyFits: Delete all cards in a FitsChan -c - astFindFits: Find a FITS card in a FitsChan by keyword -c - astGetFits<X>: Get a keyword value from a FitsChan -c - astGetTables: Retrieve any FitsTables from a FitsChan -c - astPurgeWCS: Delete all WCS-related cards in a FitsChan -c - astPutCards: Stores a set of FITS header card in a FitsChan -c - astPutFits: Store a FITS header card in a FitsChan -c - astPutTable: Store a single FitsTable in a FitsChan -c - astPutTables: Store multiple FitsTables in a FitsChan -c - astReadFits: Read cards in through the source function -c - astRemoveTables: Remove one or more FitsTables from a FitsChan -c - astRetainFits: Ensure current card is retained in a FitsChan -c - astSetFits<X>: Store a new keyword value in a FitsChan -c - astShowFits: Display the contents of a FitsChan on standard output -c - astTableSource: Register a source function for FITS table access -c - astTestFits: Test if a keyword has a defined value in a FitsChan -c - astWriteFits: Write all cards out to the sink function -f - AST_DELFITS: Delete the current FITS card in a FitsChan -f - AST_EMPTYFITS: Delete all cards in a FitsChan -f - AST_FINDFITS: Find a FITS card in a FitsChan by keyword -f - AST_GETFITS<X>: Get a keyword value from a FitsChan -f - AST_GETTABLES: Retrieve any FitsTables from a FitsChan -f - AST_PURGEWCS: Delete all WCS-related cards in a FitsChan -f - AST_PUTCARDS: Stores a set of FITS header card in a FitsChan -f - AST_PUTFITS: Store a FITS header card in a FitsChan -f - AST_PUTTABLE: Store a single FitsTables in a FitsChan -f - AST_PUTTABLES: Store multiple FitsTables in a FitsChan -f - AST_READFITS: Read cards in through the source function -f - AST_REMOVETABLES: Remove one or more FitsTables from a FitsChan -f - AST_RETAINFITS: Ensure current card is retained in a FitsChan -f - AST_SETFITS<X>: Store a new keyword value in a FitsChan -c - AST_SHOWFITS: Display the contents of a FitsChan on standard output -f - AST_TABLESOURCE: Register a source function for FITS table access -f - AST_TESTFITS: Test if a keyword has a defined value in a FitsChan -f - AST_WRITEFITS: Write all cards out to the sink function - -* Copyright: -* Copyright (C) 1997-2006 Council for the Central Laboratory of the -* Research Councils -* Copyright (C) 2008-2011 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 Berry (Starlink) -* RFWS: R.F. Warren-Smith (Starlink, RAL) -* TIMJ: Tim Jenness (JAC, Hawaii) - -* History: -* 11-DEC-1996 (DSB): -* Original version. -* 20-MAR-1997 (DSB): -* Made keyword setting and getting functions protected instead of -* public. Renamed public methods. Added Ncard attribute. -* 20-MAY-1997 (RFWS): -* Tidied public prologues. -* 30-JUN-1997 (DSB): -* Added support for reading post-2000 DATE-OBS strings. Reading DSS -* or FITS-WCS objects now returns NULL unless the FitsChan is -* positioned at the start-of-file prior to the read. Bug fixed -* which caused Ncard to be returned too large by one. Removed -* dependancy on hard-wired header and footer text in Native -* FitsChans. -* 18-AUG-1997 (DSB): -* Bug fixed in WcsNative which caused incorrect CRVAL values -* to be used if the axes needed permuting. Values assigned to the -* Projection attribute fo the SkyFrames created by astRead. -* 2-SEP-1997 (DSB): -* Added the IRAF convention that EPOCH=0.0 really means EPOCH=1950.0 -* (the EPOCH keyword is deprecated in the new FITS-WCS conventions -* and is taken always as a Besselian epoch). -* 19-SEP-1997 (DSB): -* Corrected interpretation of the FITS CD matrix. -* 25-SEP-1997 (DSB): -* o Fix bug in LinearMap which caused it always to detect a linear -* mapping. For instance, this allowed DssMaps to be erroneously -* written out using FITS-WCS encoding with a CAR projection. -* o Assign a full textual description to SkyFrame's Projection -* attribute instead of a 3 letter acronym. -* o If DATE-OBS >= 1999.0 then DATE-OBS is now written in new -* Y2000 format. For DATE-OBS < 1999.0, the old format is written. -* o Add new attribute CDMatrix to determine whether PC or CD -* matrices should be used when writing objects using FITS-WCS -* encoding. -* o Modified the way floating point values are formatted to omit -* unnecessary leading zeros from the exponent (i.e. E-5 instead of -* E-05). -* o New-line characters at the end of supplied header cards are now -* ignored. -* o Cater for EQUINOX specified as a string prefixed by B or J -* rather than as a floating point value (some HST data does this). -* o Corrected SetValue so that it always inserts comment cards -* rather than over-write existing comment cards. Previously, -* writing a FrameSet to a DSS encoded FitsChan resulted in all -* comments cards being stripped except for the last one. -* o Reading a FrameSet from a DSS-encoded FrameSet now only -* removes the keywords actually required to construct the FrameSet. -* Previously, all keywords were removed. -* o The EPOCH and EQUINOX keywords created when a FrameSet is -* written to a DSS-encoded FitsChan are now determined from the -* epoch and equinox of the current Frame, instead of from a copy -* of the original FitsChan stored within the DssMap. -* o The Encoding and CDMatrix attributes, and keyword types are -* now stored as strings externally instead of integers. -* 11-NOV-1997 (DSB): -* o Assume default of j2000 for DSS EQUINOX value. -* o Check for null object pointers in the interfaces for -* virtual functions which execute even if an error has previously -* occurred. Otherwise, a segmentation violation can occur when -* trying to find the member function pointer. -* o Trailing spaces ignored in Encoding attribute. -* o Bugs fixed in FindWcs and SetValue which resulted in WCS cards -* being written at the wrong place if the supplied FitsChan does not -* contain any WCS keywords. -* o Default for CDMatrix (if no axis rotation keywords can be found) -* changed to 2 (i.e. use "CDi_j" form keywords). -* o Write now leaves the current card unchanged if nothing is -* written to the FitsChan. -* 17-NOV-1997 (RFWS): -* Disabled use of CDmatrix. Fixed initialisation problems in -* astLoadFitsChan. -* 24-NOV-1997 (DSB): -* Replace references to error code AST__OPT with AST__RDERR. -* 28-NOV-1997 (DSB): -* o Function WcsValues modified to prevent it from changing the -* current card. Previously, this could cause new cards to be -* written to the wrong place in a FITS-WCS encoded FitsChan. -* o Description of argument "value" corrected in prologue of -* function SetFits. -* o Argument "lastkey" removed from function SetValue since it -* was never used (it was a relic from a previous method of -* determining where to store new cards). Corresponding changes -* have been made to all the functions which create "lastkey" values -* or pass them on to SetValue (i.e DescWcs, WcsPrimary, WcsSecondary, -* WriteWcs and WriteDss). -* 10-DEC-1997 (DSB): -* Bug fixed which caused the initial character designating the system -* within CTYPE value (eg E in ELON, G in GLON, etc) to be omitted. -* 1-JUN-1998 (DSB): -* CDELT values of zero are now replaced by a small non-zero value -* when creating the "pixel-to-relative physical" transformation -* matrix. Previously, zero CDELT values could cause the matrix to -* be non-invertable. -* 4-SEP-1998 (DSB): -* - Indicate that SphMaps created by this class when using FITS-WCS -* encoding all operate on the unit sphere. This aids simplification. -* - Fix a bug in StoreFits which caused CD matrices to be indexed -* incorrectly (sometimes causing floating exceptions) if they do not -* describe a celestial longitude/latitude system. -* - Changed astFindFits to ignore trailing spaces in the keyword -* template. -* - astSplit changed so that an error is not reported if a textual -* keyword value ends before column 20. -* 7-OCT-1998 (DSB): -* - Corrected test for linearity in LinearMap to include a factor -* of the test vector length. Also LinearMap now uses a simplified -* Mapping. -* 5-NOV-1998 (DSB): -* Added FITS-IRAF encoding. -* 9-NOV-1998 (DSB): -* - Corrected values of macros DSS_ENCODING and MAX_ENCODING. -* - Corrected erroneous success indication in IrafStore. -* - Included checks for bad values in function LinearMap. -* 17-NOV-1998 (DSB): -* The Domain name GRID is now given to the Base Frame in any FrameSets -* created by astRead when using FitsChans with DSS, FITS-WCS or -* FITS-IRAF encodings. -* 18-DEC-1998 (DSB): -* Check for "D" exponents in floating point keyword strings. -* 12-FEB-1998 (DSB): -* Modified EncodeFloat to avoid exceeding the 20 character FITS -* limit wherever possible if FitsDigits is positive. -* 10-MAY-1998 (DSB): -* Bug fixed in astSplit which caused comments associated with string -* keywords to be lost when storing the card in a FitsChan. -* 15-JUN-1999 (DSB): -* Report an error if an unrecognised projection name is supplied. -* 9-DEC-1999 (DSB): -* - Fixed bug in WcsNatPole which could result in longitude values -* being out by 180 degrees for cylindrical projections such as CAR. -* - Only report an "unrecognised projection" error for CTYPE values -* which look like celestial longitude or latitude axes (i.e. if the -* first 4 characters are "RA--", "DEC-", "xLON" or "xLAT", and the -* fifth character is "-"). -* - Added function SpecTrans to translated keywords related to the -* IRAF ZPX projection into keyword for the standard ZPN projection. -* - Add ICRS as a valid value for the RADECSYS keyword. Since the -* SkyFrame class does not yet support ICRS, an FK5 SkyFrame is -* created if RADECSYS=ICRS. -* 16-DEC-1999 (DSB): -* - Modified SpecTrans so that all keywords used to created a -* standard WCS representation from a non-standard one are consumed -* by the astRead operation. -* - Changed the text of ASTWARN cards added to the FitsChan if an -* IRAF ZPX projection is found to require unsupported corrections. -* - Simplified the documentation describing the handling of the IRAF -* ZPX projection. -* - Fixed code which assumed that the 10 FITS-WCS projection -* parameters were PROJP1 -> PROJP10. In fact they are PROJP0 - -* PROJP9. This could cause projection parameter values to be -* incorrectly numbered when they are written out upon deletion of -* the FitsChan. -* 1-FEB-2000 (DSB): -* Check that FITS_IRAF encoding is not being used before using a -* PC matrix when reading WCS information from a header. This is -* important if the header contains both PC and CD matrices. -* 8-FEB-2000 (DSB): -* - Header cards are now only consumed by an astRead operation if the -* operation succeeds (i.e. returns a non-null Object). -* - The original FITS-WCS encoding has been renamed as FITS-PC (to -* indicate the use of a PCiiijjj matrix), and a new FITS-WCS -* encoding has been added. -* - The disabled CDMatrix attribute has been removed. -* - Bug in LinearMap corrected which prevented genuinely linear -* Mappings from being judged to be linear. This bug was previously -* fudged (so it now appears) by the introduction of the test vector -* length factor (see History entry for 7-OCT-1998). This test -* vector length scale factor has consequently now been removed. -* - Added FITS-AIPS encoding. -* - The critical keywords used to select default encoding have been -* changed. -* - Support for common flavours of IRAF TNX projections added. -* - The algorithm used to find a WcsMap in the supplied FrameSet -* has been improved so that compound Mappings which contain complex -* mixtures of parallel and serial Mappings can be translated into -* FITS-WCS encoding. -* - Trailing white space in string keyword values is now retained -* when using foreign encodings to enable correct concatenation where -* a string has been split over several keywords. E.g. if 2 string -* keywords contain a list of formatted numerical values (e.g. IRAF -* WAT... keywords), and the 1st one ends "0.123 " and the next one -* begins "1234.5 ", the trailing space at the end of the first keyword -* is needed to prevent the two numbers being merged into "0.1231234.5". -* Trailing spaces in native encodings is still protected by enclosing -* the whole string in double quotes. -* - The Channel methods WriteString and GetNextData can now save -* and restore strings of arbitary length. This is done by storing -* as much of the string as possible in the usual way, and then -* storing any remaining characters in subsequent CONTINUE cards, -* using the FITSIO conventions. This storage and retrieval of long -* strings is only available for native encodings. -* 19-MAY-2000 (DSB): -* Added attribute Warnings. Lowered DSS in the priority list -* of encodings implemented by GetEncoding. -* 6-OCT-2000 (DSB): -* Increased size of buffers used to store CTYPE values to take -* account of the possiblity of lots of trailing spaces. -* 5-DEC-2000 (DSB): -* Add support for the WCSNAME FITS keyword. -* 12-DEC-2000 (DSB): -* Add a title to each physical, non-celestial coord Frame based on -* its Domain name (if any). -* 3-APR-2001 (DSB): -* - Use an "unknown" celestial coordinate system, instead of a -* Cartesian coordinate system, if the CTYPE keywords specify an -* unknown celestial coordinate system. -* - Do not report an error if there are no CTYPE keywords in the -* header (assume a unit mapping, like in La Palma FITS files). -* - Add a NoCTYPE warning condition. -* - Added AllWarnings attribute. -* - Ensure multiple copies of identical warnings are not produced. -* - Use the Object Ident attribute to store the identifier letter -* associated with each Frame read from a secondary axis description, -* so that they can be given the same letter when they are written -* out to a new FITS file. -* 10-AUG-2001 (DSB): -* - Corrected function value returned by SkySys to be 1 unless an -* error occurs. This error resulted in CAR headers being produced -* by astWrite with CRVAL and CD values till in radians rather than -* degrees. -* - Introduced SplitMap2 in order to guard against producing -* celestial FITS headers for a Mapping which includes more than -* one WcsMap. -* 13-AUG-2001 (DSB): -* - Modified FixNew so that it retains the current card index if possible. -* This fixed a bug which could cause headers written out using Native -* encodings to be non-contiguous. -* - Corrected ComBlock to correctly remove AST comment blocks in -* native encoded fitschans. -* 14-AUG-2001 (DSB): -* - Modified FixUsed so that it it does not set the current card -* back to the start of file if the last card in the FitsChan is -* deleted. -* 16-AUG-2001 (DSB): -* Modified WcsNative to limit reference point latitude to range -* +/-90 degs (previously values outside this range were wrapped -* round onto the opposite meridian). Also added new warning -* condition "badlat". -* 23-AUG-2001 (DSB): -* - Re-write LinearMap to use a least squares fit. -* - Check that CDj_i is not AST__BAD within WcsWithWcs when -* forming the increments along each physical axis. -* 28-SEP-2001 (DSB): -* GoodWarns changed so that no error is reported if a blank list -* of conditions is supplied. -* 12-OCT-2001 (DSB): -* - Added DefB1950 attribute. -* - Corrected equations which calculate CROTA when writing -* FITS-AIPS encodings. -* - Corrected equations which turn a CROTA value into a CD matrix. -* 29-NOV-2001 (DSB): -* Corrected use of "_" and "-" characters when referring to FK4-NO-E -* system in function SkySys. -* 20-FEB-2002 (DSB) -* Added CarLin attribute. -* 8-MAY-2002 (DSB): -* Correct DSSToStore to ignore trailing blanks in the PLTDECSN -* keyword value. -* 9-MAY-2002 (DSB): -* Correct GetCard to avoid infinite loop if the current card has -* been marked as deleted. -* 25-SEP-2002 (DSB): -* AIPSFromStore: use larger of coscro and sincro when determining -* CDELT values. Previously a non-zero coscro was always used, even -* if it was a s small as 1.0E-17. -* 3-OCT-2002 (DSB): -* - SkySys: Corrected calculation of longitude axis index for unknown -* celestial systems. -* - SpecTrans: Corrected check for latcor terms for ZPX projections. -* - WcsFrame: Only store an explicit equinox value in a skyframe if -* it needs one (i.e. if the system is ecliptic or equatorial). -* - WcsWithWcs: For Zenithal projections, always use the default -* LONPOLE value, and absorb any excess rotation caused by this -* into the CD matrix. -* - WcsWithWcs: Improve the check that the native->celestial mapping -* is a pure rotation, allowing for rotations which change the -* handed-ness of the system (if possible). -* - WcsWithWcs: Avoid using LONPOLE keywords when creating headers -* for a zenithal projection. Instead, add the corresponding rotation -* into the CD matrix. -* 22-OCT-2002 (DSB): -* - Retain leading and trailing white space within COMMENT cards. -* - Only use CTYPE comments as axis labels if all non-celestial -* axes have a unique non-blank comment (otherwise use CTYPE -* values as labels). -* - Updated to use latest FITS-WCS projections. This means that the -* "TAN with projection terms" is no longer a standard FITS -* projection. It is now represented using the AST-specific TPN -* projection (until such time as FITS-WCS paper IV is finished). -* - Remove trailing "Z" from DATE-OBS values created by astWrite. -* 14-NOV-2002 (DSB): -* - WcsWithWcs: Corrected to ignore longitude axis returned by -* astPrimaryFrame since it does not take into account any axis -* permutation. -* 26-NOV-2002 (DSB): -* - SpecTrans: Corrected no. of characters copied from CTYPE to PRJ, -* (from 5 to 4), and terminate PRJ correctly. -* 8-JAN-2003 (DSB): -* Changed private InitVtab method to protected astInitFitsChanVtab -* method. -* 22-JAN-2003 (DSB): -* Restructured the functions used for reading FITS_WCS headers to -* make the distinction between the generic parts (pixel->intermediate -* world coordinates) and the specialised parts (e.g. celestial, -* spectral, etc) clearer. -* 31-JAN-2003 (DSB) -* - Added Clean attribute. -* - Corrected initialisation and defaulting of CarLin and DefB1950 -* attributes. -* - Extensive changes to allow foreign encodings to be produced in -* cases where the Base Frame has fewer axes than the Current Frame. -* 12-FEB-2003 (DSB) -* - Modified SetFits so that the existing card comment is retained -* if the new data value equals the existing data value. -* 30-APR-2003 (DSB): -* - Revert to standard "TAN" code for distorted tan projections, -* rather than using the "TPN" code. Also recognise QVi_m (produced -* by AUTOASTROM) as an alternative to PVi_m when reading distorted -* TAN headers. -* 22-MAY-2003 (DSB): -* Modified GetEncoding so that the presence of RADECSYS and/or -* PROJPm is only considered significant if the modern equivalent -* keyword (REDESYS or PVi_m) is *NOT* present. -* 2-JUN-2003 (DSB): -* - Added support for PCi_j kewwords within FITS-WCS encoding -* - Added CDMatrix attribute -* - Changed internal FitsStore usage to use PC/CDELT instead of CD -* (as preparation for FITS-WCS paper IV). -* - Added warning "BadMat". -* 11-JUN-2003 (DSB): -* - Modified WcsNative to use the new SphMap PolarLong attribute -* in order to ensure correct propagation of the longitude CRVAL -* value in cases where the fiducial point is coincident with a pole. -* - Use PVi_3 and PVi_4 for longitude axis "i" (if present) in -* preference to LONPOLE and LATPOLE when reading a FITS-WCS header. -* Note, these projection values are never written out (LONPOLE and -* LATPOLE are written instead). -* - Associate "RADESYS=ICRS" with SkyFrame( "System=ICRS" ), rather -* than SkyFrame( "System=FK5" ). -* - If DefB1950 is zero, use ICRS instead of FK5 as the default RADESYS -* if no EQUINOX is present. -* 1-SEP-2003 (DSB): -* - Modify Dump so that it dumps all cards including those flagged as -* having been read. -* - Added "reset" parameter to FixUsed. -* - WcsMapFrm: store an Ident of ' ' for the primary coordinate -* description (previously Ident was left unset) -* - Default value for DefB1950 attribute now depends on the value -* of the Encoding attribute. -* 15-SEP-2003 (DSB): -* - Added Warnings "BadVal", "Distortion". -* - Ignore FITS-WCS paper IV CTYPE distortion codes (except for -* "-SIP" which is interpreted correctly on reading). -* 22-OCT-2003 (DSB): -* - GetEncoding: If the header contains CDi_j but does not contain -* any of the old IRAF keywords (RADECSYS, etc) then assume FITS-WCS -* encoding. This allows a FITS-WCS header to have both CDi_j *and* -* CROTA keywords. -* 5-JAN-2004 (DSB): -* - SpecTrans: Use 1.0 (instead of the CDELT value) as the -* diagonal PCi_j term for non-celestial axes with associated CROTA -* values. -* 12-JAN-2004 (DSB): -* - CelestialAxes: Initialise "tmap1" pointer to NULL in case of error -* (avoids a segvio happening in the case of an error). -* - AddVersion: Do not attempt to add a Frame into the FITS header -* if the mapping from grid to frame is not invertable. -* - WorldAxes: Initialise the returned "perm" values to safe values, -* and return these values if no basis vectors cen be created. -* 19-JAN-2004 (DSB): -* - When reading a FITS-WCS header, allow all keywords to be defaulted -* as decribed in paper I. -* 27-JAN-2004 (DSB): -* - Modify FitLine to use correlation between actual and estimated -* axis value as the test for linearity. -* - Modify RoundFString to avoid writing beyond the end of the -* supplied buffer if the supplied string contains a long list of 9's. -* 11-MAR-2004 (DSB): -* - Modified SpecTrans to check all axis descriptions for keywords -* to be translated. -* 19-MAR-2004 (DSB): -* - Added astPutCards to support new fits_hdr2str function in -* CFITSIO. -* 25-MAR-2004 (DSB): -* - Corrected bug in astSplit which causes legal cards to be -* rejected because characters beyond the 80 char limit are being -* considered significant. -* - Corrected bug in SpecTrans which caused QV keywords to be -* ignored. -* 15-APR-2004 (DSB): -* - SpecTrans modified to include translation of old "-WAV", "-FRQ" -* and "-VEL" spectral algorithm codes to modern "-X2P" form. -* - WcsFromStore modified to supress creation of WCSAXES keywords -* for un-used axis versions. -* - IsMapLinear modified to improve fit by doing a second least -* squares fit to the residualleft by the first least squares fit. -* 16-APR-2004 (DSB): -* - NonLinSpecWcs: Issue a warning if an illegal non-linear -* spectral code is encountered. -* - Add a BadCTYPE warning condition. -* - Corrected default value for Clean so that it is zero (as -* documented). -* 21-APR-2004 (DSB): -* - FindWcs: Corrected to use correct OBSGEO template. This bug -* caused OBSGEO keywords to be misplaced in written headers. -* 23-APR-2004 (DSB): -* - SplitMap: Modified so that a Mapping which has celestial axes -* with constant values (such as produced by a PermMap) are treated -* as a valid sky coordinate Mapping. -* - AddFrame modified so that WCS Frames with a different number -* of axes to the pixel Frame can be added into the FrameSet. -* - IRAFFromStore and AIPSFromStore modified so that they do not -* create any output keywords if the number of WCS axes is different -* to the number of pixel axes. -* - Handling of OBSGEO-X/Y/Z corrected again. -* - WCSFromStore modified to avouid writing partial axis descriptions. -* 26-APR-2004 (DSB): -* - Corrected text of output SPECSYS keyword values. -* 17-MAY-2004 (DSB): -* - Added IWC attribute. -* 15-JUN-2004 (DSB): -* - Ensure out-of-bounds longitude CRPIX values for CAR -* projections are wrapped back into bounds. -* 21-JUN-2004 (DSB): -* - Ensure primary MJD-OBS value is used when reading foreign FITS -* headers. -* 7-JUL-2004 (DSB): -* - Issue errors if an un-invertable PC/CD matrix is supplied in a -* FITS-WCS Header. -* 11-JUL-2004 (DSB): -* - Re-factor code for checking spectral axis CTYPE values into -* new function IsSpectral. -* - Modify AIPSFromSTore to create spectral axis keywords if -* possible. -* - Modify SpecTrans to recognize AIPS spectral axis keywords, and -* to convert "HZ" to "Hz". -* - Added FITS-AIPS++ encoding. -* 12-AUG-2004 (DSB): -* - Convert GLS projection codes to equivalent SFL in SpecTrans. -* - Added FITS-CLASS encoding. -* 16-AUG-2004 (DSB): -* - Removed support for paper III keyword VSOURCE, and added -* support for SSYSSRC keyword. -* - Added initial support for CLASS encoding. -* - In FitOK: Changed tolerance for detecting constant values -* from 1.0E-10 to 1.0E-8. -* 17-AUG-2004 (DSB): -* Correct GetFiducialNSC so that the stored values for longitude -* parameters 1 and 2 are ignored unless the value of parameter 0 is -* not zero. -* 19-AUG-2004 (DSB): -* Modify SpecTrans to ignore any CDELT values if the header -* includes some CDi_j values. -* 26-AUG-2004 (DSB): -* Modify astSplit_ to allow floating point keyword values which -* include an exponent to be specified with no decimal point -* (e.g. "2E-4"). -* 27-AUG-2004 (DSB): -* Completed initial attempt at a FITS-CLASS encoding. -* 9-SEP-2004 (DSB): -* Fixed usage of uninitialised values within ReadCrval. -* 13-SEP-2004 (DSB): -* Check the "text" pointer can be used safely before using it in -* DSSToStore. -* 27-SEP-2004 (DSB): -* In SpecTrans, before creating new PCi_j values, check that no -* PCi_j values have been created via an earlier translation. -* 28-SEP-2004 (DSB): -* In AIPSPPFromStore only get projection parameters values if there -* are some celestialaxes. Also allow CROTA to describe rotation of -* non-celestial axes (same for AIPSFromSTore). -* 4-OCT-2004 (DSB): -* Correct rounding of CRPIX in AddVersion to avoid integer overflow. -* 11-NOV-2004 (DSB): -* - WcsFcRead: Avoid issuing warnings about bad keywords which -* have already been translated into equivalent good forms. -* - SpecTrans: If both PROJP and PV keywords are present, use PV -* in favour of PROJP only if the PV values look correct. -* 17-NOV-2004 (DSB): -* - Make astSetFits<X> public. -* 16-MAR-2005 (DSB): -* - Primary OBSGEO-X/Y/Z, MJD-AVG and MJDOBS keywords are associated -* with all axis descriptions and should not have a trailing single -* character indicating an alternate axis set. -* 9-AUG-2005 (DSB): -* In WcsMapFrm, check reffrm is used before annulling it. -* 8-SEP-2005 (DSB): -* - Change "if( a < b < c )" constructs to "if( a < b && b < c )" -* - DSBSetup: correct test on FrameSet pointer state -* - Ensure CLASS keywords written to a FitsChan do not come before -* the final fixed position keyword. -* 9-SEP-2005 (DSB): -* - Added "AZ--" and "EL--" as allowed axis types in FITS-WCS -* ctype values. -* 12-SEP-2005 (DSB): -* - Cast difference between two pointers to (int) -* - CLASSFromStore:Check source velocity is defined before -* storing it in the output header. -* 13-SEP-2005 (DSB): -* - Corrected B1940 to B1950 in AddEncodingFrame. This bug -* prevented some FrameSets being written out using FITS-CLASS. -* - Rationalise the use of the "mapping" pointer in AddVersion. -* - WcsCelestial: Modified so that the FITS reference point is -* stored as the SkyFrame SkyRef attribute value. -* 7-OCT-2005 (DSB): -* Make astGetFits<X> public. -* 30-NOV-2005 (DSB): -* Add support for undefined FITS keyword values. -* 5-DEC-2005 (DSB): -* - Include an IMAGFREQ keyword in the output when writing a -* DSBSpecFrame out using FITS-WCS encoding. -* - Correct test for constant values in FitOK. -* 7-DEC-2005 (DSB): -* Free memory allocated by calls to astReadString. -* 30-JAN-2006 (DSB): -* Modify astSplit so that it does no read the supplied card beyond -* column 80. -* 14-FEB-2006 (DSB): -* Override astGetObjSize. -* 28-FEB-2006 (DSB): -* Correct documentation typo ("NCards" -> "Ncard"). -* 5-APR-2006 (DSB): -* Modify SpecTrans to convert CTYPE="LAMBDA" to CTYPE="WAVE". -* 26-MAY-2006 (DSB): -* Guard against NULL comment pointer when converting RESTFREQ to -* RESTFRQ in SpecTrans. -* 29-JUN-2006 (DSB): -* - Added astRetainFits. -* - Consume VELOSYS FITS-WCS keywords when reading an object. -* - Write out VELOSYS FITS-WCS keywords when writing an object. -* 7-AUG-2006 (DSB): -* Remove trailing spaces from the string returned by astGetFitsS -* if the original string contains 8 or fewer characters. -* 16-AUG-2006 (DSB): -* Document non-destructive nature of unsuccessful astRead calls. -* 17-AUG-2006 (DSB): -* Fix bugs so that the value of the Clean attribute is honoured -* even if an error has occurred. -* 4-SEP-2006 (DSB): -* Modify GetClean so that it ignores the inherited status. -* 20-SEP-2006 (DSB): -* Fix memory leak in WcsSpectral. -* 6-OCT-2006 (DSB): -* Modify IsSpectral and IsAIPSSpectral to allow for CTYPE values that -* are shorter than eight characters. -* 13-OCT-2006 (DSB): -* - Ensure SpecFrames and SkyFrames created from a foreign FITS header -* are consistent in their choice of Epoch. -* - Convert MJD-OBS and MJD-AVG values from TIMESYS timescale to -* TDB before using as the Epoch value in an AstFrame. Use UTC if -* TIMESYS is absent. -* - Convert Epoch values from TDB to UTC before storing as the -* value of an MJD-OBS or MJD-AVG keyword (no TIMESYS keyword is -* written). -* 23-OCT-2006 (DSB): -* Prefer MJD-AVG over MJD-OBS. -* 30-OCT-2006 (DSB): -* In FitOK: Changed lower limit on acceptbale correlation from -* 0.999999 to 0.99999. -* 1-NOV-2006 (DSB): -* - When reading a foreign header that contains a DUT1 keyword, -* use it to set the Dut1 attribute in the SkyFrame. Note, JACH -* store DUT1 in units of days. This may clash with the FITS-WCS -* standard (when its produced). Also note that DUT1 is not written -* out as yet when writing a FrameSet to a foreign FITS header. -* - Correct bug that prevented ZSOURCE keyword being added to the -* output header if the source velocity was negative. -* 9-NOV-2006 (DSB): -* Add STATUS argument to docs for F77 AST_SETx. -* 20-DEC-2006 (DSB): -* Correct FK5 to ICRS in error message issued if no RADESYS or -* EQUINOX is found. -* 16-JAN-2007 (DSB): -* Cast ignored function return values to (void) to avoid compiler -* warnings. -* 31-JAN-2007 (DSB): -* Change SpecTrans to ignore blank unit strings (previously -* converted them to "Hz"). -* 16-APR-2007 (DSB): -* In SplitMat, increase the allowed level of rounding erros from -* 1.0E-10 to 1.0E-7 (to avoid spurious low CDi_j values being -* created that should be zero). -* 30-APR-2007 (DSB): -* - Change DSBSetup so that the central DSBSpecFrame frequency is -* CRVAL and the IF is the difference between CRVAL and LO. -* - Change tolerance in FitOK from 0.99999 to 0.995 to handle data from Nicolas -* Peretto. -* 1-MAY-2007 (DSB): -* - In astSplit, if a keyword value looks like an int but is too long to -* fit in an int, then treat it as a float instead. -* 18-MAY-2007 (DSB): -* In CnvType, use input type rather than output type when checking -* for a COMMENT card. Also, return a null data value buffer for a -* COMMENT card. -* 4-JUN-2007 (DSB): -* In CLASSFromStore, create a DELTAV header even if it is equal to -* the spectral CDELT value. Also, convert spatial reference point -* to (az,el) and write out as headers AZIMUTH and ELEVATIO. -* 9-JUL-2007 (DSB): -* Fixed bug in DSBSetUp - previously, this function assumed that -* the supplied DSBSpecFrame represented frequency, and so gave -* incorrect values for IF and DSBCentre if the header described -* velocity. -* 9-AUG-2007 (DSB): -* Changed GetEncoding so that critcal keywords are ignored if -* there are no CTYPE, CRPIX or CRVAL keywords in the header. -* 10-AUG-2007 (DSB): -* - Changed GetEncoding so that FITS_PC is not returned if there are -* any CDi_j or PCi_j keywords in the header. -* - Added astPurgeWCS method. -* 13-AUG-2007 (DSB): -* - Include the DSS keywords AMDX%d and AMDY%d in FindWCS. -* 16-AUG-2007 (DSB): -* - Force all FITS-CLASS headers to contain frequency axes -* (velocity axes seem not to be recognised properly by CLASS). -* - Change the CLASS "VELO-LSR" header to be the velocity at the -* reference channel, not the source velocity. -* 22-AUG-2007 (DSB): -* - Remove debugging printf statements. -* 20-SEP-2007 (DSB): -* Changed FitOK to check that the RMS residual is not more than -* a fixed small fraction of the pixel size. -* 4-DEC-2007 (DSB): -* Changed CreateKeyword so that it uses a KeyMap to search for -* existing keywords. This is much faster than checking every -* FitsCard in the FitsChan explicitly. -* 18-DEC-2007 (DSB): -* Add keyword VLSR to the CLASS encoding. It holds the same value -* as VELO-LSR, but different versions of class use different names. -* Also write out the DELTAV keyword in the LSR rest frame rather -* than the source rest frame. -* 31-JAN-2008 (DSB): -* Correct calculation of redshift from radio velocity in ClassTrans. -* 25-FEB-2008 (DSB): -* Ensure a SkyFrame represents absolute (rather than offset) -* coords before writing it out in any non-native encoding. -* 28-FEB-2008 (DSB): -* Test for existing of SkyRefIs attribute before accessing it. -* 2-APR-2008 (DSB): -* In CLASSFromStore, adjust the spatial CRVAL and CRPIX values to be -* the centre of the first pixel if the spatial axes are degenerate. -* 17-APR-2008 (DSB): -* Ignore latitude axis PV terms supplied in a TAN header -* (previously, such PV terms were used as polynomial correction -* terms in a TPN projection). -* 30-APR-2008 (DSB): -* SetValue changed so that new keywords are inserted before the -* current card. -* 1-MAY-2008 (DSB): -* Added UndefRead warning. -* 7-MAY-2008 (DSB): -* Correct conversion of CDi_j to PCi_j/CDELT in SpecTrans. -* 8-MAY-2008 (DSB): -* When writing out a FITS-WCS header, allow linear grid->WCS -* mapping to be represented by a CAR projection. -* 9-MAY-2008 (DSB): -* Make class variables IgnoreUsed and MarkNew static. -* 30-JUN-2008 (DSB): -* Improve efficiency of FindWcs. -* 2-JUL-2008 (DSB): -* FitsSof now returns non-zero if the FitsChan is empty. -* 16-JUL-2008 (DSB): -* Plug memory leak caused by failure to free the Warnings -* attribute string when a FitsChan is deleted. -* 24-JUL-2008 (TIMJ): -* Fix buffer overrun in astGetFits when writing the keyword -* to the buffer (occurred if the input string was 80 characters). -* 1-OCT-2008 (DSB): -* When reading a FITS-WCS header, spurious PVi_j keywords no -* longer generate an error. Instead they generate warnings via the -* new "BadPV" warning type. -* 21-NOV-2008 (DSB): -* Do not remove keywords from read headers if they may be of -* relevance to things other than WCS (e.g. MJD-OBS, OBSGEO, etc). -* 2-DEC-2008 (DSB): -* - astGetFits<X> now reports an error if the keyword value is undefined. -* - Add new functions astTestFits and astSetFitsU. -* - Remove use of AST__UNDEF<X> constants. -* - Remove "undefread" warning. -* 16-JAN-2009 (DSB): -* Use astAddWarning to store each warning in the parent Channel -* structure. -* 4-MAR-2009 (DSB): -* DATE-OBS and MJD-OBS cannot have an axis description character. -* 13-MAR-2009 (DSB): -* The VELOSYS value read from the header is never used, so do not -* report an error if VELOSYS has an undefined value. -* 11-JUN-2009 (DSB): -* Delay reading cards from the source until they are actually -* needed. Previously, the source function was called in the -* FitsChan initialiser, but this means it is not possible for -* application code to call astPutChannelData before the source -* function is called. The ReadFromSource function is now called -* at the start of each (nearly) public or protected function to -* ensure the source function has been called (the source function -* pointer in the FitsChan is then nullified to ensure it is not -* called again). -* 18-JUN-2009 (DSB): -* Include the effect of observer height (in the ObsAlt attribute) -* when creating OBSGEO-X/Y/Z headers, and store a value for -* ObsAlt when reading a set of OBSGEO-X/Y/Z headers. -* 2-JUL-2009 (DSB): -* Check FitsChan is not empty at start of FindWcs. -* 7-JUL-2009 (DSB): -* Add new function astSetFitsCM. -* 30-JUL-2009 (DSB): -* Fix axis numbering in SkyPole. -* 12-FEB-2010 (DSB): -* Use "<bad>" to represent AST__BAD externally. -* 25-JUN-2010 (DSB): -* Fix problem rounding lots of 9's in RoundFString. The problem -* only affected negative values, and could lead to an extra zero -* being included in the integer part. -* 28-JUN-2010 (DSB): -* Another problem in RoundFString! If the value has a series of -* 9's followed by a series of zeros, with no decimal point (e.g. -* "260579999000"), then the trailing zeros were being lost. -* 16-JUL-2010 (DSB): -* In SpecTrans, avoid over-writing the spatial projection code -* with the spectral projection code. -* 20-JUL-2010 (DSB): -* Correct interpretation of NCP projection code. -* 14-OCT-2010 (DSB): -* - Correct loading of FitsChans that contain UNDEF keywords. -* - Correct translation of spectral units with non-standard -* capitalisation in SpecTrans. -* 10-JAN-2011 (DSB): -* Fix memory leak in MakeIntWorld. -* 13-JAN-2011 (DSB): -* Rename astEmpty ast astEmptyFits and make public. -* 20-JAN-2011 (DSB): -* - Extensive changes to support -TAB algorithm -* - Recovery from a major unrequested reformatting of whitespace by -* my editor! -* 7-FEB-2011 (DSB): -* Put a space between keyword value and slash that starts a comment -* when formatting a FITS header card. -* 11-FEB-2011 (DSB): -* Change meaning of TabOK attribute. It is no longer a simple -* boolean indicating if the -TAB algorithm is supported. Instead -* it gives the value to be used for the EXTVER header - i.e. the -* version number to store with any binary table created as a -* result of calling astWrite. If TabOK is zero or begative, then -* the -TAB algorithm is not supported. This is so that there is -* some way of having multiple binary table extensions with the same -* name (but different EXTVER values). -* 14-FEB-2011 (DSB): -* - Spectral reference point CRVAL records the obs. centre. So for -TAB -* (when CRVAL is set to zero) we need to record the obs centre some -* other way (use the AST-specific AXREF keywords, as for spatial axes). -* - Whether to scale spatial axes from degs to rads depends on -* whether the spatial axes are descirbed by -TAB or not. -* - Relax the linearity requirement in IsMapLinear by a factor of -* 10 to prevent a change in rest frame resulting in a non-linear -* mapping. -* 17-FEB-2011 (DSB): -* Fix bug in axis linearity check (IsMapLinear). -* 22-FEB-2011 (DSB): -* The translations of AIPS non-standard CTYPE values were always -* stored as primary axis description keywords, even if the original -* non-standard CTYPE values were read from an alternative axis -* descriptions. -* 5-APR-2011 (DSB): -* In SpecTrans, correct the MSX CAR projection translation. The -* first pixel starts at GRID=0.5, not GRID=0.0. So the CRPIX value -* needs to be reduced by 0.5 prior to normalisation, and then -* increased by 0.5 after normalisation. -* 23-MAY-2011 (DSB): -* Add support for TNX projections that use Chebyshev polynomials. -* 24-MAY-2011 (DSB): -* - Add support for ZPX projections that include IRAF polynomial -* corrections. -* - Add PolyTan attribute. -* - Fix interpretation of -SIP headers that have no inverse. -* 1-JUN-2011 (DSB): -* In astInitFitsChanVtab, only create the two TimeFrames if they -* have not already been created (fixes scuba2 trac ticket #666). -* 9-JUN-2011 (DSB): -* In WCSFcRead, ignore trailing spaces when reading string values -* for WCS keywords. -* 23-JUN-2011 (DSB): -* - Override the parent astSetSourceFile method so that it reads -* headers from the SourceFile and appends them to the end of the -* FitsChan. -* - On deletion, write out the FitsChan contents to the file -* specified by the SinkFile attribute. If no file is specified, -* use the sink function specified when the FitsChan was created. -* 30-AUG-2011 (DSB): -* - Added astWriteFits and astReadFits. -* - Move the deletion of tables and warnings from Delete to -* EmptyFits. -* 21-SEP-2011 (DSB): -* - In RoundFString, remember to update the pointer to the exponent. -* This bug caused parts of the exponent to dissappear when -* formatting a value that included some trailing zeros and a -* series of adjacent 9's. -* - Added Nkey attribute. -* 22-SEP-2011 (DSB): -* - Added CardType attribute -* - Allow GetFits to be used to get/set the value of the current -* card. -* 4-OCT-2011 (DSB): -* When reading a FITS-WCFS header, if the projection is TPV (as produced -* by SCAMP), change to TPN (the internal AST code for a distorted -* TAN projection). -* 22-NOV-2011 (DSB): -* Allow the "-SIP" code to be used with non-celestial axes. -* 1-FEB-2012 (DSB): -* Write out MJD-OBS in the timescale specified by any TIMESYS -* keyword in the FitsChan, and ensure the TIMESYS value is included -* in the output header. -* 23-FEB-2012 (DSB): -* Use iauGd2gc in place of palGeoc where is saves some calculations. -* 24-FEB-2012 (DSB): -* Move invocation of AddEncodingFrame from Write to end of -* MakeFitsFrameSet. This is so that AddEncodingFrame can take -* advantage of any standardisations (such as adding celestial axes) -* performed by MakeFItsFrameSet. Without this, a FRameSet contain -* a 1D SpecFrame (no celestial axes) would fail to be exported using -* FITS-CLASS encoding. -* 29-FEB-2012 (DSB): -* Fix bug in CLASSFromStore that caused spatial axes added by -* MakeFitsFrameSet to be ignored. -* 2-MAR-2012 (DSB): -* - In CLASSFromSTore, ensure NAXIS2/3 values are stored in teh FitsChan, -* and cater for FrameSets that have only a apectral axis and no celestial -* axes (this prevented the VELO_LSR keyword being created).. -* 7-MAR-2012 (DSB): -* Use iauGc2gd in place of Geod. -* 22-JUN-2012 (DSB): -* - Check for distorted TAN projections that have zero for all PVi_m -* coefficients. Issue a warning and ignore the distortion in such -* cases. -* - Remove all set but unused variables. -* - Convert SAO distorted TAN projections (which use COi_j keywords -* for polynomial coeffs) to TPN. -* 26-JUN-2012 (DSB): -* Correct call to astKeyFields in SAOTrans (thanks to Bill Joye -* for pointing out this error). -* 8-AUG-2012 (DSB): -* Correct assignment to lonpole within CLASSFromStore. -* 10-AUG-2012 (DSB): -* Default DSS keywords CNPIX1 and CNPIX2 to zero if they are -* absent, rather than reporting an error. -* 7-DEC-2012 (DSB): -* - When writing out a FrameSet that uses an SkyFrame to describe a -* generalised spherical coordinate system ("system=unknown"), ensure -* that the generated FITS CTYPE values use FITS-compliant codes -* for the axis type ( "xxLN/xxLT" or "xLON/xLAT" ). -* - Add support for reading and writing offset SkyFrames to -* FITS-WCS. -* 30-JAN-2013 (DSB): -* When reading a FITS-CLASS header, use "VLSR" keyword if -* "VELO-..." is not available. -* 15-APR-2013 (DSB): -* Correct initialisation of missing coefficients When reading a -* SAO plate solution header. -* 16-APR-2013 (DSB): -* When determining default Encoding value, use "VLSR" keyword if -* "VELO-..." is not available. -* 30-MAY-2013 (DSB): -* Prevent seg fault caused by overrunning the coeffs array in -* WATCoeffs in cases where the TNX/ZPX projection is found to be -* of a form that cannot be implemented as a TPN projection. -* 11-JUN-2013 (DSB): -* Fix support for reading GLS projections, and add support for -* rotated GLS projections. -* 28-AUG-2013 (DSB): -* In WcsCelestial, if celestial axes are found with no projection -* code in CTYPE, assume an old-fashioned CAR projection (i.e. no -* rotation from native to WCS coords). Before this change, -* CTYPE = "RA" | "DEC" axes got treated as radians, not degrees. -* 16-SEP-2013 (DSB): -* When exporting alternate offset SkyFrames to FITS-WCS headers, -* correctly test the alternate Frame in the supplied FrameSet, rather -* than the current Frame. -* 24-SEP-2013 (DSB): -* Fix bug in choosing default value for PolyTan attribute. -* 19-OCT-2013 (DSB): -* - In SIPMapping, always ignore any inverse polynomial supplied in -* a SIP header as they seem often to be inaccurate. A new inverse is -* created to replace it. -* - In SIPMapping, only use a fit to the inverted SIP transformation -* if an accuracy of 0.01 pixel can be achieved over an area three -* times the dimensions of the image. Otherwise use an iterative -* inverse for each point. People were seeing bad round-trip errors -* when transforming points outside the image because the fit was -* being used when it was not very accurate. -* 12-NOV-2013 (DSB): -* Added CardName and CardComm attributes. -* 13-NOV-2013 (DSB): -* Use a zero-length string for the CardComm attribute if the card -* has no comment. -* 15-NOV-2013 (DSB): -* - Added method astShowFits. -* - Ensure PurgeWcs removes WCS cards even if an error occurs when -* reading FrameSets from the FitsChan. -* - Change IsMapTab1D to improve chances of a -TAB mapping being found. -* 6-JAN-2014 (DSB): -* - Allow default options for newly created FitsChans to be -* specified by the FITSCHAN_OPTIONS environment variable. -* - Ensure the used CarLin value is not changed by a trailing frequency axis. -* 9-JUL-2014 (DSB): -* Added attribute FitsAxisOrder, which allows an order to be -* specified for WCS axis within FITS headers generated using astWrite. -* 9-SEP-2014 (DSB): -* Modify Split so that any non-printing characters such as -* newlines at the end of the string are ignored. -* 30-SEP-2014 (DSB): -* Modify CnvType to indicate that comment cards cannot be -* converted to any other data type. For instance, this causes -* a warning to be issued if an equals sign is misplaced in a -* WCS-related card (causing it to be treated as a comment card). -* 24-MAR-2015 (DSB): -* Modify SpecTrans to avoid modifying the CRPIC value for CAR -* projections when they do not need to be modified. The princiuple -* is that the bulk of the array should be witin the native longitude -* range [-180,+180]. Prompted by bug report from Bill Joye "yet -* another CAR issue" on 24-MAR-2015 (file CHIPASS_Equ.head in -* ast_tester). -* 27-APR-2015 (DSB): -* Modify MakeFitsFrameSet so that isolated SkyAxes (e.g. -* individual axes that have been oicked from a SkyFrame) are -* re-mapped into degrees before being used. -* 20-APR-2015 (DSB): -* In MakeIntWorld, relax tolerance for checking that each FITS-WCS IWC -* axis is linear, from 0.01 of a pixel to 0.1 of a pixel. -* 6-JUL-2015 (DSB): -* When checking a sub-string, ensure the whole string is at least as -* long as the offset to the start of the sub-string. Without this, you -* can get erroneous sub-string matches by chance, depending on what -* characters happen to be present in memory after the end of the string. -* 11-AUG-2015 (DSB): -* - Fix bug in CheckFitsName that prevented an error from being reported -* if the FITS keyword name contained any illegal printable characters. -* - Add new Warning "badkeyname", and issue such a warning instead -* of an error if illegal characters are found in a keyword name. -* 31-AUG-2015 (DSB): -* In FitLine, use the whole axis rather than 0.1 of the axis (if "dim" -* is supplied). This is because non-linearity can become greater at -* further distances along the axis. In practice, it meant that SIP -* distortion were being treated as linear because the test did not -* explore a large enough region of pixel space. -* 12-OCT-2015 (DSB): -* Only add sky axes to a SpecFrame if the WCS Frame contains no -* other axes other than the SpecFrame (MakeFitsFrameSet). -* 17-OCT-2015 (DSB): -* - Add new Warning "badkeyvalue", and issue such a warning instead -* of an error if the Split function cannot determine the keyword value. -* - Move the check for PLTRAH (i.e. DSS encoding) higher up in GetEncoding. -* This is because some DSS file slaos have CD and/or PC keywords. -* 5-NOV-2015 (DSB): -* Fix bug in MakeFitsFrameSet that could cause an inappropriate -* RefRA and RefDec values to be used when writing out a SpecFrame -* using FITS-WCS. This bug was caused by the assumption that -* changing the current Frame of a FRameSet also changes the Frame -* that was added into the FRameSet. This used to be the case as -* astAddFrame took a clone of the supplied Frame pointer, but it -* now takes a deep copy, so the original Frame and the FrameSet's -* current Frame are now independent of each other. -* 28-JUN-2016 ((DSB): -* IsAipsSpectral: Trailing spaces in CTYPE values are insignificant. -* 17-MAR-2017 (DSB): -* Fix memory leak in MakeFitsFrameSet. -* 25-APR-2017 (DSB): -* When reading foreign WCS, retain the TIMESYS keyword by default. -* 28-APR-2017 (DSB): -* When reading a JCMT or UKIRT foreign header that contains a -* DTAI keyword, use it to set the Dtai attribute in the WCS Frame. -* 11-SEP-2017 (DSB): -* Allow a NULL keyword name to be supplied to astTestFits to -* indicate that the current card should be used (as is also done in -* astGetFits). -* 25-OCT-2017 (DSB): -* Added attribute FitsTol. -* 27-OCT-2017 (DSB): -* In RoundFString, only right justift the final string if a -* minimum field width is given. Otherwise, leave the unchanged -* characters in their original positions. Right justifying ccould -* cause very long strings to be returned. -* 6-NOV-2017 (DSB): -* In CelestialAxes, simplify the base->current mapping before -* attempting to split it. This can cause multiple WcsMaps to cancel out, -* which could otherwise prevent SplitMap from splitting the Mapping -* successfully. -* 7-NOV-2017 (DSB): -* If an IWC Frame is included in the FrameSet returned by astRead, -* ensure it comes between the pixel and sky frames in the mapping chain. -* Previously the order was PIXEL->SKY->IWC, Now it is PIXEL->IWC->SKY. -* The inter-Frame Mappings required by the new arrangment are simpler -* than for the old arrangement. -* 20-NOV-2017 (DSB) -* Added SipReplace attribute. -* 20-NOV-2017 (DSB) -* Added SipReplace attribute. -* 30-DEC-2017 (DSB): -* Add the SipOK attribute, and support for writing SIP headers. -*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 FitsChan - -/* A macro which tests a character to see if it can be used within a FITS - keyword. We include lower case letters here, but they are considered - as equivalent to upper case letter. */ -#define isFits(a) ( islower(a) || isupper(a) || isdigit(a) || (a)=='-' || (a)=='_' ) - -/* A amacro to test if a Frame is a SkyFrame, and is used to describe the - sky (skyframes could be used for other purposes - eg a POLANAL Frame). */ -#define IsASkyFrame(frame) (astIsASkyFrame(frame)&&!strcmp("SKY",astGetDomain(frame))) - -/* Macro which takes a pointer to a FitsCard and returns non-zero if the - card has been used and so should be ignored. */ -#define CARDUSED(card) ( \ - ( ignore_used == 2 && \ - ( (FitsCard *) (card) )->flags & PROVISIONALLY_USED ) || \ - ( ignore_used >= 1 && \ - ( (FitsCard *) (card) )->flags & USED ) ) - -/* Set of characters used to encode a "sequence number" at the end of - FITS keywords in an attempt to make them unique.. */ -#define SEQ_CHARS "_ABCDEFGHIJKLMNOPQRSTUVWXYZ" - -/* A general tolerance for equality between floating point values. */ -#define TOL1 10.0*DBL_EPSILON - -/* A tolerance for equality between angular values in radians. */ -#define TOL2 1.0E-10 - -/* Macro to check for equality of floating point angular values. We cannot - compare bad values directory because of the danger of floating point - exceptions, so bad values are dealt with explicitly. The smallest - significant angle is assumed to be 1E-9 radians (0.0002 arc-seconds).*/ -#define EQUALANG(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=astMAX(1.0E5*(fabs(aa)+fabs(bb))*DBL_EPSILON,1.0E-9)))) - -/* Macro to compare an angle in radians with zero, allowing some tolerance. */ -#define ZEROANG(aa) (fabs(aa)<1.0E-9) - -/* Constants: */ -#define UNKNOWN_ENCODING -1 -#define NATIVE_ENCODING 0 -#define FITSPC_ENCODING 1 -#define DSS_ENCODING 2 -#define FITSWCS_ENCODING 3 -#define FITSIRAF_ENCODING 4 -#define FITSAIPS_ENCODING 5 -#define FITSAIPSPP_ENCODING 6 -#define FITSCLASS_ENCODING 7 -#define MAX_ENCODING 7 -#define UNKNOWN_STRING "UNKNOWN" -#define NATIVE_STRING "NATIVE" -#define FITSPC_STRING "FITS-PC" -#define FITSPC_STRING2 "FITS_PC" -#define DSS_STRING "DSS" -#define FITSWCS_STRING "FITS-WCS" -#define FITSWCS_STRING2 "FITS_WCS" -#define FITSIRAF_STRING "FITS-IRAF" -#define FITSIRAF_STRING2 "FITS_IRAF" -#define FITSAIPS_STRING "FITS-AIPS" -#define FITSAIPS_STRING2 "FITS_AIPS" -#define FITSAIPSPP_STRING "FITS-AIPS++" -#define FITSAIPSPP_STRING2 "FITS_AIPS++" -#define FITSCLASS_STRING "FITS-CLASS" -#define FITSCLASS_STRING2 "FITS_CLASS" -#define INDENT_INC 3 -#define PREVIOUS 0 -#define NEXT 1 -#define HEADER_TEXT "Beginning of AST data for " -#define FOOTER_TEXT "End of AST data for " -#define FITSNAMLEN 8 -#define FITSSTCOL 20 -#define FITSRLCOL 30 -#define FITSIMCOL 50 -#define FITSCOMCOL 32 -#define NORADEC 0 -#define FK4 1 -#define FK4NOE 2 -#define FK5 3 -#define GAPPT 4 -#define ICRS 5 -#define NOCEL 0 -#define RADEC 1 -#define ECLIP 2 -#define GALAC 3 -#define SUPER 4 -#define HECLIP 5 -#define AZEL 6 -#define LONAX -1 -#define NONAX 0 -#define LATAX 1 -#define NDESC 9 -#define MXCTYPELEN 81 -#define ALLWARNINGS " distortion noequinox noradesys nomjd-obs nolonpole nolatpole tnx zpx badcel noctype badlat badmat badval badctype badpv badkeyname badkeyvalue " -#define NPFIT 10 -#define SPD 86400.0 -#define FL 1.0/298.257 /* Reference spheroid flattening factor */ -#define A0 6378140.0 /* Earth equatorial radius (metres) */ - -/* String used to represent AST__BAD externally. */ -#define BAD_STRING "<bad>" - -/* Each card in the fitschan has a set of flags associated with it, - stored in different bits of the "flags" item within each FitsCard - structure (note, in AST V1.4 these flags were stored in the "del" - item... Dump and LoadFitsChan will need to be changed to use a - correspondingly changed name for the external representation of this - item). The following flags are currently defined: */ - -/* "USED" - This flag indicates that the the card has been used in the - construction of an AST Object returned by astRead. Such cards should - usually be treated as if they do not exist, i.e. they should not be - used again by subsequent calls to astRead, they should not be recognised - by public FitsChan methods which search the FitsChan for specified - cards, and they should not be written out when the FitsChan is deleted. - This flag was the only flag available in AST V1.4, and was called - "Del" (for "deleted"). Used cards are retained in order to give an - indication of where abouts within the header new cards should be placed - when astWrite is called (i.e. new cards should usually be placed at - the same point within the header as the cards which they replace). */ -#define USED 1 - -/* "PROVISIONALLY_USED" - This flag indicates that the the card is being - considered as a candidate for inclusion in the construction of an AST - Object. If the Object is constructed succesfully, cards flagged as - "provisionally used" will be changed to be flagged as "definitely used" - (using the USED flag). If the Object fails to be constructed - succesfully (if some required cards are missing from the FitsChan - for instance), then "provisionally used" cards will be returned to the - former state which they had prior to the attempt to construct the - object. */ -#define PROVISIONALLY_USED 2 - -/* "NEW" - This flag indicates that the the card has just been added to - the FitsChan and may yet proove to be unrequired. For instance if the - supplied Object is not of an appropriate flavour to be stored using - the requested encoding, all "new" cards which were added before the - inappropriateness was discovered will be removed from the FitsChan. - Two different levels of "newness" are available. */ -#define NEW1 4 -#define NEW2 8 - -/* "PROTECTED" - This flag indicates that the the card should not be - removed form the FitsChan when an Object is read using astRead. If - this flag is not set, then the card will dehave as if it has been - deleted if it was used in the construction of the returned AST Object. */ -#define PROTECTED 16 - -/* Include files. */ -/* ============== */ - -/* Interface definitions. */ -/* ---------------------- */ -#include "channel.h" -#include "cmpframe.h" -#include "cmpmap.h" -#include "dssmap.h" -#include "error.h" -#include "fitschan.h" -#include "frame.h" -#include "frameset.h" -#include "grismmap.h" -#include "lutmap.h" -#include "mathmap.h" -#include "matrixmap.h" -#include "memory.h" -#include "object.h" -#include "permmap.h" -#include "pointset.h" -#include "shiftmap.h" -#include "skyframe.h" -#include "timeframe.h" -#include "keymap.h" -#include "pal.h" -#include "erfa.h" -#include "slamap.h" -#include "specframe.h" -#include "dsbspecframe.h" -#include "specmap.h" -#include "sphmap.h" -#include "unit.h" -#include "unitmap.h" -#include "polymap.h" -#include "wcsmap.h" -#include "winmap.h" -#include "zoommap.h" -#include "globals.h" -#include "fitstable.h" - -/* Error code definitions. */ -/* ----------------------- */ -#include "ast_err.h" /* AST error codes */ - -/* C header files. */ -/* --------------- */ -#include <ctype.h> -#include <float.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <errno.h> - -/* Type Definitions */ -/* ================ */ - -/* This structure contains information describing a single FITS header card - in a circular list of such structures. */ -typedef struct FitsCard { - char name[ FITSNAMLEN + 1 ];/* Keyword name (plus terminating null). */ - int type; /* Data type. */ - void *data; /* Pointer to the keyword's data value. */ - char *comment; /* Pointer to a comment for the keyword. */ - int flags; /* Flags for each card */ - size_t size; /* Size of data value */ - struct FitsCard *next; /* Pointer to next structure in list. */ - struct FitsCard *prev; /* Pointer to previous structure in list. */ -} FitsCard; - -/* Structure used to store information derived from the FITS WCS keyword - values in a form more convenient to further processing. Conventions - for units, etc, for values in a FitsStore follow FITS-WCS (e.g. angular - values are stored in degrees, equinox is B or J depending on RADECSYS, - etc). */ -typedef struct FitsStore { - char ****cname; - char ****ctype; - char ****ctype_com; - char ****cunit; - char ****radesys; - char ****wcsname; - char ****specsys; - char ****ssyssrc; - char ****ps; - char ****timesys; - double ***pc; - double ***cdelt; - double ***crpix; - double ***crval; - double ***equinox; - double ***latpole; - double ***lonpole; - double ***mjdobs; - double ***dtai; - double ***dut1; - double ***mjdavg; - double ***pv; - double ***wcsaxes; - double ***obsgeox; - double ***obsgeoy; - double ***obsgeoz; - double ***restfrq; - double ***restwav; - double ***zsource; - double ***velosys; - double ***asip; - double ***bsip; - double ***apsip; - double ***bpsip; - double ***imagfreq; - double ***axref; - int naxis; - AstKeyMap *tables; - double ***skyref; - double ***skyrefp; - char ****skyrefis; -} FitsStore; - -/* 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 void (* parent_setsourcefile)( AstChannel *, const char *, int * ); -static int (* parent_getobjsize)( AstObject *, int * ); -static const char *(* parent_getattrib)( AstObject *, const char *, int * ); -static int (* parent_getfull)( AstChannel *, int * ); -static int (* parent_getskip)( AstChannel *, int * ); -static int (* parent_testattrib)( AstObject *, const char *, int * ); -static void (* parent_clearattrib)( AstObject *, const char *, int * ); -static void (* parent_setattrib)( AstObject *, const char *, int * ); -static int (* parent_write)( AstChannel *, AstObject *, int * ); -static AstObject *(* parent_read)( AstChannel *, int * ); -#if defined(THREAD_SAFE) -static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); -#endif - -/* Strings to describe each data type. These should be in the order implied - by the corresponding macros (eg AST__FLOAT, etc). */ -static const char *type_names[9] = {"comment", "integer", "floating point", - "string", "complex floating point", - "complex integer", "logical", - "continuation string", "undef" }; - -/* Text values used to represent Encoding values externally. */ - -static const char *xencod[8] = { NATIVE_STRING, FITSPC_STRING, - DSS_STRING, FITSWCS_STRING, - FITSIRAF_STRING, FITSAIPS_STRING, - FITSAIPSPP_STRING, FITSCLASS_STRING }; -/* Define two variables to hold TimeFrames which will be used for converting - MJD values between time scales. */ -static AstTimeFrame *tdbframe = NULL; -static AstTimeFrame *timeframe = NULL; - -/* Max number of characters in a formatted int */ -static int int_dig; - -/* Define macros for accessing each item of thread specific global data. */ -#ifdef THREAD_SAFE - -/* Define how to initialise thread-specific globals. */ -#define GLOBAL_inits \ - globals->Class_Init = 0; \ - globals->GetAttrib_Buff[ 0 ] = 0; \ - globals->Items_Written = 0; \ - globals->Write_Nest = -1; \ - globals->Current_Indent = 0; \ - globals->Ignore_Used = 1; \ - globals->Mark_New = 0; \ - globals->CnvType_Text[ 0 ] = 0; \ - globals->CnvType_Text0[ 0 ] = 0; \ - globals->CnvType_Text1[ 0 ] = 0; \ - globals->CreateKeyword_Seq_Nchars = -1; \ - globals->FormatKey_Buff[ 0 ] = 0; \ - globals->FitsGetCom_Sval[ 0 ] = 0; \ - globals->IsSpectral_Ret = NULL; \ - globals->Match_Fmt[ 0 ] = 0; \ - globals->Match_Template = NULL; \ - globals->Match_PA = 0; \ - globals->Match_PB = 0; \ - globals->Match_NA = 0; \ - globals->Match_NB = 0; \ - globals->Match_Nentry = 0; \ - globals->WcsCelestial_Type[ 0 ] = 0; \ - globals->Ignore_Used = 1; \ - globals->Mark_New = 0; - -/* Create the function that initialises global data for this module. */ -astMAKE_INITGLOBALS(FitsChan) - -/* Define macros for accessing each item of thread specific global data. */ -#define class_init astGLOBAL(FitsChan,Class_Init) -#define class_vtab astGLOBAL(FitsChan,Class_Vtab) -#define getattrib_buff astGLOBAL(FitsChan,GetAttrib_Buff) -#define items_written astGLOBAL(FitsChan,Items_Written) -#define write_nest astGLOBAL(FitsChan,Write_Nest) -#define current_indent astGLOBAL(FitsChan,Current_Indent) -#define ignore_used astGLOBAL(FitsChan,Ignore_Used) -#define mark_new astGLOBAL(FitsChan,Mark_New) -#define cnvtype_text astGLOBAL(FitsChan,CnvType_Text) -#define cnvtype_text0 astGLOBAL(FitsChan,CnvType_Text0) -#define cnvtype_text1 astGLOBAL(FitsChan,CnvType_Text1) -#define createkeyword_seq_nchars astGLOBAL(FitsChan,CreateKeyword_Seq_Nchars) -#define formatkey_buff astGLOBAL(FitsChan,FormatKey_Buff) -#define fitsgetcom_sval astGLOBAL(FitsChan,FitsGetCom_Sval) -#define isspectral_ret astGLOBAL(FitsChan,IsSpectral_Ret) -#define match_fmt astGLOBAL(FitsChan,Match_Fmt) -#define match_template astGLOBAL(FitsChan,Match_Template) -#define match_pa astGLOBAL(FitsChan,Match_PA) -#define match_pb astGLOBAL(FitsChan,Match_PB) -#define match_na astGLOBAL(FitsChan,Match_NA) -#define match_nb astGLOBAL(FitsChan,Match_NB) -#define match_nentry astGLOBAL(FitsChan,Match_Nentry) -#define wcscelestial_type astGLOBAL(FitsChan,WcsCelestial_Type) -static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; -#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); -#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); -static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER; -#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 ); -#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 ); -static pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER; -#define LOCK_MUTEX4 pthread_mutex_lock( &mutex4 ); -#define UNLOCK_MUTEX4 pthread_mutex_unlock( &mutex4 ); - -/* If thread safety is not needed, declare and initialise globals at static - variables. */ -#else - -/* Buffer returned by GetAttrib. */ -static char getattrib_buff[ AST__FITSCHAN_GETATTRIB_BUFF_LEN + 1 ]; - -/* Buffer for returned text string in CnvType */ -static char cnvtype_text[ AST__FITSCHAN_FITSCARDLEN + 1 ]; - -/* Buffer for real value in CnvType */ -static char cnvtype_text0[ AST__FITSCHAN_FITSCARDLEN + 1 ]; - -/* Buffer for imaginary value in CnvType */ -static char cnvtype_text1[ AST__FITSCHAN_FITSCARDLEN + 1 ]; - -/* Number of output items written since the last "Begin" or "IsA" - output item, and level of Object nesting during recursive - invocation of the astWrite method. */ -static int items_written = 0; -static int write_nest = -1; - -/* Indentation level for indented comments when writing Objects to a - FitsChan. */ -static int current_indent = 0; - -/* Ignore_Used: If 2, then cards which have been marked as either "definitely - used" or "provisionally used" (see the USED flag above) will be ignored - when searching the FitsChan, etc (i.e. they will be treated as if they - have been removed from the FitsChan). If 1, then cards which have been - "definitely used" will be skipped over. If zero then no cards will be - skipped over. */ -static int ignore_used = 1; - -/* Mark_New: If non-zero, then all cards added to the FitsChan will be - marked with both the NEW1 and NEW2 flags (see above). If zero then - new cards will not be marked with either NEW1 or NEW2. */ -static int mark_new = 0; - -/* Number of characters used for encoding */ -static int createkeyword_seq_nchars = -1; - -/* Buffer for value returned by FormatKey */ -static char formatkey_buff[ 10 ]; - -/* Buffer for value returned by FitsGetCom */ -static char fitsgetcom_sval[ AST__FITSCHAN_FITSCARDLEN + 1 ]; - -/* Pointer returned by IsSpectral */ -static const char *isspectral_ret = NULL; - -/* Format specifier for reading an integer field in Match */ -static char match_fmt[ 10 ]; - -/* Pointer to start of template in Match */ -static const char *match_template = NULL; - -/* Pointer to first returned field value in Match */ -static int *match_pa = 0; - -/* Pointer to last returned field value in Match */ -static int *match_pb = 0; - -/* No. of characters read from the test string in Match */ -static int match_na = 0; - -/* No. of characters read from the template string in Match */ -static int match_nb = 0; - -/* Number of recursive entries into Match */ -static int match_nentry = 0; - -/* Buffer for celestial system in WcsCelestial */ -static char wcscelestial_type[ 4 ]; - -/* Define the class virtual function table and its initialisation flag - as static variables. */ -static AstFitsChanVtab class_vtab; /* Virtual function table */ -static int class_init = 0; /* Virtual function table initialised? */ -#define LOCK_MUTEX2 -#define UNLOCK_MUTEX2 -#define LOCK_MUTEX3 -#define UNLOCK_MUTEX3 -#define LOCK_MUTEX4 -#define UNLOCK_MUTEX4 -#endif - -/* 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. */ -AstFitsChan *astFitsChanForId_( const char *(*)( void ), - char *(*)( const char *(*)( void ), int * ), - void (*)( const char * ), - void (*)( void (*)( const char * ), const char *, int * ), - const char *, ... ); -AstFitsChan *astFitsChanId_( const char *(* source)( void ), - void (* sink)( const char * ), - const char *options, ... ); - -/* Prototypes for Private Member Functions. */ -/* ======================================== */ -static int GetObjSize( AstObject *, int * ); -static void ClearCard( AstFitsChan *, int * ); -static int GetCard( AstFitsChan *, int * ); -static int TestCard( AstFitsChan *, int * ); -static void SetCard( AstFitsChan *, int, int * ); -static void ClearEncoding( AstFitsChan *, int * ); -static int GetEncoding( AstFitsChan *, int * ); -static int TestEncoding( AstFitsChan *, int * ); -static void SetEncoding( AstFitsChan *, int, int * ); -static void ClearCDMatrix( AstFitsChan *, int * ); -static int GetCDMatrix( AstFitsChan *, int * ); -static int TestCDMatrix( AstFitsChan *, int * ); -static void SetCDMatrix( AstFitsChan *, int, int * ); -static void ClearFitsDigits( AstFitsChan *, int * ); -static int GetFitsDigits( AstFitsChan *, int * ); -static int TestFitsDigits( AstFitsChan *, int * ); -static void SetFitsDigits( AstFitsChan *, int, int * ); -static void ClearFitsAxisOrder( AstFitsChan *, int * ); -static const char *GetFitsAxisOrder( AstFitsChan *, int * ); -static int TestFitsAxisOrder( AstFitsChan *, int * ); -static void SetFitsAxisOrder( AstFitsChan *, const char *, int * ); -static void ClearDefB1950( AstFitsChan *, int * ); -static int GetDefB1950( AstFitsChan *, int * ); -static int TestDefB1950( AstFitsChan *, int * ); -static void SetDefB1950( AstFitsChan *, int, int * ); -static void ClearTabOK( AstFitsChan *, int * ); -static int GetTabOK( AstFitsChan *, int * ); -static int TestTabOK( AstFitsChan *, int * ); -static void SetTabOK( AstFitsChan *, int, int * ); -static void ClearCarLin( AstFitsChan *, int * ); -static int GetCarLin( AstFitsChan *, int * ); -static int TestCarLin( AstFitsChan *, int * ); -static void SetCarLin( AstFitsChan *, int, int * ); -static void ClearSipReplace( AstFitsChan *, int * ); -static int GetSipReplace( AstFitsChan *, int * ); -static int TestSipReplace( AstFitsChan *, int * ); -static void SetSipReplace( AstFitsChan *, int, int * ); -static void ClearPolyTan( AstFitsChan *, int * ); -static int GetPolyTan( AstFitsChan *, int * ); -static int TestPolyTan( AstFitsChan *, int * ); -static void SetPolyTan( AstFitsChan *, int, int * ); -static void ClearSipOK( AstFitsChan *, int * ); -static int GetSipOK( AstFitsChan *, int * ); -static int TestSipOK( AstFitsChan *, int * ); -static void SetSipOK( AstFitsChan *, int, int * ); -static void ClearIwc( AstFitsChan *, int * ); -static int GetIwc( AstFitsChan *, int * ); -static int TestIwc( AstFitsChan *, int * ); -static void SetIwc( AstFitsChan *, int, int * ); -static void ClearClean( AstFitsChan *, int * ); -static int GetClean( AstFitsChan *, int * ); -static int TestClean( AstFitsChan *, int * ); -static void SetClean( AstFitsChan *, int, int * ); -static void ClearWarnings( AstFitsChan *, int * ); -static const char *GetWarnings( AstFitsChan *, int * ); -static int TestWarnings( AstFitsChan *, int * ); -static void SetWarnings( AstFitsChan *, const char *, int * ); -static double GetFitsTol( AstFitsChan *, int * ); -static int TestFitsTol( AstFitsChan *, int * ); -static void ClearFitsTol( AstFitsChan *, int * ); -static void SetFitsTol( AstFitsChan *, double, int * ); - - -static AstFitsChan *SpecTrans( AstFitsChan *, int, const char *, const char *, int * ); -static AstFitsTable *GetNamedTable( AstFitsChan *, const char *, int, int, int, const char *, int * ); -static AstFrameSet *MakeFitsFrameSet( AstFitsChan *, AstFrameSet *, int, int, int, const char *, const char *, int * ); -static AstGrismMap *ExtractGrismMap( AstMapping *, int, AstMapping **, int * ); -static AstKeyMap *GetTables( AstFitsChan *, int * ); -static AstMapping *AddUnitMaps( AstMapping *, int, int, int * ); -static AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *, double *, int *, char, FitsStore *, int *, int, const char *, const char *, int * ); -static AstMapping *GrismSpecWcs( char *, FitsStore *, int, char, AstSpecFrame *, const char *, const char *, int * ); -static AstMapping *IsMapTab1D( AstMapping *, double, const char *, AstFrame *, double *, int, int, AstFitsTable **, int *, int *, int *, int * ); -static AstMapping *IsMapTab2D( AstMapping *, double, const char *, AstFrame *, double *, int, int, int, int, AstFitsTable **, int *, int *, int *, int *, int *, int *, int *, int *, int * ); -static AstMapping *LinearWcs( FitsStore *, int, char, const char *, const char *, int * ); -static AstMapping *LogAxis( AstMapping *, int, int, double *, double *, double, int * ); -static AstMapping *LogWcs( FitsStore *, int, char, const char *, const char *, int * ); -static AstMapping *MakeColumnMap( AstFitsTable *, const char *, int, int, const char *, const char *, int * ); -static AstMapping *NonLinSpecWcs( AstFitsChan *, char *, FitsStore *, int, char, AstSpecFrame *, const char *, const char *, int * ); -static AstMapping *OtherAxes( AstFitsChan *, AstFrameSet *, double *, int *, char, FitsStore *, double *, int *, const char *, const char *, int * ); -static AstMapping *SIPIntWorld( AstMapping *, int, int, char, FitsStore *, double *, int[2], double[2], double[4], const char *, const char *, int * ); -static AstMapping *SIPMapping( AstFitsChan *, double *, FitsStore *, char, int, const char *, const char *, int * ); -static AstMapping *SpectralAxes( AstFitsChan *, AstFrameSet *, double *, int *, char, FitsStore *, double *, int *, const char *, const char *, int * ); -static AstMapping *TabMapping( AstFitsChan *, FitsStore *, char, int **, const char *, const char *, int *); -static AstMapping *WcsCelestial( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, double *, double *, AstSkyFrame **, AstMapping **, int *, const char *, const char *, int * ); -static AstMapping *WcsIntWorld( AstFitsChan *, FitsStore *, char, int, const char *, const char *, int * ); -static AstMapping *WcsMapFrm( AstFitsChan *, FitsStore *, char, AstFrame **, const char *, const char *, int * ); -static AstMapping *WcsNative( AstFitsChan *, FitsStore *, char, AstWcsMap *, int, int, const char *, const char *, int * ); -static AstMapping *WcsOthers( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, const char *, const char *, int * ); -static AstMapping *WcsSpectral( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, double, double, AstSkyFrame *, const char *, const char *, int * ); -static AstMapping *ZPXMapping( AstFitsChan *, FitsStore *, char, int, int[2], const char *, const char *, int * ); -static AstMatrixMap *WcsCDeltMatrix( FitsStore *, char, int, const char *, const char *, int * ); -static AstMatrixMap *WcsPCMatrix( FitsStore *, char, int, const char *, const char *, int * ); -static AstObject *FsetFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static AstObject *Read( AstChannel *, int * ); -static AstSkyFrame *WcsSkyFrame( AstFitsChan *, FitsStore *, char, int, char *, int, int, const char *, const char *, int * ); -static AstTimeScaleType TimeSysToAst( AstFitsChan *, const char *, const char *, const char *, int * ); -static AstWinMap *WcsShift( FitsStore *, char, int, const char *, const char *, int * ); -static FitsCard *GetLink( FitsCard *, int, const char *, const char *, int * ); -static FitsStore *FitsToStore( AstFitsChan *, int, const char *, const char *, int * ); -static FitsStore *FreeStore( FitsStore *, int * ); -static FitsStore *FsetToStore( AstFitsChan *, AstFrameSet *, int, double *, int, const char *, const char *, int * ); -static char *CardComm( AstFitsChan *, int * ); -static char *CardName( AstFitsChan *, int * ); -static char *ConcatWAT( AstFitsChan *, int, const char *, const char *, int * ); -static char *FormatKey( const char *, int, int, char, int * ); -static char *GetItemC( char *****, int, int, char, char *, const char *method, const char *class, int * ); -static char *SourceWrap( const char *(*)( void ), int * ); -static char *UnPreQuote( const char *, int * ); -static char GetMaxS( double ****item, int * ); -static const char *GetAllWarnings( AstFitsChan *, int * ); -static const char *GetAttrib( AstObject *, const char *, int * ); -static const char *GetCardComm( AstFitsChan *, int * ); -static const char *GetCardName( AstFitsChan *, int * ); -static const char *GetFitsSor( const char *, int * ); -static const char *IsSpectral( const char *, char[5], char[5], int * ); -static double **OrthVectorSet( int, int, double **, int * ); -static double *Cheb2Poly( double *, int, int, double, double, double, double, int * ); -static double *FitLine( AstMapping *, double *, double *, double *, double, double *, int * ); -static double *OrthVector( int, int, double **, int * ); -static double *ReadCrval( AstFitsChan *, AstFrame *, char, const char *, const char *, int * ); -static double ChooseEpoch( AstFitsChan *, FitsStore *, char, const char *, const char *, int * ); -static double DateObs( const char *, int * ); -static double GetItem( double ****, int, int, char, char *, const char *method, const char *class, int * ); -static double NearestPix( AstMapping *, double, int, int * ); -static double TDBConv( double, int, int, const char *, const char *, int * ); -static int *CardFlags( AstFitsChan *, int * ); -static int AIPSFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static int AIPSPPFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static int AddEncodingFrame( AstFitsChan *, AstFrameSet *, int, const char *, const char *, int * ); -static int AddVersion( AstFitsChan *, AstFrameSet *, int, int, FitsStore *, double *, char, int, int, const char *, const char *, int * ); -static int CLASSFromStore( AstFitsChan *, FitsStore *, AstFrameSet *, double *, const char *, const char *, int * ); -static int CardType( AstFitsChan *, int * ); -static int CheckFitsName( AstFitsChan *, const char *, const char *, const char *, int * ); -static int ChrLen( const char *, int * ); -static int CnvType( int, void *, size_t, int, int, void *, const char *, const char *, const char *, int * ); -static int CnvValue( AstFitsChan *, int , int, void *, const char *, int * ); -static int ComBlock( AstFitsChan *, int, const char *, const char *, int * ); -static int CountFields( const char *, char, const char *, const char *, int * ); -static int DSSFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static int EncodeFloat( char *, int, int, int, double, int * ); -static int EncodeValue( AstFitsChan *, char *, int, int, const char *, int * ); -static int FindBasisVectors( AstMapping *, int, int, double *, AstPointSet *, AstPointSet *, int * ); -static int FindFits( AstFitsChan *, const char *, char[ AST__FITSCHAN_FITSCARDLEN + 1 ], int, int * ); -static int FindKeyCard( AstFitsChan *, const char *, const char *, const char *, int * ); -static int FindLonLatSpecAxes( FitsStore *, char, int *, int *, int *, const char *, const char *, int * ); -static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * ); -static int FitOK( int, double *, double *, double, int * ); -static int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm, int *perm, int *status ); -static int FitsEof( AstFitsChan *, int * ); -static int FitsFromStore( AstFitsChan *, FitsStore *, int, double *, AstFrameSet *, const char *, const char *, int * ); -static int FitsGetCom( AstFitsChan *, const char *, char **, int * ); -static int FitsSof( AstFitsChan *, int * ); -static int FullForm( const char *, const char *, int, int * ); -static int GetCardType( AstFitsChan *, int * ); -static int GetFiducialWCS( AstWcsMap *, AstMapping *, int, int, double *, double *, int * ); -static int GetFitsCF( AstFitsChan *, const char *, double *, int * ); -static int GetFitsCI( AstFitsChan *, const char *, int *, int * ); -static int GetFitsCN( AstFitsChan *, const char *, char **, int * ); -static int GetFitsF( AstFitsChan *, const char *, double *, int * ); -static int GetFitsI( AstFitsChan *, const char *, int *, int * ); -static int GetFitsL( AstFitsChan *, const char *, int *, int * ); -static int GetFitsS( AstFitsChan *, const char *, char **, int * ); -static int GetFull( AstChannel *, int * ); -static int GetMaxI( double ****item, char, int * ); -static int GetMaxJM( double ****item, char, int * ); -static int GetMaxJMC( char *****item, char, int * ); -static int GetNcard( AstFitsChan *, int * ); -static int GetNkey( AstFitsChan *, int * ); -static int GetSkip( AstChannel *, int * ); -static int GetUsedPolyTan( AstFitsChan *, AstFitsChan *, int, int, char, const char *, const char *, int * ); -static int GetValue( AstFitsChan *, const char *, int, void *, int, int, const char *, const char *, int * ); -static int GetValue2( AstFitsChan *, AstFitsChan *, const char *, int, void *, int, const char *, const char *, int * ); -static int GoodWarns( const char *, int * ); -static int HasAIPSSpecAxis( AstFitsChan *, const char *, const char *, int * ); -static int HasCard( AstFitsChan *, const char *, const char *, const char *, int * ); -static int IRAFFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static int IsAIPSSpectral( const char *, char **, char **, int * ); -static int IsMapLinear( AstMapping *, const double [], const double [], int, int * ); -static int IsSkyOff( AstFrameSet *, int, int * ); -static int KeyFields( AstFitsChan *, const char *, int, int *, int *, int * ); -static int LooksLikeClass( AstFitsChan *, const char *, const char *, int * ); -static int MakeBasisVectors( AstMapping *, int, int, double *, AstPointSet *, AstPointSet *, int * ); -static int MakeIntWorld( AstMapping *, AstFrame *, int *, char, FitsStore *, double *, double, int, const char *, const char *, int * ); -static int Match( const char *, const char *, int, int *, int *, const char *, const char *, int * ); -static int MatchChar( char, char, const char *, const char *, const char *, int * ); -static int MatchFront( const char *, const char *, char *, int *, int *, int *, const char *, const char *, const char *, int * ); -static int MoveCard( AstFitsChan *, int, const char *, const char *, int * ); -static int PCFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static int SAOTrans( AstFitsChan *, AstFitsChan *, const char *, const char *, int * ); -static int SearchCard( AstFitsChan *, const char *, const char *, const char *, int * ); -static int SetFits( AstFitsChan *, const char *, void *, int, const char *, int, int * ); -static int Similar( const char *, const char *, int * ); -static int SkySys( AstFitsChan *, AstSkyFrame *, int, int, FitsStore *, int, int, char c, int, const char *, const char *, int * ); -static int Split( AstFitsChan *, const char *, char **, char **, char **, const char *, const char *, int * ); -static int SplitMap( AstMapping *, int, int, int, AstMapping **, AstWcsMap **, AstMapping **, int * ); -static int SplitMap2( AstMapping *, int, AstMapping **, AstWcsMap **, AstMapping **, int * ); -static int SplitMat( int , double *, double *, int * ); -static int TestAttrib( AstObject *, const char *, int * ); -static int TestFits( AstFitsChan *, const char *, int *, int * ); -static int Use( AstFitsChan *, int, int, int * ); -static int Ustrcmp( const char *, const char *, int * ); -static int Ustrncmp( const char *, const char *, size_t, int * ); -static int WATCoeffs( const char *, int, double **, int **, int *, int * ); -static int WcsFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static int WcsNatPole( AstFitsChan *, AstWcsMap *, double, double, double, double *, double *, double *, int * ); -static int WorldAxes( AstFitsChan *this, AstMapping *, double *, int *, int * ); -static int Write( AstChannel *, AstObject *, int * ); -static void *CardData( AstFitsChan *, size_t *, int * ); -static void AdaptLut( AstMapping *, int, double, double, double, double, double, double **, double **, int *, int * ); -static void AddFrame( AstFitsChan *, AstFrameSet *, int, int, FitsStore *, char, const char *, const char *, int * ); -static void ChangePermSplit( AstMapping *, int * ); -static void CheckZero( char *, double, int, int * ); -static void Chpc1( double *, double *, int, int *, int *, int * ); -static void ClassTrans( AstFitsChan *, AstFitsChan *, int, int, const char *, const char *, int * ); -static void ClearAttrib( AstObject *, const char *, int * ); -static void Copy( const AstObject *, AstObject *, int * ); -static void CreateKeyword( AstFitsChan *, const char *, char [ FITSNAMLEN + 1 ], int * ); -static void DSBSetUp( AstFitsChan *, FitsStore *, AstDSBSpecFrame *, char, double, const char *, const char *, int * ); -static void DSSToStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static void DelFits( AstFitsChan *, int * ); -static void Delete( AstObject *, int * ); -static void DeleteCard( AstFitsChan *, const char *, const char *, int * ); -static void DistortMaps( AstFitsChan *, FitsStore *, char, int , AstMapping **, AstMapping **, AstMapping **, AstMapping **, const char *, const char *, int * ); -static void Dump( AstObject *, AstChannel *, int * ); -static void EmptyFits( AstFitsChan *, int * ); -static void FindWcs( AstFitsChan *, int, int, int, const char *, const char *, int * ); -static void FixNew( AstFitsChan *, int, int, const char *, const char *, int * ); -static void FixUsed( AstFitsChan *, int, int, int, const char *, const char *, int * ); -static void FormatCard( AstFitsChan *, char *, const char *, int * ); -static void FreeItem( double ****, int * ); -static void FreeItemC( char *****, int * ); -static void GetFiducialNSC( AstWcsMap *, double *, double *, int * ); -static void GetFiducialPPC( AstWcsMap *, double *, double *, int * ); -static void GetNextData( AstChannel *, int, char **, char **, int * ); -static void InsCard( AstFitsChan *, int, const char *, int, void *, const char *, const char *, const char *, int * ); -static void MakeBanner( const char *, const char *, const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ], int * ); -static void MakeIndentedComment( int, char, const char *, const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1], int * ); -static void MakeIntoComment( AstFitsChan *, const char *, const char *, int * ); -static void MakeInvertable( double **, int, double *, int * ); -static void MarkCard( AstFitsChan *, int * ); -static void NewCard( AstFitsChan *, const char *, int, const void *, const char *, int, int * ); -static void PreQuote( const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ], int * ); -static void PurgeWCS( AstFitsChan *, int * ); -static void PutCards( AstFitsChan *, const char *, int * ); -static void PutFits( AstFitsChan *, const char [ AST__FITSCHAN_FITSCARDLEN + 1 ], int, int * ); -static void PutTable( AstFitsChan *, AstFitsTable *, const char *, int * ); -static void PutTables( AstFitsChan *, AstKeyMap *, int * ); -static void ReadFits( AstFitsChan *, int * ); -static void ReadFromSource( AstFitsChan *, int * ); -static void RemoveTables( AstFitsChan *, const char *, int * ); -static void RetainFits( AstFitsChan *, int * ); -static void RoundFString( char *, int, int * ); -static void SetAlgCode( char *, const char *, int * ); -static void SetAttrib( AstObject *, const char *, int * ); -static void SetFitsCF( AstFitsChan *, const char *, double *, const char *, int, int * ); -static void SetFitsCI( AstFitsChan *, const char *, int *, const char *, int, int * ); -static void SetFitsCM( AstFitsChan *, const char *, int, int * ); -static void SetFitsCN( AstFitsChan *, const char *, const char *, const char *, int, int * ); -static void SetFitsCom( AstFitsChan *, const char *, const char *, int, int * ); -static void SetFitsF( AstFitsChan *, const char *, double, const char *, int, int * ); -static void SetFitsI( AstFitsChan *, const char *, int, const char *, int, int * ); -static void SetFitsL( AstFitsChan *, const char *, int, const char *, int, int * ); -static void SetFitsS( AstFitsChan *, const char *, const char *, const char *, int, int * ); -static void SetFitsU( AstFitsChan *, const char *, const char *, int, int * ); -static void SetItem( double ****, int, int, char, double, int * ); -static void SetItemC( char *****, int, int, char, const char *, int * ); -static void SetSourceFile( AstChannel *, const char *, int * ); -static void SetValue( AstFitsChan *, const char *, void *, int, const char *, int * ); -static void ShowFits( AstFitsChan *, int * ); -static void Shpc1( double, double, int, double *, double *, int * ); -static void SinkWrap( void (*)( const char * ), const char *, int * ); -static void SkyPole( AstWcsMap *, AstMapping *, int, int, int *, char, FitsStore *, const char *, const char *, int * ); -static void TableSource( AstFitsChan *, void (*)( AstFitsChan *, const char *, int, int, int * ), int * ); -static void TidyOffsets( AstFrameSet *, int * ); -static void Warn( AstFitsChan *, const char *, const char *, const char *, const char *, int * ); -static void WcsFcRead( AstFitsChan *, AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static void WcsToStore( AstFitsChan *, AstFitsChan *, FitsStore *, const char *, const char *, int * ); -static void WriteBegin( AstChannel *, const char *, const char *, int * ); -static void WriteDouble( AstChannel *, const char *, int, int, double, const char *, int * ); -static void WriteEnd( AstChannel *, const char *, int * ); -static void WriteFits( AstFitsChan *, int * ); -static void WriteInt( AstChannel *, const char *, int, int, int, const char *, int * ); -static void WriteIsA( AstChannel *, const char *, const char *, int * ); -static void WriteObject( AstChannel *, const char *, int, int, AstObject *, const char *, int * ); -static void WriteString( AstChannel *, const char *, int, int, const char *, const char *, int * ); -static void WriteToSink( AstFitsChan *, int * ); -static void SetTableSource( AstFitsChan *, - void (*)( void ), - void (*)( void (*)( void ), - AstFitsChan *, const char *, int, int, int * ), int * ); -static void TabSourceWrap( void (*)( void ), - AstFitsChan *, const char *, int, int, int * ); -#if defined(THREAD_SAFE) -static int ManageLock( AstObject *, int, int, AstObject **, int * ); -#endif - -/* Member functions. */ -/* ================= */ - -static void AdaptLut( AstMapping *map, int npos, double eps, double x0, - double x1, double v0, double v1, double **xtab, - double **vtab, int *nsamp, int *status ){ -/* -* Name: -* AdaptLut - -* Purpose: -* Create a table of optimally sampled values for a Mapping. - -* Type: -* Private function. - -* Synopsis: -* void AdaptLut( AstMapping *map, int npos, double eps, double x0, -* double x1, double v0, double v1, double **xtab, -* double **vtab, int *nsamp, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function returns a look-up table holding samples of the supplied -* 1D mapping. The input values at which the samples are taken are -* returned in the "xtab" array, and the Mapping output values at -* these input values are returned in the "vtab" array. The sample -* spacing is smaller at positions where the output gradient is -* changing more rapidly (i.e. where the output is more non-linear). - -* Parameters: -* map -* Pointer to the Mapping. Should have 1 input and 1 output. -* npos -* The minimum number of samples to place within the interval to be -* sampled, excluding the two end points (which are always sampeld -* anyway). These samples are placed evenly through the [x0,x1] - interval. The interval between adjacent samples will be further -* subdivided if necessary by calling this function recursively. -* eps -* The maximum error in X (i.e. the Mapping input) allowed before -* the supplied interval is subdivided further by a recursive call -* to this function. -* x0 -* The Mapping input value at the start of the interval to be sampled. -* It is assumed that this value is already stored in (*xtab)[0] on -* entry. -* x1 -* The Mapping input value at the end of the interval to be sampled. -* v0 -* The Mapping output value at the start of the interval to be sampled. -* It is assumed that this value is already stored in (*vtab)[0] on -* entry. -* v1 -* The Mapping output value at the end of the interval to be sampled. -* xtab -* Address of a pointer to the array in which to store the Mapping -* input values at which samples were taken. The supplied pointer -* may be changed on exit to point to a larger array. New values -* are added to the end of this array. The initial size of the array -* is given by the supplied value for "*nsamp" -* vtab -* Address of a pointer to the array in which to store the Mapping -* output value at each sample. The supplied pointer may be changed -* on exit to point to a larger array. New values are added to the -* end of this array. The initial size of the array is given by the -* supplied value for "*nsamp". -* nsamp -* Address of an int holding the number of values in the "*xtab" -* and "*ytab" arrays. Updated on exit to include the new values -* added to the arrays by this function. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The size of the returned xtab and vtab arrays. -*/ - -/* Local Variables: */ - double *vv; /* Pointer to Mapping output values */ - double *xx; /* Pointer to Mapping input values */ - double dx; /* Step between sample positions */ - double rg; /* Reciprocal of gradient of (x0,v0)->(x1,v1) line */ - double xx0; /* X at first new sample position */ - int ipos; /* Interior sample index */ - int isamp; /* Index into extended xtab and vtab arrays. */ - int subdivide; /* Subdivide each subinterval? */ - -/* Check the inherited status. */ - if( !astOK ) return; - -/* Allocate work space. */ - xx = astMalloc( sizeof( double )*npos ); - vv = astMalloc( sizeof( double )*npos ); - if( astOK ) { - -/* Set up the evenly spaced interior sample positions. */ - dx = ( x1 - x0 )/( npos + 1 ); - xx0 = x0 + dx; - for( ipos = 0; ipos < npos; ipos++ ) { - xx[ ipos ] = xx0 + ipos*dx; - } - -/* Find the Mapping output values at these input values. */ - astTran1( map, npos, xx, 1, vv ); - -/* See if any of these samples deviate significantly from the straight line - defined by (x0,v0) and (x1,v1). If any such sample is found, we call - this function recursively to sample the subdivided intervals. First - handle cases where the straight line has zero gradient. */ - subdivide = 0; - if( v0 == v1 ) { - -/* Subdivide if any of the interior sample values are different to the - end values. */ - for( ipos = 0; ipos < npos; ipos++ ) { - if( vv[ ipos ] != v0 ) { - subdivide = 1; - break; - } - } - -/* Now handle cases where the line has non-zero gradient. Subdivide if any - of the interior sample input positions are further than "eps" from the - input position that would give the same output value if the mapping was - linear. */ - } else { - rg = ( x1 - x0 )/( v1 - v0 ); - for( ipos = 0; ipos < npos; ipos++ ) { - if( vv[ ipos ] == AST__BAD || - fabs( rg*( vv[ ipos ] - v0 ) - ( xx[ ipos ] - x0 ) ) > eps ) { - subdivide = 1; - break; - } - } - } - -/* If required, call this function recursively to subdivide each section - of the supplied input interval, and append samples to the returned - arrays. */ - if( subdivide ) { - -/* Do each sub-interval, except the last one. The number of subintervals - is one more than the number of interior samples. */ - for( ipos = 0; ipos < npos; ipos++ ) { - -/* Append samples covering the current subinterval to the ends of the - arrays. */ - AdaptLut( map, npos, eps, x0, xx[ ipos ], v0, vv[ ipos ], - xtab, vtab, nsamp, status ); - -/* Store the starting position for the next sub-interval. */ - x0 = xx[ ipos ]; - v0 = vv[ ipos ]; - } - -/* Now do the final sub-interval. */ - AdaptLut( map, npos, eps, x0, x1, v0, v1, xtab, vtab, nsamp, status ); - -/* If we do not need to subdivide, store the samples in the returned - array, together with the supplied final point. */ - } else { - -/* Extend the arrays. */ - isamp = *nsamp; - *nsamp += npos + 1; - *xtab = astGrow( *xtab, *nsamp, sizeof( double ) ); - *vtab = astGrow( *vtab, *nsamp, sizeof( double ) ); - if( astOK ) { - -/* Store the sample positions and values at the end of the extended - arrays. */ - for( ipos = 0; ipos < npos; ipos++, isamp++ ) { - (*xtab)[ isamp ] = xx[ ipos ]; - (*vtab)[ isamp ] = vv[ ipos ]; - } - (*xtab)[ isamp ] = x1; - (*vtab)[ isamp ] = v1; - } - } - } - -/* Free resources. */ - xx = astFree( xx ); - vv= astFree( vv ); -} - -static int AddEncodingFrame( AstFitsChan *this, AstFrameSet *fs, int encoding, - const char *method, const char *class, int *status ){ - -/* -* Name: -* AddEncodingFrame - -* Purpose: -* Add a Frame which conforms to the requirements of the specified encoding. - -* Type: -* Private function. - -* Synopsis: -* int AddEncodingFrame( AstFitsChan *this, AstFrameSet *fs, int encoding, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function attempts to create a Frame based on the current Frame -* of the supplied FrameSet, which conforms to the requirements of the -* specified Encoding. If created, this Frame is added into the -* FrameSet as the new current Frame, and the index of the original current -* Frame is returned. - -* Parameters: -* this -* Pointer to the FitsChan. -* fs -* Pointer to the FrameSet. -* encoding -* The encoding in use. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The index of the original current Frame in the FrameSet. A value of -* AST__NOFRAME is returned if no new Frame is added to the FrameSet, -* or if an error occurs. -*/ - -/* Local Variables: */ - AstCmpFrame *cmpfrm; /* Pointer to spectral cube frame */ - AstFrame *cfrm; /* Pointer to original current Frame */ - AstFrame *newfrm; /* Frame describing coord system to be used */ - AstFrame *pfrm; /* Pointer to primary Frame containing axis */ - AstFrameSet *fsconv; /* FrameSet converting what we have to what we want */ - AstMapping *map; /* Mapping from what we have to what we want */ - AstSkyFrame *skyfrm; /* Pointer to SkyFrame */ - AstSpecFrame *specfrm; /* Pointer to SpecFrame */ - AstSystemType sys; /* Frame coordinate system */ - int i; /* Axis index */ - int naxc; /* No. of axes in original current Frame */ - int paxis; /* Axis index in primary frame */ - int result; /* Returned value */ - -/* Initialise */ - result = AST__NOFRAME; - -/* Check the inherited status. */ - if( !astOK ) return result; - -/* Get a pointer to the current Frame and note how many axes it has. */ - cfrm = astGetFrame( fs, AST__CURRENT ); - naxc = astGetNaxes( cfrm ); - -/* FITS-CLASS */ -/* ========== */ - if( encoding == FITSCLASS_ENCODING ) { - -/* Try to locate a SpecFrame and a SkyFrame in the current Frame. */ - specfrm = NULL; - skyfrm = NULL; - for( i = 0; i < naxc; i++ ) { - astPrimaryFrame( cfrm, i, &pfrm, &paxis ); - if( astIsASpecFrame( pfrm ) ) { - if( !specfrm ) specfrm = astCopy( pfrm ); - } else if( IsASkyFrame( pfrm ) ) { - if( !skyfrm ) skyfrm = astCopy( pfrm ); - } - pfrm = astAnnul( pfrm ); - } - -/* Cannot do anything if either is missing. */ - if( specfrm && skyfrm ) { - -/* If the spectral axis is not frequency, set it to frequency. Also set - spectral units of "Hz". */ - sys = astGetSystem( specfrm ); - if( sys != AST__FREQ ) { - astSetSystem( specfrm, AST__FREQ ); - sys = AST__FREQ; - } - -/* Ensure the standard of rest is Source and units are "Hz". */ - astSetUnit( specfrm, 0, "Hz" ); - astSetStdOfRest( specfrm, AST__SCSOR ); - -/* The celestial axes must be either FK4, FK5 or galactic. */ - sys = astGetSystem( skyfrm ); - if( sys != AST__FK4 && sys != AST__FK5 && sys != AST__GALACTIC ) { - astSetSystem( skyfrm, AST__FK5 ); - sys = AST__FK5; - } - -/* FK5 systems must be J2000, and FK4 must be B1950. */ - if( sys == AST__FK5 ) { - astSetC( skyfrm, "Equinox", "J2000.0" ); - } else if( sys == AST__FK4 ) { - astSetC( skyfrm, "Equinox", "B1950.0" ); - } - -/* Combine the spectral and celestial Frames into a single CmpFrame with - the spectral axis being the first axis. */ - cmpfrm = astCmpFrame( specfrm, skyfrm, "", status ); - -/* Attempt to obtain the current Frame of the supplied FrameSet to this - new Frame. */ - fsconv = astConvert( cfrm, cmpfrm, "" ); - if( fsconv ) { - -/* Get the Mapping and current Frame from the rconversion FrameSet. */ - newfrm = astGetFrame( fsconv, AST__CURRENT ); - map = astGetMapping( fsconv, AST__BASE, AST__CURRENT ); - -/* Save the original current Frame index. */ - result = astGetCurrent( fs ); - -/* Add the new Frame into the supplied FrameSet using the above Mapping - to connect it to the original current Frame. The new Frame becomes the - current Frame. */ - astAddFrame( fs, AST__CURRENT, map, newfrm ); - -/* Free resources */ - map = astAnnul( map ); - newfrm = astAnnul( newfrm ); - fsconv = astAnnul( fsconv ); - } - -/* Free resources */ - cmpfrm = astAnnul( cmpfrm ); - } - -/* Release resources. */ - if( specfrm ) specfrm = astAnnul( specfrm ); - if( skyfrm ) skyfrm = astAnnul( skyfrm ); - } - -/* Free reources. */ - cfrm = astAnnul( cfrm ); - -/* Return the result */ - return result; -} - -static void AddFrame( AstFitsChan *this, AstFrameSet *fset, int pixel, - int npix, FitsStore *store, char s, const char *method, - const char *class, int *status ){ -/* -* Name: -* AddFrame - -* Purpose: -* Create a Frame describing a set of axes with a given co-ordinate -* version, and add it to the supplied FrameSet. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void AddFrame( AstFitsChan *this, AstFrameSet *fset, int pixel, -* int npix, FitsStore *store, char s, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A Frame is created describing axis with a specific co-ordinate -* version character, reading information from the supplied FitsStore. -* A suitable Mapping is created to connect the new Frame to the pixel -* (GRID) Frame in the supplied FrameSet, and the Frame is added into -* the FrameSet using this Mapping. - -* Parameters: -* this -* The FitsChan from which the keywords were read. Warning messages -* are added to this FitsChan if the celestial co-ordinate system is -* not recognized. -* fset -* Pointer to the FrameSet to be extended. -* pixel -* The index of the pixel (GRID) Frame within fset. -* npix -* The number of pixel axes. -* store -* The FitsStore containing the required information extracted from -* the FitsChan. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - AstFrame *frame; /* Requested Frame */ - AstMapping *mapping; /* Mapping from pixel to requested Frame */ - AstMapping *tmap; /* Temporary Mapping pointer */ - AstPermMap *pmap; /* PermMap pointer to add or remove axes */ - double con; /* Value to be assigned to missing axes */ - int *inperm; /* Pointer to input axis permutation array */ - int *outperm; /* Pointer to output axis permutation array */ - int i; /* Axis index */ - int nf; /* Number of Frames originally in fset */ - int nwcs; /* Number of wcs axes */ - -/* Check the inherited status. */ - if( !astOK ) return; - -/* Get a Mapping between pixel coordinates and physical coordinates, using - the requested axis descriptions. Also returns a Frame describing the - physical coordinate system. */ - mapping = WcsMapFrm( this, store, s, &frame, method, class, status ); - -/* Add the Frame into the FrameSet, and annul the mapping and frame. If - the new Frame has more axes than the pixel Frame, use a PermMap which - assigns constant value 1.0 to the extra axes. If the new Frame has less - axes than the pixel Frame, use a PermMap which throws away the extra - axes. */ - if( mapping != NULL ) { - nwcs = astGetNin( mapping ); - if( nwcs != npix ) { - inperm = astMalloc( sizeof(int)*(size_t)npix ); - outperm = astMalloc( sizeof(int)*(size_t)nwcs ); - if( astOK ) { - for( i = 0; i < npix; i++ ) { - inperm[ i ] = ( i < nwcs ) ? i : -1; - } - for( i = 0; i < nwcs; i++ ) { - outperm[ i ] = ( i < npix ) ? i : -1; - } - con = 1.0; - pmap = astPermMap( npix, inperm, nwcs, outperm, &con, "", status ); - tmap = (AstMapping *) astCmpMap( pmap, mapping, 1, "", status ); - pmap = astAnnul( pmap ); - (void) astAnnul( mapping ); - mapping = tmap; - } - inperm = astFree( inperm ); - outperm = astFree( outperm ); - } - -/* Record the original number of Frames in the FrameSet. */ - nf = astGetNframe( fset ); - -/* Add in the Frame (which may be a FrameSet). */ - astAddFrame( fset, pixel, mapping, frame ); - -/* Ensure the WCS Frame is the current Frame within fset (it may not be if - the frame returned by WcsMapFrm is actually a FrameSet containing a IWC - Frame as well as a WCS Frame). The WCS Frame is always the first frame - in the frame/frameset returned by WcsMapFrm. */ - astSetCurrent( fset, nf + 1 ); - -/* Annul temporary resources. */ - mapping = astAnnul( mapping ); - } - frame = astAnnul( frame ); -} - -static int AddVersion( AstFitsChan *this, AstFrameSet *fs, int ipix, int iwcs, - FitsStore *store, double *dim, char s, int encoding, - int isoff, const char *method, const char *class, - int *status ){ -/* -* Name: -* AddVersion - -* Purpose: -* Add values to a FitsStore describing a specified Frame in a FrameSet. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int AddVersion( AstFitsChan *this, AstFrameSet *fs, int ipix, int iwcs, -* FitsStore *store, double *dim, char s, int encoding, -* int isoff, const char *method, const char *class, -* int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Values are added to the supplied FitsStore describing the specified -* WCS Frame, and its relationship to the specified pixel Frame. These -* values are based on the standard FITS-WCS conventions. - -* Parameters: -* this -* Pointer to the FitsChan. -* fs -* Pointer to the FrameSet. -* ipix -* The index of the pixel (GRID) Frame within fset. -* iwcs -* The index of the Frame within fset to use as the WCS co-ordinate -* Frame. -* store -* The FitsStore in which to store the information extracted from -* the FrameSet. -* dim -* Pointer to an array of pixel axis dimensions. Individual elements -* will be AST__BAD if dimensions are not known. The number of -* elements should equal the number of axes in the base Frame of the -* supplied FrameSet. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* encoding -* The encoding being used. -* isoff -* If greater than zero, the Frame is an offset SkyFrame and the -* description added to the FitsStore should describe offset coordinates. -* If less than than zero, the Frame is an offset SkyFrame and the -* description added to the FitsStore should describe absolute coordinates. -* If zero, the Frame is an absolute SkyFrame and the description added -* to the FitsSTore should (by necessity) describe absolute coordinates. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Retuned Value: -* A value of 1 is returned if the WCS Frame was succesfully added to -* the FitsStore. A value of zero is returned otherwise. -*/ - -/* Local Variables: */ - AstFrame *wcsfrm; /* WCS Frame */ - AstFrameSet *fset; /* Temporary FrameSet */ - AstMapping *iwcmap; /* Mapping from WCS to IWC Frame */ - AstMapping *mapping; /* Mapping from pixel to WCS Frame */ - AstMapping *pixiwcmap; /* Mapping from pixel to IWC Frame */ - AstMapping *tmap2; /* Temporary Mapping */ - AstMapping *tmap; /* Temporary Mapping */ - const char *old_skyrefis;/* Old value of SkyRefIs attribute */ - double *crvals; /* Pointer to array holding default CRVAL values */ - double cdelt2; /* Sum of squared PC values */ - double cdelt; /* CDELT value for axis */ - double crpix; /* CRPIX value for axis */ - double crval; /* CRVAL value for axis */ - double fitstol; /* Max departure from linearity, in pixels */ - double pc; /* Element of the PC array */ - int *axis_done; /* Flags indicating which axes have been done */ - int *wperm; /* FITS axis for each Mapping output (Frame axis) */ - int fits_i; /* FITS WCS axis index */ - int fits_j; /* FITS pixel axis index */ - int iax; /* Frame axis index */ - int icurr; /* Index of current Frame */ - int nwcs; /* No. of axes in WCS frame */ - int ret; /* Returned value */ - int sipok; /* Should SIP headers be produced? */ - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* If the frame is a SkyFrame describing offset coordinates, but the - description added to the FitsStore should be for absolute coordinates, - temporarily clear the SkyFrame SkyRefIs attribute. We need to make it - the current Frame first so that we can use the FrameSet to clear the - attribte, so that the SkyFrame will be re-mapped within the FrameSet - to take account of the clearing. For negative isoff values, set the - specific negative value to indicate the original SkyRefIs value. */ - if( isoff < 0 ) { - icurr = astGetCurrent( fs ); - astSetCurrent( fs, iwcs ); - old_skyrefis = astGetC( fs, "SkyRefIs" ); - if( astOK ) { - if( !Ustrcmp( old_skyrefis, "POLE", status ) ) { - isoff = -1; - } else if( !Ustrcmp( old_skyrefis, "ORIGIN", status ) ) { - isoff = -2; - } else { - isoff = -3; - } - } - astClear( fs, "SkyRefIs" ); - astSetCurrent( fs, icurr ); - } else { - old_skyrefis = AST__BAD_REF; - } - -/* Construct a new FrameSet holding the pixel and WCS Frames from the - supplied FrameSet, but in which the current Frame is a copy of the - supplied WCS Frame, but optionally extended to include any extra axes - needed to conform to the FITS model. For instance, if the WCS Frame - consists of a single 1D SpecFrame with a defined celestial reference - position (SpecFrame attributes RefRA and RefDec), then FITS-WCS paper - III requires there to be a pair of celestial axes in the WCS Frame in - which the celestial reference point for the spectral axis is defined. */ - fset = MakeFitsFrameSet( this, fs, ipix, iwcs, encoding, method, class, status ); - -/* If required, re-instate the original value of the SkyRefIs attribute - in the supplied FrameSet. */ - if( old_skyrefis != AST__BAD_REF ) { - astSetCurrent( fs, iwcs ); - astSetC( fs, "SkyRefIs", old_skyrefis ); - astSetCurrent( fs, icurr ); - } - -/* Abort if the FrameSet could not be produced. */ - if( !fset ) return ret; - -/* Get the Mapping from base to current Frame and check its inverse is - defined. Return if not. Note, we can handle non-invertable Mappings if - we are allowed to use the -TAB algorithm. */ - mapping = astGetMapping( fset, AST__BASE, AST__CURRENT ); - wcsfrm = astGetFrame( fset, AST__CURRENT ); - if( !astGetTranInverse( mapping ) && astGetTabOK( this ) <= 0 ) { - mapping = astAnnul( mapping ); - wcsfrm = astAnnul( wcsfrm ); - fset = astAnnul( fset ); - return ret; - } - -/* We now need to choose the "FITS WCS axis" (i.e. the number that is included - in FITS keywords such as CRVAL2) for each axis of the output Frame. - Allocate memory to store these indices. */ - nwcs = astGetNout( mapping ); - wperm = astMalloc( sizeof(int)*(size_t) nwcs ); - -/* Attempt to use the FitsAxisOrder attribute to determine the order. If - this is set to "<auto>", then for each WCS axis, we use the index of - the pixel axis which is most closely aligned with it. */ - if( !FitsAxisOrder( this, nwcs, wcsfrm, wperm, status ) && - !WorldAxes( this, mapping, dim, wperm, status ) ) { - wperm = astFree( wperm ); - mapping = astAnnul( mapping ); - wcsfrm = astAnnul( wcsfrm ); - fset = astAnnul( fset ); - return ret; - } - -/* Allocate an array of flags, one for each axis, which indicate if a - description of the corresponding axis has yet been stored in the - FitsStore. Initialise them to indicate that no axes have yet been - described. */ - axis_done = astMalloc( sizeof(int)*(size_t) nwcs ); - if( astOK ) for( iax = 0; iax < nwcs; iax++ ) axis_done[ iax ] = 0; - -/* Get the original reference point from the FitsChan and convert it into - the require WCS Frame. This is used as the default reference point (some - algorithms may choose to ignore this default reference point ). */ - crvals = ReadCrval( this, wcsfrm, s, method, class, status ); - -/* For each class of FITS conventions (celestial, spectral, others), - identify any corresponding axes within the WCS Frame and add - descriptions of them to the FitsStore. These descriptions are in terms - of the FITS keywords defined in the corresponding FITS-WCS paper. Note, - the keywords which describe the pixel->IWC mapping (CRPIX, CD, PC, - CDELT) are not stored by these functions, instead each function - returns a Mapping from WCS to IWC coords (these Mappings pass on axes - of the wrong class without change). These Mappings are combined in - series to get the final WCS->IWC Mapping. First do celestial axes. */ - iwcmap = CelestialAxes( this, fset, dim, wperm, s, store, axis_done, - isoff, method, class, status ); - -/* Now look for spectral axes, and update the iwcmap. */ - tmap = SpectralAxes( this, fset, dim, wperm, s, store, crvals, axis_done, - method, class, status ); - tmap2 = (AstMapping *) astCmpMap( iwcmap, tmap, 1, "", status ); - tmap = astAnnul( tmap ); - (void) astAnnul( iwcmap ); - iwcmap = tmap2; - -/* Finally add descriptions of any axes not yet described (they are - assumed to be linear), and update the iwcmap. */ - tmap = OtherAxes( this, fset, dim, wperm, s, store, crvals, axis_done, - method, class, status ); - tmap2 = (AstMapping *) astCmpMap( iwcmap, tmap, 1, "", status ); - tmap = astAnnul( tmap ); - (void) astAnnul( iwcmap ); - iwcmap = tmap2; - -/* The "iwcmap" Mapping found above converts from the WCS Frame to the IWC - Frame. Combine the pixel->WCS Mapping with this WCS->IWC Mapping to - get the pixel->IWC Mapping. */ - pixiwcmap = (AstMapping *) astCmpMap( mapping, iwcmap, 1, "", status ); - mapping = astAnnul( mapping ); - iwcmap = astAnnul( iwcmap ); - -/* Get the maximum departure from linearity, in pixels, for the pixiwcmap - mapping to be considered linear. */ - fitstol = astGetFitsTol( this ); - -/* See if SIP headers are to be produced. */ - sipok = astGetSipOK( this ); - -/* Now attempt to store values for the keywords describing the pixel->IWC - Mapping (CRPIX, CD, PC, CDELT). This tests that the iwcmap is linear. - It can also include keywords describing distortion in the form of SIP - headers, if appropriate. Zero is returned if the test fails. */ - ret = MakeIntWorld( pixiwcmap, wcsfrm, wperm, s, store, dim, fitstol, - sipok, method, class, status ); - -/* If succesfull... */ - if( ret ) { - -/* Store the Domain name as the WCSNAME keyword (if set). */ - if( astTestDomain( wcsfrm ) ) { - SetItemC( &(store->wcsname), 0, 0, s, (char *) astGetDomain( wcsfrm ), - status ); - } - -/* Store the UT1-UTC correction, if set, converting from seconds to days - (as used by JACH). */ - if( astTestDut1( wcsfrm ) && s == ' ' ) { - SetItem( &(store->dut1), 0, 0, ' ', astGetDut1( wcsfrm )/SPD, status ); - } - -/* Store the TAI-UTC correction, if set. */ - if( astTestDtai( wcsfrm ) && s == ' ' ) { - SetItem( &(store->dtai), 0, 0, ' ', astGetDtai( wcsfrm ), status ); - } - -/* Set CRVAL values which are very small compared to the pixel size to - zero. */ - for( iax = 0; iax < nwcs; iax++ ) { - fits_i = wperm[ iax ]; - crval = GetItem( &(store->crval), fits_i, 0, s, NULL, method, class, - status ); - if( crval != AST__BAD ) { - cdelt2 = 0.0; - for( fits_j = 0; fits_j < nwcs; fits_j++ ){ - pc = GetItem( &(store->pc), fits_i, fits_j, s, NULL, method, class, status ); - if( pc == AST__BAD ) pc = ( fits_i == fits_j ) ? 1.0 : 0.0; - cdelt2 += pc*pc; - } - cdelt = GetItem( &(store->cdelt), fits_i, 0, s, NULL, method, class, status ); - if( cdelt == AST__BAD ) cdelt = 1.0; - cdelt2 *= ( cdelt*cdelt ); - if( fabs( crval ) < sqrt( DBL_EPSILON*cdelt2 ) ) { - SetItem( &(store->crval), fits_i, 0, s, 0.0, status ); - } - } - } - -/* Round CRPIX values to the nearest millionth of a pixel. */ - for( iax = 0; iax < nwcs; iax++ ) { - crpix = GetItem( &(store->crpix), 0, iax, s, NULL, method, class, status ); - if( crpix != AST__BAD ) { - SetItem( &(store->crpix), 0, iax, s, - floor( crpix*1.0E6 + 0.5 )*1.0E-6, status ); - } - } - } - -/* Free remaining resources. */ - if( crvals ) crvals = astFree( crvals ); - wcsfrm = astAnnul( wcsfrm ); - pixiwcmap = astAnnul( pixiwcmap ); - axis_done = astFree( axis_done ); - wperm = astFree( wperm ); - fset = astAnnul( fset ); - -/* If an error has occurred, return zero */ - return astOK ? ret : 0; -} - -static AstMapping *AddUnitMaps( AstMapping *map, int iax, int nax, int *status ) { -/* -* Name: -* AddUnitMaps - -* Purpose: -* Embed a Mapping within a pair of parallel UnitMaps. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *AddUnitMaps( AstMapping *map, int iax, int nax, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns a Mapping which consists of the supplied Mapping -* in parallel with a pair of UnitMaps so that the first axis of the -* supplied Mapping is at a specified axis number in the returned Mapping. - -* Parameters: -* map -* Pointer to the Mapping. The Mapping must have equal numbers of -* input and output coordinates. -* iax -* The index for the first input of "map" within the returned -* Mapping. -* nax -* The number of axes for the returned Mapping. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A Mapping which has "nax" axes, and in which the "iax" axis -* corresponds to the first axis of "map". Axes lower than "iax" are -* transformed using a UnitMap, and axes higher than the last axis of -* "map" are transformed using a UnitMap. -*/ - -/* Local Variables: */ - AstMapping *ret; /* Returned Mapping */ - AstMapping *tmap0; /* Temporary Mapping */ - AstMapping *tmap1; /* Temporary Mapping */ - AstMapping *tmap2; /* Temporary Mapping */ - int nmap; /* Number of supplied Mapping inputs */ - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Initialise the returned Mapping to be a clone of the supplied Mapping. */ - ret = astClone( map ); - -/* Note the number of inputs of the supplied Mapping (assumed to be equal - to the number of outputs). */ - nmap = astGetNin( map ); - -/* If necessary produce a parallel CmpMap which combines the Mapping with a - UnitMap representing the axes lower than "iax". */ - if( iax > 0 ) { - tmap0 = (AstMapping *) astUnitMap( iax, "", status ); - tmap1 = (AstMapping *) astCmpMap( tmap0, ret, 0, "", status ); - ret = astAnnul( ret ); - tmap0 = astAnnul( tmap0 ); - ret = tmap1; - } - -/* If necessary produce a parallel CmpMap which combines the Mapping with a - UnitMap representing the axes higher than "iax+nmap". */ - if( iax + nmap < nax ) { - tmap1 = (AstMapping *) astUnitMap( nax - iax - nmap, "", status ); - tmap2 = (AstMapping *) astCmpMap( ret, tmap1, 0, "", status ); - ret = astAnnul( ret ); - tmap1 = astAnnul( tmap1 ); - ret = tmap2; - } - -/* Return the result. */ - return ret; -} - -static int AIPSFromStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ){ - -/* -* Name: -* AIPSFromStore - -* Purpose: -* Store WCS keywords in a FitsChan using FITS-AIPS encoding. - -* Type: -* Private function. - -* Synopsis: - -* int AIPSFromStore( AstFitsChan *this, FitsStore *store, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function copies the WCS information stored in the supplied -* FitsStore into the supplied FitsChan, using FITS-AIPS encoding. -* -* AIPS encoding is like FITS-WCS encoding but with the following -* restrictions: -* -* 1) The celestial projection must not have any projection parameters -* which are not set to their default values. The one exception to this -* is that SIN projections are acceptable if the associated projection -* parameter PV<axlat>_1 is zero and PV<axlat>_2 = cot( reference point -* latitude). This is encoded using the string "-NCP". The SFL projection -* is encoded using the string "-GLS". Note, the original AIPS WCS -* system only recognised a small subset of the currently available -* projections, but some more recent AIPS-like software recognizes some -* of the new projections included in the FITS-WCS encoding. The AIT, -* GLS and MER can only be written if the CRVAL keywords are zero for -* both longitude and latitude axes. -* -* 2) The celestial axes must be RA/DEC, galactic or ecliptic. -* -* 3) LONPOLE and LATPOLE must take their default values. -* -* 4) Only primary axis descriptions are written out. -* -* 5) EPOCH is written instead of EQUINOX & RADECSYS, and uses the -* IAU 1984 rule ( EPOCH < 1984.0 is treated as a Besselian epoch -* and implies RADECSYS=FK4, EPOCH >= 1984.0 is treated as a -* Julian epoch and implies RADECSYS=FK5). The RADECSYS & EQUINOX -* values in the FitsStore must be consistent with this rule. -* -* 6) Any rotation produced by the PC matrix must be restricted to -* the celestial plane, and must involve no shear. A CROTA keyword -* with associated CDELT values are produced instead of the PC -* matrix. -* -* 7) ICRS is not supported. -* -* 8) Spectral axes can be created only for FITS-WCS CTYPE values of "FREQ" -* "VRAD" and "VOPT-F2W" and with standards of rest of LSRK, LSRD, -* BARYCENT and GEOCENTR. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if succesfull, and zero is returned -* otherwise. -*/ - -/* Local Variables: */ - char *comm; /* Pointer to comment string */ - const char *cval; /* Pointer to string keyword value */ - const char *specunit;/* Pointer to corrected spectral units string */ - char combuf[80]; /* Buffer for FITS card comment */ - char lattype[MXCTYPELEN];/* Latitude axis CTYPE */ - char lontype[MXCTYPELEN];/* Longitude axis CTYPE */ - char s; /* Co-ordinate version character */ - char sign[2]; /* Fraction's sign character */ - char spectype[MXCTYPELEN];/* Spectral axis CTYPE */ - double *cdelt; /* Pointer to CDELT array */ - double cdl; /* CDELT term */ - double cdlat_lon; /* Off-diagonal CD element */ - double cdlon_lat; /* Off-diagonal CD element */ - double coscro; /* Cos( CROTA ) */ - double crota; /* CROTA value to use */ - double epoch; /* Epoch of reference equinox */ - double fd; /* Fraction of a day */ - double latval; /* CRVAL for latitude axis */ - double lonval; /* CRVAL for longitude axis */ - double mjd99; /* MJD at start of 1999 */ - double p1, p2; /* Projection parameters */ - double rho_a; /* First estimate of CROTA */ - double rho_b; /* Second estimate of CROTA */ - double sincro; /* Sin( CROTA ) */ - double specfactor; /* Factor for converting internal spectral units */ - double val; /* General purpose value */ - int axlat; /* Index of latitude FITS WCS axis */ - int axlon; /* Index of longitude FITS WCS axis */ - int axrot1; /* Index of first CROTA rotation axis */ - int axrot2; /* Index of second CROTA rotation axis */ - int axspec; /* Index of spectral FITS WCS axis */ - int i; /* Axis index */ - int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ - int iymdf[ 4 ]; /* Year, month, date, fractional day */ - int j; /* Axis index */ - int jj; /* SlaLib status */ - int naxis; /* No. of axes */ - int ok; /* Is FitsSTore OK for IRAF encoding? */ - int prj; /* Projection type */ - -/* Check the inherited status. */ - if( !astOK ) return 0; - -/* Initialise */ - specunit = ""; - specfactor = 1.0; - -/* First check that the values in the FitsStore conform to the - requirements of the AIPS encoding. Assume they do to begin with. */ - ok = 1; - -/* Just do primary axes. */ - s = ' '; - -/* Look for the primary celestial axes. */ - FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); - -/* If both longitude and latitude axes are present ...*/ - if( axlon >= 0 && axlat >= 0 ) { - -/* Get the CRVAL values for both axes. */ - latval = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status ); - if( latval == AST__BAD ) ok = 0; - lonval = GetItem( &( store->crval ), axlon, 0, s, NULL, method, class, status ); - if( lonval == AST__BAD ) ok = 0; - -/* Get the CTYPE values for both axes. Extract the projection type as - specified by the last 4 characters in the latitude CTYPE keyword value. */ - cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else { - strcpy( lontype, cval ); - } - cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - prj = AST__WCSBAD; - } else { - strcpy( lattype, cval ); - prj = astWcsPrjType( cval + 4 ); - } - -/* Check the projection type is OK. */ - if( prj == AST__WCSBAD ){ - ok = 0; - } else if( prj != AST__SIN ){ - -/* There must be no projection parameters. */ - if( GetMaxJM( &(store->pv), ' ', status ) >= 0 ) { - ok = 0; - -/* FITS-AIPS cannot handle the AST-specific TPN projection. */ - } else if( prj == AST__TPN ) { - ok = 0; - -/* For AIT, MER and GLS, check that the reference point is the origin of - the celestial co-ordinate system. */ - } else if( prj == AST__MER || - prj == AST__AIT || - prj == AST__SFL ) { - if( latval != 0.0 || lonval != 0.0 ){ - ok = 0; - -/* Change the new SFL projection code to to the older equivalent GLS */ - } else if( prj == AST__SFL ){ - (void) strcpy( lontype + 4, "-GLS" ); - (void) strcpy( lattype + 4, "-GLS" ); - } - } - -/* SIN projections are only acceptable if the associated projection - parameters are both zero, or if the first is zero and the second - = cot( reference point latitude ) (the latter case is equivalent to - the old NCP projection). */ - } else { - p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status ); - p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status ); - if( p1 == AST__BAD ) p1 = 0.0; - if( p2 == AST__BAD ) p2 = 0.0; - ok = 0; - if( p1 == 0.0 ) { - if( p2 == 0.0 ) { - ok = 1; - } else if( fabs( p2 ) >= 1.0E14 && latval == 0.0 ){ - ok = 1; - (void) strcpy( lontype + 4, "-NCP" ); - (void) strcpy( lattype + 4, "-NCP" ); - } else if( fabs( p2*tan( AST__DD2R*latval ) - 1.0 ) - < 0.01 ){ - ok = 1; - (void) strcpy( lontype + 4, "-NCP" ); - (void) strcpy( lattype + 4, "-NCP" ); - } - } - } - -/* Identify the celestial coordinate system from the first 4 characters of the - longitude CTYPE value. Only RA, galactic longitude, and ecliptic - longitude can be stored using FITS-AIPS. */ - if( ok && strncmp( lontype, "RA--", 4 ) && - strncmp( lontype, "GLON", 4 ) && - strncmp( lontype, "ELON", 4 ) ) ok = 0; - -/* If the physical Frame requires a LONPOLE or LATPOLE keyword, it cannot - be encoded using FITS-IRAF. */ - if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ) - != AST__BAD || - GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ) - != AST__BAD ) ok = 0; - } - -/* If a spectral axis is present ...*/ - if( ok && axspec >= 0 ) { - -/* Get the CTYPE values for the axis, and find the AIPS equivalent, if - possible. */ - cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else { - if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) { - strcpy( spectype, "FREQ" ); - } else if( !strncmp( cval, "VRAD", astChrLen( cval ) ) ) { - strcpy( spectype, "VELO" ); - } else if( !strncmp( cval, "VOPT-F2W", astChrLen( cval ) ) ) { - strcpy( spectype, "FELO" ); - } else { - ok = 0; - } - } - -/* If OK, check the SPECSYS value and add the AIPS equivalent onto the - end of the CTYPE value.*/ - cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else if( ok ) { - if( !strncmp( cval, "LSRK", astChrLen( cval ) ) ) { - strcpy( spectype+4, "-LSR" ); - } else if( !strncmp( cval, "LSRD", astChrLen( cval ) ) ) { - strcpy( spectype+4, "-LSD" ); - } else if( !strncmp( cval, "BARYCENT", astChrLen( cval ) ) ) { - strcpy( spectype+4, "-HEL" ); - } else if( !strncmp( cval, "GEOCENTR", astChrLen( cval ) ) ) { - strcpy( spectype+4, "-GEO" ); - } else { - ok = 0; - } - } - -/* If still OK, ensure the spectral axis units are Hz or m/s. */ - cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else if( ok ) { - if( !strcmp( cval, "Hz" ) ) { - specunit = "HZ"; - specfactor = 1.0; - } else if( !strcmp( cval, "kHz" ) ) { - specunit = "HZ"; - specfactor = 1.0E3; - } else if( !strcmp( cval, "MHz" ) ) { - specunit = "HZ"; - specfactor = 1.0E6; - } else if( !strcmp( cval, "GHz" ) ) { - specunit = "HZ"; - specfactor = 1.0E9; - } else if( !strcmp( cval, "m/s" ) ) { - specunit = "m/s"; - specfactor = 1.0; - } else if( !strcmp( cval, "km/s" ) ) { - specunit = "m/s"; - specfactor = 1.0E3; - } else { - ok = 0; - } - } - } - -/* Save the number of axes */ - naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1; - -/* If this is different to the value of NAXIS abort since this encoding - does not support WCSAXES keyword. */ - if( naxis != store->naxis ) ok = 0; - -/* Allocate memory to store the CDELT values */ - if( ok ) { - cdelt = (double *) astMalloc( sizeof(double)*naxis ); - if( !cdelt ) ok = 0; - } else { - cdelt = NULL; - } - -/* Check that rotation is restricted to the celestial plane, and extract - the CDELT (diagonal) terms, etc. If there are no celestial - axes, restrict rotation to the first two non-spectral axes. */ - if( axlat < 0 && axlon < 0 ) { - if( axspec >= 0 && naxis > 2 ) { - axrot2 = ( axspec == 0 ) ? 1 : 0; - axrot1 = axrot2 + 1; - if( axrot1 == axspec ) axrot1++; - } else if( naxis > 1 ){ - axrot2 = 0; - axrot1 = 1; - } else { - axrot2 = -1; - axrot1 = -1; - } - } else { - axrot1 = axlon; - axrot2 = axlat; - } - cdlat_lon = 0.0; - cdlon_lat = 0.0; - for( i = 0; i < naxis && ok; i++ ){ - cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - if( cdl == AST__BAD ) cdl = 1.0; - for( j = 0; j < naxis && ok; j++ ){ - val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; - val *= cdl; - if( i == j ){ - cdelt[ i ] = val; - } else if( i == axrot2 && j == axrot1 ){ - cdlat_lon = val; - } else if( i == axrot1 && j == axrot2 ){ - cdlon_lat = val; - } else if( val != 0.0 ){ - ok = 0; - } - } - } - -/* Find the CROTA and CDELT values for the celestial axes. */ - if( ok && axrot1 >= 0 && axrot2 >= 0 ) { - if( cdlat_lon > 0.0 ) { - rho_a = atan2( cdlat_lon, cdelt[ axrot1 ] ); - } else if( cdlat_lon == 0.0 ) { - rho_a = 0.0; - } else { - rho_a = atan2( -cdlat_lon, -cdelt[ axrot1 ] ); - } - if( cdlon_lat > 0.0 ) { - rho_b = atan2( cdlon_lat, -cdelt[ axrot2 ] ); - } else if( cdlon_lat == 0.0 ) { - rho_b = 0.0; - } else { - rho_b = atan2( -cdlon_lat, cdelt[ axrot2 ] ); - } - if( fabs( palDrange( rho_a - rho_b ) ) < 1.0E-2 ){ - crota = 0.5*( palDranrm( rho_a ) + palDranrm( rho_b ) ); - coscro = cos( crota ); - sincro = sin( crota ); - if( fabs( coscro ) > fabs( sincro ) ){ - cdelt[ axrot2 ] /= coscro; - cdelt[ axrot1 ] /= coscro; - } else { - cdelt[ axrot2 ] = -cdlon_lat/sincro; - cdelt[ axrot1 ] = cdlat_lon/sincro; - } - crota *= AST__DR2D; - } else { - ok = 0; - } - } else { - crota = 0.0; - } - -/* Get RADECSYS and the reference equinox (called EPOCH in FITS-AIPS). */ - cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); - epoch = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); - -/* If RADECSYS was available... */ - if( cval ){ - -/* ICRS is not supported in this encoding. */ - if( !strcmp( "ICRS", cval ) ) ok = 0; - -/* If epoch was not available, set a default epoch. */ - if( epoch == AST__BAD ){ - if( !strcmp( "FK4", cval ) ){ - epoch = 1950.0; - } else if( !strcmp( "FK5", cval ) ){ - epoch = 2000.0; - } else { - ok = 0; - } - -/* If an epoch was supplied, check it is consistent with the IAU 1984 - rule. */ - } else { - if( !strcmp( "FK4", cval ) ){ - if( epoch >= 1984.0 ) ok = 0; - } else if( !strcmp( "FK5", cval ) ){ - if( epoch < 1984.0 ) ok = 0; - } else { - ok = 0; - } - } - } - -/* Only create the keywords if the FitsStore conforms to the requirements - of the FITS-AIPS encoding. */ - if( ok ) { - -/* Get and save CRPIX for all pixel axes. These are required, so break - if they are not available. */ - for( j = 0; j < naxis && ok; j++ ){ - val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - } else { - sprintf( combuf, "Reference pixel on axis %d", j + 1 ); - SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val, - AST__FLOAT, combuf, status ); - } - } - -/* Get and save CRVAL for all intermediate axes. These are required, so - break if they are not available. */ - for( i = 0; i < naxis && ok; i++ ){ - val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - } else { - if( i == axspec ) val *= specfactor; - sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); - SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, - AST__FLOAT, combuf, status ); - } - } - -/* Get and save CTYPE for all intermediate axes. These are required, so - break if they are not available. Use the potentially modified versions - saved above for the celestial axes. */ - for( i = 0; i < naxis && ok; i++ ){ - if( i == axlat ) { - cval = lattype; - } else if( i == axlon ) { - cval = lontype; - } else if( i == axspec ) { - cval = spectype; - } else { - cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - } - if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) { - comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); - if( !comm ) { - sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); - comm = combuf; - } - SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, - AST__STRING, comm, status ); - } else { - ok = 0; - } - } - -/* CDELT values */ - if( axspec != -1 ) cdelt[ axspec ] *= specfactor; - for( i = 0; i < naxis; i++ ){ - SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i, - AST__FLOAT, "Pixel size", status ); - } - -/* CUNIT values. */ - for( i = 0; i < naxis; i++ ) { - cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); - if( cval ) { - if( i == axspec ) cval = specunit; - sprintf( combuf, "Units for axis %d", i + 1 ); - SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING, - combuf, status ); - } - } - -/* CROTA */ - if( axrot2 != -1 ){ - SetValue( this, FormatKey( "CROTA", axrot2 + 1, -1, s, status ), &crota, - AST__FLOAT, "Axis rotation", status ); - } else if( ( axspec == -1 && naxis > 1 ) || - ( axspec != -1 && naxis > 2 ) ) { - SetValue( this, "CROTA1", &crota, AST__FLOAT, "Axis rotation", status ); - } - -/* Reference equinox */ - if( epoch != AST__BAD ) SetValue( this, "EPOCH", &epoch, AST__FLOAT, - "Epoch of reference equinox", status ); - -/* Date of observation. */ - val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status ); - if( val != AST__BAD ) { - -/* The format used for the DATE-OBS keyword depends on the value of the - keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. - Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ - palCaldj( 99, 1, 1, &mjd99, &jj ); - if( val < mjd99 ) { - palDjcal( 0, val, iymdf, &jj ); - sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], - iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); - } else { - palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); - palDd2tf( 3, fd, sign, ihmsf ); - sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", - iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], - ihmsf[2], ihmsf[3] ); - } - -/* Now store the formatted string in the FitsChan. */ - cval = combuf; - SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, - "Date of observation", status ); - } - -/* Spectral stuff.. */ - if( axspec >= 0 ) { - -/* Rest frequency */ - val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFREQ", -1, -1, s, status ), - &val, AST__FLOAT, "[Hz] Rest frequency", status ); - } - } - -/* Release CDELT workspace */ - if( cdelt ) cdelt = (double *) astFree( (void *) cdelt ); - -/* Return zero or ret depending on whether an error has occurred. */ - return astOK ? ok : 0; -} - -static int AIPSPPFromStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ){ - -/* -* Name: -* AIPSPPFromStore - -* Purpose: -* Store WCS keywords in a FitsChan using FITS-AIPS++ encoding. - -* Type: -* Private function. - -* Synopsis: - -* int AIPSPPFromStore( AstFitsChan *this, FitsStore *store, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function copies the WCS information stored in the supplied -* FitsStore into the supplied FitsChan, using FITS-AIPS++ encoding. -* -* AIPS++ encoding is like FITS-WCS encoding but with the following -* restrictions: -* -* 1) The celestial axes must be RA/DEC, galactic or ecliptic. -* -* 2) Only primary axis descriptions are written out. -* -* 3) RADESYS is not written and so the RADECSYS & EQUINOX values in the -* FitsStore must be consistent with the "1984" rule. -* -* 4) Any rotation produced by the PC matrix must be restricted to -* the celestial plane, and must involve no shear. A CROTA keyword -* with associated CDELT values are produced instead of the PC -* matrix. -* -* 5) ICRS is not supported. -* -* 6) Spectral axes can be created only for FITS-WCS CTYPE values of "FREQ" -* "VRAD" and "VOPT-F2W" and with standards of rest of LSRK, LSRD, -* BARYCENT and GEOCENTR. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if succesfull, and zero is returned -* otherwise. -*/ - -/* Local Variables: */ - char *comm; /* Pointer to comment string */ - const char *cval; /* Pointer to string keyword value */ - const char *specunit;/* Pointer to corrected spectral units string */ - char combuf[80]; /* Buffer for FITS card comment */ - char lattype[MXCTYPELEN];/* Latitude axis CTYPE */ - char lontype[MXCTYPELEN];/* Longitude axis CTYPE */ - char s; /* Co-ordinate version character */ - char sign[2]; /* Fraction's sign character */ - char spectype[MXCTYPELEN];/* Spectral axis CTYPE */ - double *cdelt; /* Pointer to CDELT array */ - double cdl; /* CDELT term */ - double cdlat_lon; /* Off-diagonal CD element */ - double cdlon_lat; /* Off-diagonal CD element */ - double coscro; /* Cos( CROTA ) */ - double crota; /* CROTA value to use */ - double epoch; /* Epoch of reference equinox */ - double fd; /* Fraction of a day */ - double mjd99; /* MJD at start of 1999 */ - double rho_a; /* First estimate of CROTA */ - double rho_b; /* Second estimate of CROTA */ - double sincro; /* Sin( CROTA ) */ - double specfactor; /* Factor for converting internal spectral units */ - double val; /* General purpose value */ - int axlat; /* Index of latitude FITS WCS axis */ - int axlon; /* Index of longitude FITS WCS axis */ - int axrot1; /* Index of first CROTA rotation axis */ - int axrot2; /* Index of second CROTA rotation axis */ - int axspec; /* Index of spectral FITS WCS axis */ - int i; /* Axis index */ - int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ - int iymdf[ 4 ]; /* Year, month, date, fractional day */ - int j; /* Axis index */ - int jj; /* SlaLib status */ - int m; /* Projection parameter index */ - int maxm; /* Max projection parameter index */ - int naxis; /* No. of axes */ - int ok; /* Is FitsSTore OK for IRAF encoding? */ - int prj; /* Projection type */ - -/* Check the inherited status. */ - if( !astOK ) return 0; - -/* Initialise */ - specunit = ""; - specfactor = 1.0; - maxm = 0; - -/* First check that the values in the FitsStore conform to the - requirements of the AIPS++ encoding. Assume they do to begin with. */ - ok = 1; - -/* Just do primary axes. */ - s = ' '; - -/* Save the number of axes */ - naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1; - -/* Look for the primary celestial and spectral axes. */ - FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); - -/* If both longitude and latitude axes are present ...*/ - if( axlon >= 0 && axlat >= 0 ) { - -/* Get the CTYPE values for both axes. Extract the projection type as - specified by the last 4 characters in the latitude CTYPE keyword value. */ - cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else { - strcpy( lontype, cval ); - } - cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - prj = AST__WCSBAD; - } else { - strcpy( lattype, cval ); - prj = astWcsPrjType( cval + 4 ); - } - -/* FITS-AIPS++ cannot handle the AST-specific TPN projection. */ - if( prj == AST__TPN || prj == AST__WCSBAD ) ok = 0; - -/* Projection parameters. FITS-AIPS++ encoding ignores projection parameters - associated with the longitude axis. The number of parameters is limited to - 10. */ - maxm = GetMaxJM( &(store->pv), ' ', status ); - for( i = 0; i < naxis && ok; i++ ){ - if( i != axlon ) { - for( m = 0; m <= maxm; m++ ){ - val = GetItem( &(store->pv), i, m, s, NULL, method, class, status ); - if( val != AST__BAD ) { - if( i != axlat || m >= 10 ){ - ok = 0; - break; - } - } - } - } - } - -/* Identify the celestial coordinate system from the first 4 characters of the - longitude CTYPE value. Only RA, galactic longitude, and ecliptic - longitude can be stored using FITS-AIPS++. */ - if( ok && strncmp( lontype, "RA--", 4 ) && - strncmp( lontype, "GLON", 4 ) && - strncmp( lontype, "ELON", 4 ) ) ok = 0; - } - -/* If a spectral axis is present ...*/ - if( axspec >= 0 ) { - -/* Get the CTYPE values for the axis, and find the AIPS equivalent, if - possible. */ - cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else { - if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) { - strcpy( spectype, "FREQ" ); - } else if( !strncmp( cval, "VRAD", astChrLen( cval ) ) ) { - strcpy( spectype, "VELO" ); - } else if( !strncmp( cval, "VOPT-F2W", astChrLen( cval ) ) ) { - strcpy( spectype, "FELO" ); - } else { - ok = 0; - } - } - -/* If OK, check the SPECSYS value and add the AIPS equivalent onto the - end of the CTYPE value.*/ - cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else { - if( !strncmp( cval, "LSRK", astChrLen( cval ) ) ) { - strcpy( spectype+4, "-LSR" ); - } else if( !strncmp( cval, "LSRD", astChrLen( cval ) ) ) { - strcpy( spectype+4, "-LSD" ); - } else if( !strncmp( cval, "BARYCENT", astChrLen( cval ) ) ) { - strcpy( spectype+4, "-HEL" ); - } else if( !strncmp( cval, "GEOCENTR", astChrLen( cval ) ) ) { - strcpy( spectype+4, "-GEO" ); - } else { - ok = 0; - } - } - -/* If still OK, ensure the spectral axis units are Hz or m/s. */ - cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else if( ok ) { - if( !strcmp( cval, "Hz" ) ) { - specunit = "HZ"; - specfactor = 1.0; - } else if( !strcmp( cval, "kHz" ) ) { - specunit = "HZ"; - specfactor = 1.0E3; - } else if( !strcmp( cval, "MHz" ) ) { - specunit = "HZ"; - specfactor = 1.0E6; - } else if( !strcmp( cval, "GHz" ) ) { - specunit = "HZ"; - specfactor = 1.0E9; - } else if( !strcmp( cval, "m/s" ) ) { - specunit = "m/s"; - specfactor = 1.0; - } else if( !strcmp( cval, "km/s" ) ) { - specunit = "m/s"; - specfactor = 1.0E3; - } else { - ok = 0; - } - } - } - -/* If this is different to the value of NAXIS abort since this encoding - does not support WCSAXES keyword. */ - if( naxis != store->naxis ) ok = 0; - -/* Allocate memory to store the CDELT values */ - if( ok ) { - cdelt = (double *) astMalloc( sizeof(double)*naxis ); - if( !cdelt ) ok = 0; - } else { - cdelt = NULL; - } - -/* Check that rotation is restricted to the celestial plane, and extract - the CDELT (diagonal) terms, etc. If there are no celestial - axes, restrict rotation to the first two non-spectral axes. */ - if( axlat < 0 && axlon < 0 ) { - if( axspec >= 0 && naxis > 2 ) { - axrot2 = ( axspec == 0 ) ? 1 : 0; - axrot1 = axrot2 + 1; - if( axrot1 == axspec ) axrot1++; - } else if( naxis > 1 ){ - axrot2 = 0; - axrot1 = 1; - } else { - axrot2 = -1; - axrot1 = -1; - } - } else { - axrot1 = axlon; - axrot2 = axlat; - } - cdlat_lon = 0.0; - cdlon_lat = 0.0; - for( i = 0; i < naxis && ok; i++ ){ - cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - if( cdl == AST__BAD ) cdl = 1.0; - for( j = 0; j < naxis && ok; j++ ){ - val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; - val *= cdl; - if( i == j ){ - cdelt[ i ] = val; - } else if( i == axrot2 && j == axrot1 ){ - cdlat_lon = val; - } else if( i == axrot1 && j == axrot2 ){ - cdlon_lat = val; - } else if( val != 0.0 ){ - ok = 0; - } - } - } - -/* Find the CROTA and CDELT values for the celestial axes. */ - if( ok && axrot1 >= 0 && axrot2 >= 0 ) { - if( cdlat_lon > 0.0 ) { - rho_a = atan2( cdlat_lon, cdelt[ axrot1 ] ); - } else if( cdlat_lon == 0.0 ) { - rho_a = 0.0; - } else { - rho_a = atan2( -cdlat_lon, -cdelt[ axrot1 ] ); - } - if( cdlon_lat > 0.0 ) { - rho_b = atan2( cdlon_lat, -cdelt[ axrot2 ] ); - } else if( cdlon_lat == 0.0 ) { - rho_b = 0.0; - } else { - rho_b = atan2( -cdlon_lat, cdelt[ axrot2 ] ); - } - if( fabs( palDrange( rho_a - rho_b ) ) < 1.0E-2 ){ - crota = 0.5*( palDranrm( rho_a ) + palDranrm( rho_b ) ); - coscro = cos( crota ); - sincro = sin( crota ); - if( fabs( coscro ) > fabs( sincro ) ){ - cdelt[ axrot2 ] /= coscro; - cdelt[ axrot1 ] /= coscro; - } else { - cdelt[ axrot2 ] = -cdlon_lat/sincro; - cdelt[ axrot1 ] = cdlat_lon/sincro; - } - crota *= AST__DR2D; - -/* Use AST__BAD to indicate that CDi_j values should be produced - instead of CROTA/CDELT. (I am told AIPS++ can understand CD matrices) */ - } else { - crota = AST__BAD; - } - } else { - crota = 0.0; - } - -/* Get RADECSYS and the reference equinox. */ - cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); - epoch = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); - -/* If RADECSYS was available... */ - if( cval ){ - -/* ICRS is not supported in this encoding. */ - if( !strcmp( "ICRS", cval ) ) ok = 0; - -/* If epoch was not available, set a default epoch. */ - if( epoch == AST__BAD ){ - if( !strcmp( "FK4", cval ) ){ - epoch = 1950.0; - } else if( !strcmp( "FK5", cval ) ){ - epoch = 2000.0; - } else { - ok = 0; - } - -/* If an equinox was supplied, check it is consistent with the IAU 1984 - rule. */ - } else { - if( !strcmp( "FK4", cval ) ){ - if( epoch >= 1984.0 ) ok = 0; - } else if( !strcmp( "FK5", cval ) ){ - if( epoch < 1984.0 ) ok = 0; - } else { - ok = 0; - } - } - } - -/* Only create the keywords if the FitsStore conforms to the requirements - of the FITS-AIPS++ encoding. */ - if( ok ) { - -/* Get and save CRPIX for all pixel axes. These are required, so break - if they are not available. */ - for( j = 0; j < naxis && ok; j++ ){ - val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - } else { - sprintf( combuf, "Reference pixel on axis %d", j + 1 ); - SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val, - AST__FLOAT, combuf, status ); - } - } - -/* Get and save CRVAL for all intermediate axes. These are required, so - break if they are not available. */ - for( i = 0; i < naxis && ok; i++ ){ - val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - } else { - if( i == axspec ) val *= specfactor; - sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); - SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, - AST__FLOAT, combuf, status ); - } - } - -/* Get and save CTYPE for all intermediate axes. These are required, so - break if they are not available. Use the potentially modified versions - saved above for the celestial axes. */ - for( i = 0; i < naxis && ok; i++ ){ - if( i == axlat ) { - cval = lattype; - } else if( i == axlon ) { - cval = lontype; - } else if( i == axspec ) { - cval = spectype; - } else { - cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - } - if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) { - comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); - if( !comm ) { - sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); - comm = combuf; - } - SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, - AST__STRING, comm, status ); - } else { - ok = 0; - } - } - -/* CDELT values */ - if( axspec != -1 ) cdelt[ axspec ] *= specfactor; - for( i = 0; i < naxis; i++ ){ - SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i, - AST__FLOAT, "Pixel size", status ); - } - -/* CUNIT values. [Spectral axis units should be upper-case] */ - for( i = 0; i < naxis; i++ ) { - cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); - if( cval ) { - if( i == axspec ) cval = specunit; - sprintf( combuf, "Units for axis %d", i + 1 ); - SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING, - combuf, status ); - } - } - -/* CD matrix. Multiply the row of the PC matrix by the CDELT value. */ - if( crota == AST__BAD ) { - for( i = 0; i < naxis; i++ ) { - cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - if( cdl == AST__BAD ) cdl = 1.0; - for( j = 0; j < naxis; j++ ){ - val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; - val *= cdl; - if( val != 0.0 ) { - SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val, - AST__FLOAT, "Transformation matrix element", status ); - } - } - } - -/* CROTA */ - } else if( crota != 0.0 ) { - if( axrot2 != -1 ){ - SetValue( this, FormatKey( "CROTA", axrot2 + 1, -1, s, status ), &crota, - AST__FLOAT, "Axis rotation", status ); - } else if( ( axspec == -1 && naxis > 1 ) || - ( axspec != -1 && naxis > 2 ) ) { - SetValue( this, "CROTA1", &crota, AST__FLOAT, "Axis rotation", status ); - } - } - -/* Reference equinox */ - if( epoch != AST__BAD ) SetValue( this, "EPOCH", &epoch, AST__FLOAT, - "Epoch of reference equinox", status ); - -/* Latitude of native north pole. */ - val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, "LATPOLE", &val, AST__FLOAT, - "Latitude of native north pole", status ); - -/* Longitude of native north pole. */ - val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, "LONPOLE", &val, AST__FLOAT, - "Longitude of native north pole", status ); - -/* Date of observation. */ - val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status ); - if( val != AST__BAD ) { - -/* The format used for the DATE-OBS keyword depends on the value of the - keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. - Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ - palCaldj( 99, 1, 1, &mjd99, &jj ); - if( val < mjd99 ) { - palDjcal( 0, val, iymdf, &jj ); - sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], - iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); - } else { - palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); - palDd2tf( 3, fd, sign, ihmsf ); - sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", - iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], - ihmsf[2], ihmsf[3] ); - } - -/* Now store the formatted string in the FitsChan. */ - cval = combuf; - SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, - "Date of observation", status ); - } - -/* Projection parameters. */ - if( axlat >= 0 && axlon >= 0 ) { - for( m = 0; m <= maxm; m++ ){ - val = GetItem( &(store->pv), axlat, m, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "PROJP", m, -1, ' ', status ), - &val, AST__FLOAT, "Projection parameter", status ); - } - } - -/* Spectral stuff.. */ - if( axspec >= 0 ) { - -/* Rest frequency */ - val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFREQ", -1, -1, s, status ), - &val, AST__FLOAT, "[Hz] Rest frequency", status ); - } - } - -/* Release CDELT workspace */ - if( cdelt ) cdelt = (double *) astFree( (void *) cdelt ); - -/* Return zero or ret depending on whether an error has occurred. */ - return astOK ? ok : 0; -} - -static char *CardComm( AstFitsChan *this, int *status ){ - -/* -* Name: -* CardComm - -* Purpose: -* Return the keyword comment from the current card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* char *CardComm( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns a pointer to a string holding the keyword comment from the -* current card. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the keyword comment, or NULL if the FitsChan is at -* end-of-file, or does not have a comment. - -* Notes: -* - The current card is not changed by this function. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - char *ret; - -/* Check the supplied object. */ - if( !this ) return NULL; - -/* If the current card is defined, store a pointer to its keyword comment. */ - if( this->card ){ - ret = ( (FitsCard *) this->card )->comment; - -/* Otherwise store a NULL pointer. */ - } else { - ret = NULL; - } - -/* Return the answer. */ - return ret; -} - -static void *CardData( AstFitsChan *this, size_t *size, int *status ){ - -/* -* Name: -* CardData - -* Purpose: -* Return a pointer to the keyword data value for the current card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void *CardData( AstFitsChan *this, size_t *size, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns a pointer to keyword data value from the current card. - -* Parameters: -* this -* Pointer to the FitsChan. -* size -* A pointer to a location at which to return the number of bytes -* occupied by the data value. NULL can be supplied if this -* information is not required. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the keyword data, or NULL if the FitsChan is at -* end-of-file, or if the keyword does not have any data. - -* Notes: -* - For text data, the returned value for "size" includes the -* terminating null character. -* - The current card is not changed by this function. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - void *ret; - -/* Check the supplied object. */ - if( !this ) return NULL; - -/* If the current card is defined, store a pointer to its keyword data. */ - if( this->card ){ - ret = ( (FitsCard *) this->card )->data; - if( size ) *size = ( (FitsCard *) this->card )->size; - -/* Otherwise store a NULL pointer. */ - } else { - ret = NULL; - if( size ) *size = 0; - } - -/* Return the answer. */ - return ret; -} - -static int *CardFlags( AstFitsChan *this, int *status ){ - -/* -* Name: -* CardFlags - -* Purpose: -* Return a pointer to the flags mask for the current card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int *CardFlags( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns a pointer to the flags mask for the current card. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The pointer to the flags mask. - -* Notes: -* - The current card is not changed by this function. -* - NULL is returned if the current card is not defined. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - int *ret; - -/* Check the supplied object. */ - if( !this ) return NULL; - -/* If the current card is defined, store its deletion flag. */ - if( this->card ){ - ret = &( ( (FitsCard *) this->card )->flags ); - -/* Otherwise store zero. */ - } else { - ret = NULL; - } - -/* Return the answer. */ - return ret; -} - -static char *CardName( AstFitsChan *this, int *status ){ -/* -* Name: -* CardName - -* Purpose: -* Return the keyword name from the current card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* char *CardName( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns a pointer to a string holding the keyword name from the -* current card. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the keyword name, or NULL if the FitsChan is at -* end-of-file. - -* Notes: -* - The current card is not changed by this function. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - char *ret; - -/* Check the supplied object. */ - if( !this ) return NULL; - -/* If the current card is defined, store a pointer to its keyword name. */ - if( this->card ){ - ret = ( (FitsCard *) this->card )->name; - -/* Otherwise store a NULL pointer. */ - } else { - ret = NULL; - } - -/* Return the answer. */ - return ret; -} - -static int CardType( AstFitsChan *this, int *status ){ -/* -* Name: -* CardType - -* Purpose: -* Return the keyword type from the current card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int CardType( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns the keyword type from the current card. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The keyword type. - -* Notes: -* - The current card is not changed by this function. -* - AST__NOTYPE is returned if the current card is not defined. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - int ret; - -/* Check the supplied object. */ - if( !this ) return AST__NOTYPE; - -/* If the current card is defined, store the keyword type. */ - if( this->card ){ - ret = ( (FitsCard *) this->card )->type; - -/* Otherwise store AST__NOTYPE. */ - } else { - ret = AST__NOTYPE; - } - -/* Return the answer. */ - return ret; -} - -static AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *fs, double *dim, - int *wperm, char s, FitsStore *store, int *axis_done, - int isoff, const char *method, const char *class, int *status ){ - -/* -* Name: -* CelestialAxes - -* Purpose: -* Add values to a FitsStore describing celestial axes in a Frame. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *fs, double *dim, -* int *wperm, char s, FitsStore *store, int *axis_done, -* int isoff, const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The current Frame of the supplied FrameSet is searched for celestial -* axes. If any are found, FITS WCS keyword values describing the axis -* are added to the supplied FitsStore, if possible (the conventions -* of FITS-WCS paper II are used). Note, this function does not store -* values for keywords which define the transformation from pixel -* coords to Intermediate World Coords (CRPIX, PC and CDELT), but a -* Mapping is returned which embodies these values. This Mapping is -* from the current Frame in the FrameSet (WCS coords) to a Frame -* representing IWC. The IWC Frame has the same number of axes as the -* WCS Frame which may be greater than the number of base Frame (i.e. -* pixel) axes. - -* Parameters: -* this -* Pointer to the FitsChan. -* fs -* Pointer to the FrameSet. The base Frame should represent FITS pixel -* coordinates, and the current Frame should represent FITS WCS -* coordinates. The number of base Frame axes should not exceed the -* number of current Frame axes. -* dim -* An array holding the image dimensions in pixels. AST__BAD can be -* supplied for any unknown dimensions. -* wperm -* Pointer to an array of integers with one element for each axis of -* the current Frame. Each element holds the zero-based -* index of the FITS-WCS axis (i.e. the value of "i" in the keyword -* names "CTYPEi", "CRVALi", etc) which describes the Frame axis. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* store -* The FitsStore in which to store the FITS WCS keyword values. -* axis_done -* An array of flags, one for each Frame axis, which indicate if a -* description of the corresponding axis has yet been stored in the -* FitsStore. -* isoff -* If greater than zero, the description to add to the FitsStore -* should describe offset coordinates. If less than zero, the -* description to add to the FitsStore should describe absolute -* coordinates but should include the SkyRefIs, SkyRef and SkyRefP -* attributes. If zero, ignore all offset coordinate info. The -* absolute value indicates the nature of the reference point: -* 1 == "pole", 2 == "origin", otherwise "ignored". -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* If celestial axes were found which can be described using the -* conventions of FITS-WCS paper II, then a Mapping from the current Frame -* of the supplied FrameSet, to the IWC Frame is returned. Otherwise, -* a UnitMap is returned. Note, the Mapping only defines the IWC -* transformation for celestial axes. Any non-celestial axes are passed -* unchanged by the returned Mapping. -*/ - -/* Local Variables: */ - AstFitsTable *table; /* Pointer to structure holding -TAB table info */ - AstFrame *pframe; /* Primary Frame containing current WCS axis*/ - AstFrame *wcsfrm; /* WCS Frame within FrameSet */ - AstMapping *map0; /* Unsimplified Pixel -> WCS mapping */ - AstMapping *map1; /* Pointer to pre-WcsMap Mapping */ - AstMapping *map3; /* Pointer to post-WcsMap Mapping */ - AstMapping *map; /* Simplified Pixel -> WCS mapping */ - AstMapping *ret; /* Returned Mapping */ - AstMapping *tmap0; /* A temporary Mapping */ - AstMapping *tmap1; /* A temporary Mapping */ - AstMapping *tmap2; /* A temporary Mapping */ - AstMapping *tmap3; /* A temporary Mapping */ - AstMapping *tmap4; /* A temporary Mapping */ - AstSkyFrame *skyfrm; /* The SkyFrame defining current WCS axis */ - AstWcsMap *map2; /* Pointer to WcsMap */ - AstWcsMap *map2b; /* Pointer to WcsMap with cleared lat/lonpole */ - char *cval; /* Pointer to keyword value */ - char *temp; /* Pointer to temporary string */ - double *mat; /* Pointer to matrix diagonal elements */ - double *ppcfid; /* Pointer to array holding PPC at fiducial point */ - double con; /* Constant value for unassigned axes */ - double crval[ 2 ]; /* Psi coords of reference point */ - double pv; /* Projection parameter value */ - double skyfid[ 2 ]; /* Sky coords of fiducial point */ - double val; /* Keyword value */ - int *inperm; /* Input axis permutation array */ - int *outperm; /* Output axis permutation array */ - int *tperm; /* Pointer to new FITS axis numbering array */ - int axlat; /* Index of latitude output from WcsMap */ - int axlon; /* Index of longitude output from WcsMap */ - int extver; /* Table version number for -TAB headers */ - int fits_ilat; /* FITS WCS axis index for latitude axis */ - int fits_ilon; /* FITS WCS axis index for longitude axis */ - int i; /* Loop index */ - int iax; /* Axis index */ - int icolindexlat; /* Index of table column holding lat index vector */ - int icolindexlon; /* Index of table column holding lon index vector */ - int icolmainlat; /* Index of table column holding main lat coord array */ - int icolmainlon; /* Index of table column holding main lon coord array */ - int interplat; /* INterpolation method for latitude look-up tables */ - int interplon; /* INterpolation method for longitude look-up tables */ - int ilat; /* Index of latitude axis within total WCS Frame */ - int ilon; /* Index of longitude axis within total WCS Frame */ - int j; /* Loop index */ - int m; /* Projection parameter index */ - int maxm; /* Largest used "m" value */ - int mlat; /* Index of latitude axis in main lat coord array */ - int mlon; /* Index of longitude axis in main lon coord array */ - int nwcs; /* Number of WCS axes */ - int nwcsmap; /* Number of inputs/outputs for the WcsMap */ - int paxis; /* Axis index within primary Frame */ - int skylataxis; /* Index of latitude axis within SkyFrame */ - int skylonaxis; /* Index of longitude axis within SkyFrame */ - int tpn; /* Is the WCS projectiona TPN projection? */ - -/* Initialise */ - ret = NULL; - -/* Other initialisation to avoid compiler warnings. */ - mlon = 0; - mlat = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Get a pointer to the WCS Frame. */ - wcsfrm = astGetFrame( fs, AST__CURRENT ); - -/* Store the number of WCS axes. */ - nwcs = astGetNout( fs ); - -/* Check each axis in the WCS Frame to see if it is a celestial axis. */ - skyfrm = NULL; - map = NULL; - ilon = -1; - ilat = -1; - for( iax = 0; iax < nwcs; iax++ ) { - -/* Obtain a pointer to the primary Frame containing the current WCS axis. */ - astPrimaryFrame( wcsfrm, iax, &pframe, &paxis ); - -/* If the current axis belongs to a SkyFrame, we have found a celestial - axis. Keep a pointer to it, and note the indices of the celestial axes - within the complete WCS Frame. The MakeFitsFrameSet function will have - ensured that the WCS Frame only contains at most a single SkyFrame. */ - if( IsASkyFrame( pframe ) ) { - if( !skyfrm ) skyfrm = astClone( pframe ); - if( paxis == 0 ) { - ilon = iax; - } else { - ilat = iax; - } - -/* Indicate that this axis has been classified. */ - axis_done[ iax ] = 1; - } - -/* Release resources. */ - pframe = astAnnul( pframe ); - } - -/* Only proceed if we found celestial axes. */ - if( ilon != -1 && ilat != -1 ) { - -/* Note the FITS WCS axis indices for the longitude and latitude axes */ - fits_ilon = wperm[ ilon ]; - fits_ilat = wperm[ ilat ]; - -/* Create an array to hold the Projection Plane Coords corresponding to the - CRVALi keywords. */ - ppcfid = (double *) astMalloc( sizeof( double )*nwcs ); - -/* Get the pixel->wcs Mapping. */ - map0 = astGetMapping( fs, AST__BASE, AST__CURRENT ); - map = astSimplify( map0 ); - map0 = astAnnul( map0 ); - -/* Get the table version number to use if we end up using the -TAB - algorithm. This is the set value of the TabOK attribute (if positive). */ - extver = astGetTabOK( this ); - -/* Some of the required FITS Keyword values are defined by the WcsMap - contained within the Mapping. Split the mapping up into a list of serial - component mappings, and locate the first WcsMap in this list. The first - Mapping returned by this call is the result of compounding all the - Mappings up to (but not including) the WcsMap, the second returned Mapping - is the (inverted) WcsMap, and the third returned Mapping is anything - following the WcsMap. Only proceed if one and only one WcsMap is found. */ - if( SplitMap( map, astGetInvert( map ), ilon, ilat, &map1, &map2, &map3, - status ) ){ - -/* Get the indices of the latitude and longitude axes within the SkyFrame - (not necessarily (1,0) because they may have been permuted). */ - skylataxis = astGetLatAxis( skyfrm ); - skylonaxis = astGetLonAxis( skyfrm ); - -/* The reference point in the celestial coordinate system is found by - transforming the fiducial point in native spherical co-ordinates - into WCS coordinates using map3. */ - if( GetFiducialWCS( map2, map3, ilon, ilat, skyfid + skylonaxis, - skyfid + skylataxis, status ) ){ - -/* We also need to find the indices of the longitude and latitude outputs - from the WcsMap. These may not be the same as ilat and ilon because of - axis permutations in "map3". */ - axlon = astGetWcsAxis( map2, 0 ); - axlat = astGetWcsAxis( map2, 1 ); - -/* Normalise the latitude and longitude values at the fiducial point. The - longitude and latitude values found above will be in radians, but after - normalization we convert them to degrees, as expected by other functions - which handle FitsStores. */ - if( skyfid[ skylonaxis ] == AST__BAD ) skyfid[ skylonaxis ] = 0.0; - if( skyfid[ skylataxis ] == AST__BAD ) skyfid[ skylataxis ] = 0.0; - if( ZEROANG( skyfid[ 0 ] ) ) skyfid[ 0 ] = 0.0; - if( ZEROANG( skyfid[ 1 ] ) ) skyfid[ 1 ] = 0.0; - astNorm( skyfrm, skyfid ); - SetItem( &(store->crval), fits_ilon, 0, s, AST__DR2D*skyfid[ skylonaxis ], status ); - SetItem( &(store->crval), fits_ilat, 0, s, AST__DR2D*skyfid[ skylataxis ], status ); - -/* Set a flag if we have a TPN projection. This is an AST-specific - projection which mimicks the old "TAN with correction terms" projection - which was removed from the final version of the FITS-WCS paper II. */ - tpn = ( astGetWcsType( map2 ) == AST__TPN ); - -/* Store the WCS projection parameters. Except for TPN projections, always - exclude parameters 3 and 4 on the longitude axis since these are - reserved to hold copies of LONPOLE and LATPOLE. */ - for( m = 0; m < WCSLIB_MXPAR; m++ ){ - if( astTestPV( map2, axlon, m ) ) { - if( m < 3 || m > 4 || tpn ) { - pv = astGetPV( map2, axlon, m ); - if( pv != AST__BAD ) SetItem( &(store->pv), fits_ilon, m, - s, pv, status ); - } - } - if( astTestPV( map2, axlat, m ) ) { - pv = astGetPV( map2, axlat, m ); - if( pv != AST__BAD ) SetItem( &(store->pv), fits_ilat, m, - s, pv, status ); - } - } - -/* If PVi_0 (for the longitude axis) is non-zero, the Cartesian coordinates - used by the WcsMap (Projection Plane Coordinates, PPC) need to be shifted - to produce Intermediate World Coordinates (IWC). This shift results in - the pixel reference position specified by the CRPIXi values (and which - corresponds to the origin of IWC) mapping on to the fiducial position - specified by the CRVALi values. The required shifts are just the PPC - coordinates of the fiducial point. The AST-specific "TPN" projection uses - longitude projection parameters to define correction terms, and so cannot - use the above convention (which is part of FITS-WCS paper II). Therefore - TPN projections always use zero shift between PPC and IWC. */ - for( iax = 0; iax < nwcs; iax++ ) ppcfid[ iax ] = 0.0; - if( !tpn && astGetPV( map2, axlon, 0 ) != 0.0 ) { - GetFiducialPPC( (AstWcsMap *) map2, ppcfid + ilon, ppcfid + ilat, status ); - if( ppcfid[ ilon ] == AST__BAD ) ppcfid[ ilon ] = 0.0; - if( ppcfid[ ilat ] == AST__BAD ) ppcfid[ ilat ] = 0.0; - ppcfid[ ilon ] *= AST__DR2D; - ppcfid[ ilat ] *= AST__DR2D; - } - -/* Store the CTYPE, CNAME, EQUINOX, MJDOBS, and RADESYS values. */ - SkySys( this, skyfrm, 1, astGetWcsType( map2 ), store, fits_ilon, - fits_ilat, s, isoff, method, class, status ); - -/* Store the LONPOLE and LATPOLE values in the FitsStore. */ - SkyPole( map2, map3, ilon, ilat, wperm, s, store, method, class, status ); - -/* The values of LONPOLE and LATPOLE stored above (in the FitsStore) will be - ignored by WcsNative if the WcsMap contains set values for projection - parameters PVi_3a and/or PVi_4a (these will be used in preference to - the values in the FitsStore). To avoid this happening we take a copy - of the WcsMap and clear the relevant parameters (but not if the WcsMap is - for a TPN projection because TPN uses PVi_3a and PVi_4a for other - purposes). */ - if( astGetWcsType( map2 ) != AST__TPN ) { - map2b = astCopy( map2 ); - astClearPV( map2b, axlon, 3 ); - astClearPV( map2b, axlon, 4 ); - } else { - map2b = astClone( map2 ); - } - -/* We will now create the Mapping from WCS coords to IWC coords. In fact, - we produce the Mapping from IWC to WCS and then invert it. Create the - first component of this Mapping which implements any shift of origin - from IWC to PPC. */ - tmap0 = (AstMapping *) astShiftMap( nwcs, ppcfid, "", status ); - -/* The next component of this Mapping scales the PPC coords from degrees - to radians on the celestial axes. */ - mat = astMalloc( sizeof( double )*(size_t) nwcs ); - if( astOK ) { - for( iax = 0; iax < nwcs; iax++ ) mat[ iax ] = 1.0; - mat[ ilon ] = AST__DD2R; - mat[ ilat ] = AST__DD2R; - tmap1 = (AstMapping *) astMatrixMap( nwcs, nwcs, 1, mat, "", status ); - mat = astFree( mat ); - } else { - tmap1 = NULL; - } - -/* Now create the Mapping from Native Spherical Coords to WCS. */ - tmap2 = WcsNative( NULL, store, s, map2b, fits_ilon, fits_ilat, - method, class, status ); - -/* Combine the WcsMap with the above Mapping, to get the Mapping from PPC - to WCS. */ - tmap3 = (AstMapping *) astCmpMap( map2b, tmap2, 1, "", status ); - tmap2 = astAnnul( tmap2 ); - -/* If there are more WCS axes than IWC axes, create a UnitMap for the extra - WCS axes and add it in parallel with tmap3. */ - nwcsmap = astGetNin( map3 ); - if( nwcsmap < nwcs ) { - tmap2 = (AstMapping *) astUnitMap( nwcs - nwcsmap, "", status ); - tmap4 = (AstMapping *) astCmpMap( tmap3, tmap2, 0, "", status ); - tmap3 = astAnnul( tmap3 ); - tmap2 = astAnnul( tmap2 ); - tmap3 = tmap4; - nwcsmap = nwcs; - } - -/* The pixel->wcs mapping may include a PermMap which selects some sub-set - or super-set of the orignal WCS axes. In this case the number of inputs - and outputs for "tmap3" created above may not equal "nwcs". To avoid this, - we embed "tmap3" between 2 PermMaps which select the required axes. */ - if( nwcsmap != nwcs || ilon != axlon || ilat != axlat ) { - inperm = astMalloc( sizeof( int )*(size_t) nwcs ); - outperm = astMalloc( sizeof( int )*(size_t) nwcsmap ); - if( astOK ) { - -/* Indicate that no inputs of the PermMap have yet been assigned to any - outputs */ - for( i = 0; i < nwcs; i++ ) inperm[ i ] = -1; - -/* Assign the WcsMap long/lat axes to the WCS Frame long/lat axes */ - inperm[ ilon ] = axlon; - inperm[ ilat ] = axlat; - -/* Assign the remaining inputs arbitrarily (doesn't matter how we do this - since the WcsMap is effectively a UnitMap on all non-celestial axes). */ - iax = 0; - for( i = 0; i < nwcs; i++ ) { - while( iax == axlon || iax == axlat ) iax++; - if( inperm[ i ] == -1 ) inperm[ i ] = iax++; - } - -/* Do the same for the outputs. */ - for( i = 0; i < nwcsmap; i++ ) outperm[ i ] = -1; - outperm[ axlon ] = ilon; - outperm[ axlat ] = ilat; - iax = 0; - for( i = 0; i < nwcsmap; i++ ) { - while( iax == ilon || iax == ilat ) iax++; - if( outperm[ i ] == -1 ) outperm[ i ] = iax++; - } - -/* Create the PermMap. */ - con = AST__BAD; - tmap2 = (AstMapping *) astPermMap( nwcs, inperm, nwcsmap, - outperm, &con, "", status ); - -/* Sandwich the WcsMap between the PermMap and its inverse. */ - tmap4 = (AstMapping *) astCmpMap( tmap2, tmap3, 1, "", status ); - tmap3 = astAnnul( tmap3 ); - astInvert( tmap2 ); - tmap3 = (AstMapping *) astCmpMap( tmap4, tmap2, 1, "", status ); - tmap2 = astAnnul( tmap2 ); - tmap4 = astAnnul( tmap4 ); - } - inperm = astFree( inperm ); - outperm = astFree( outperm ); - } - -/* Combine these Mappings together. */ - tmap4 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, "", status ); - tmap0 = astAnnul( tmap0 ); - tmap1 = astAnnul( tmap1 ); - ret = (AstMapping *) astCmpMap( tmap4, tmap3, 1, "", status ); - tmap3 = astAnnul( tmap3 ); - tmap4 = astAnnul( tmap4 ); - -/* Invert this Mapping to get the Mapping from WCS to IWC. */ - astInvert( ret ); - -/* The spherical rotation involved in converting WCS to IWC can result in - inappropriate numbering of the FITS axes. For instance, a LONPOLE - value of 90 degrees causes the IWC axes to be transposed. For this - reason we re-asses the FITS axis numbers assigned to the celestial - axes in order to make the IWC axes as close as possible to the pixel - axes with the same number (but only if the axis order is being - determined automatically). To do this, we need the Mapping from - pixel to IWC, which is formed by concatenating the pixel->WCS - Mapping with the WCS->IWC Mapping. */ - if( astChrMatch( astGetFitsAxisOrder( this ), "<auto>" ) ) { - tmap0 = (AstMapping *) astCmpMap( map, ret, 1, "", status ); - -/* Find the outputs of this Mapping which should be associated with each - input. */ - tperm = astMalloc( sizeof(int)*(size_t) nwcs ); - if( ! WorldAxes( this, tmap0, dim, tperm, status ) ) { - ret = astAnnul( ret ); - } - -/* If the index associated with the celestial axes appear to have been - swapped... */ - if( ret && astOK && fits_ilon == tperm[ ilat ] && - fits_ilat == tperm[ ilon ] ) { - -/* Swap the fits axis indices associated with each WCS axis to match. */ - wperm[ ilon ] = fits_ilat; - wperm[ ilat ] = fits_ilon; - -/* Swap the stored CRVAL value for the longitude and latitude axis. */ - val = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status ); - SetItem( &(store->crval), fits_ilat, 0, s, - GetItem( &(store->crval), fits_ilon, 0, s, NULL, - method, class, status ), status ); - SetItem( &(store->crval), fits_ilon, 0, s, val, status ); - -/* Swap the stored CTYPE value for the longitude and latitude axis. */ - cval = GetItemC( &(store->ctype), fits_ilat, 0, s, NULL, method, class, status ); - if( cval ) { - temp = astStore( NULL, (void *) cval, strlen( cval ) + 1 ); - cval = GetItemC( &(store->ctype), fits_ilon, 0, s, NULL, method, class, status ); - if( cval ) { - SetItemC( &(store->ctype), fits_ilat, 0, s, cval, status ); - SetItemC( &(store->ctype), fits_ilon, 0, s, temp, status ); - } - temp = astFree( temp ); - } - -/* Swap the stored CNAME value for the longitude and latitude axis. */ - cval = GetItemC( &(store->cname), fits_ilat, 0, s, NULL, method, class, status ); - if( cval ) { - temp = astStore( NULL, (void *) cval, strlen( cval ) + 1 ); - cval = GetItemC( &(store->cname), fits_ilon, 0, s, NULL, method, class, status ); - if( cval ) { - SetItemC( &(store->cname), fits_ilat, 0, s, cval, status ); - SetItemC( &(store->cname), fits_ilon, 0, s, temp, status ); - } - temp = astFree( temp ); - } - -/* Swap the projection parameters asociated with the longitude and latitude - axes. */ - maxm = GetMaxJM( &(store->pv), s, status ); - for( m = 0; m <= maxm; m++ ){ - val = GetItem( &(store->pv), fits_ilat, m, s, NULL, method, class, status ); - SetItem( &(store->pv), fits_ilat, m, s, - GetItem( &(store->pv), fits_ilon, m, s, NULL, - method, class, status ), status ); - SetItem( &(store->pv), fits_ilon, m, s, val, status ); - } - } - -/* Release resources. */ - tperm = astFree( tperm ); - tmap0 = astAnnul( tmap0 ); - } - map2b = astAnnul( map2b ); - } - -/* Release resources. */ - map1 = astAnnul( map1 ); - map2 = astAnnul( map2 ); - map3 = astAnnul( map3 ); - -/* If no WcsMap was found in the pixel->WCS Mapping, it may be possible - to describe the celestial axes using a tabular look-up table (i.e. the - FITS-WCS "_TAB" algorithm). Only do this if the -TAB algorithm is to - be supported. */ - } else if( extver > 0 ) { - -/* Get any pre-existing FitsTable from the FitsStore. This is the table - in which the tabular data will be stored (if the Mapping can be expressed - in -TAB form). */ - if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL; - -/* See if the transformations for the celestial axes can be expressed in -TAB - form. The returned Mapping (if any) is the Mapping from (lon,lat) - (rads) to (psi_lon,psi_lat) (pixels). See FITS-WCS paper III section 6.1.2 - for definition of psi. Scale the values stored in the table from radians - to degrees. */ - tmap0 = IsMapTab2D( map, AST__DR2D, "deg", wcsfrm, dim, ilon, ilat, - fits_ilon, fits_ilat, &table, &icolmainlon, - &icolmainlat, &icolindexlon, &icolindexlat, - &mlon, &mlat, &interplon, &interplat, status ); - if( tmap0 ) { - -/* Store the CTYPE, CNAME, EQUINOX, MJDOBS, and RADESYS values. */ - SkySys( this, skyfrm, 0, 0, store, fits_ilon, fits_ilat, s, isoff, - method, class, status ); - -/* If possible, choose the two CRVAL values (which are values on the psi - axes) so that transforming them using the Mapping returned by - IsMapTab2D gives the sky reference position stored in the SkyFrame. - Check the SkyFrame has a defined reference position. */ - if( astTestSkyRef( skyfrm, 0 ) && astTestSkyRef( skyfrm, 1 ) ){ - -/* Get the longitude and latitude at the reference point in radians. */ - skyfid[ 0 ] = astGetSkyRef( skyfrm, astGetLonAxis( skyfrm )); - skyfid[ 1 ] = astGetSkyRef( skyfrm, astGetLatAxis( skyfrm )); - -/* We use the WCS->psi Mapping to convert the reference point WCS coords - (rads) into psi coords (pixels). We can only do this if the WCS->psi - Mapping has a defined forward transformation. */ - if( astGetTranForward( tmap0 ) ) { - astTran2( tmap0, 1, skyfid, skyfid + 1, 1, crval, - crval + 1 ); - -/* If the WCS->psi mapping has an undefined forward transformation, then - just store the sky reference point coords (in degs) in keywords - AXREFn, and use 1.0 for the CRVAL values, so that IWC becomes equal - to (psi-1) i.e. (grid coords - 1). This means the reference point is - at grid coords (1.0,1.0). Note this choice of 1.0 for CRVAL is not - arbitrary since it is required by the trick used to create invertable CD - matrix in function MakeInvertable. */ - } else { - SetItem( &(store->axref), fits_ilon, 0, s, - AST__DR2D*skyfid[ 0 ], status ); - SetItem( &(store->axref), fits_ilat, 0, s, - AST__DR2D*skyfid[ 1 ], status ); - crval[ 0 ] = 1.0; - crval[ 1 ] = 1.0; - } - -/* If the SkyFrame has no reference position, use 1.0 for the CRVAL values. */ - } else { - crval[ 0 ] = 1.0; - crval[ 1 ] = 1.0; - } - -/* Create a Mapping that describes the transformation from the lon and lat - psi axes to the lon and lat IWC axes (i.e. a ShiftMap that just subtracts - the CRVAL values from each axis). */ - crval[ 0 ] = -crval[ 0 ]; - crval[ 1 ] = -crval[ 1 ]; - tmap1 = (AstMapping *) astShiftMap( 2, crval, " ", status ); - crval[ 0 ] = -crval[ 0 ]; - crval[ 1 ] = -crval[ 1 ]; - -/* Create a series compound Mapping that applies the Mapping returned - by IsMapTab2D first (the Mapping from WCS to psi), followed by the - Mapping from psi to IWC created above. There-after, use this compound - Mapping in place of the Mapping returned by IsMapTab2D. It maps WCS to - IWC. */ - tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, " ", status ); - (void) astAnnul( tmap0 ); - tmap1 = astAnnul( tmap1 ); - tmap0 = tmap2; - -/* Store the CRVAL values */ - SetItem( &(store->crval), fits_ilon, 0, s, crval[ 0 ], status ); - SetItem( &(store->crval), fits_ilat, 0, s, crval[ 1 ], status ); - -/* Store TAB-specific values in the FitsStore. First the name of the - FITS binary table extension holding the coordinate info. */ - SetItemC( &(store->ps), fits_ilon, 0, s, AST_TABEXTNAME, status ); - SetItemC( &(store->ps), fits_ilat, 0, s, AST_TABEXTNAME, status ); - -/* Next the table version number. This is the set (positive) value for the - TabOK attribute. */ - SetItem( &(store->pv), fits_ilon, 1, s, extver, status ); - SetItem( &(store->pv), fits_ilat, 1, s, extver, status ); - -/* Also store the table version in the binary table header. */ - astSetFitsI( table->header, "EXTVER", extver, "Table version number", - 0 ); - -/* Next the name of the table column containing the main coords array. */ - SetItemC( &(store->ps), fits_ilon, 1, s, - astColumnName( table, icolmainlon ), status ); - SetItemC( &(store->ps), fits_ilat, 1, s, - astColumnName( table, icolmainlat ), status ); - -/* Next the name of the column containing the index array. */ - if( icolindexlon >= 0 ) SetItemC( &(store->ps), fits_ilon, 2, s, - astColumnName( table, icolindexlon ), status ); - if( icolindexlat >= 0 ) SetItemC( &(store->ps), fits_ilat, 2, s, - astColumnName( table, icolindexlat ), status ); - -/* The one-based index of the axes within the coordinate array that - describes FITS WCS axes "fits_ilon" and "fits_ilat". */ - SetItem( &(store->pv), fits_ilon, 3, s, mlon, status ); - SetItem( &(store->pv), fits_ilat, 3, s, mlat, status ); - -/* The interpolation method (an AST extension to the published -TAB - algorithm, communicated through the QVi_4a keyword). */ - SetItem( &(store->pv), fits_ilon, 4, s, interplon, status ); - SetItem( &(store->pv), fits_ilat, 4, s, interplat, status ); - -/* Also store the FitsTable itself in the FitsStore. */ - astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL ); - -/* Allocate space for the arrays that define the permutations required - for the inputs and outputs of a PermMap. */ - inperm = astMalloc( sizeof( double )*nwcs ); - outperm = astMalloc( sizeof( double )*nwcs ); - if( astOK ) { - -/* Create the WCS -> IWC Mapping. First create a parallel CmpMap that - combines the Mapping returned by IsMapTab2D (which transforms the celestial - axes), with a UnitMap which transforms the non-celestial axes. */ - if( nwcs > 2 ) { - tmap1 = (AstMapping *) astUnitMap( nwcs - 2, " ", status ); - tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 0, " ", status ); - tmap1 = astAnnul( tmap1 ); - } else { - tmap2 = astClone( tmap0 ); - } - -/* Now create a PermMap that permutes the inputs of this CmpMap into the - order of the axes in the WCS Frame. */ - outperm[ 0 ] = ilon; - outperm[ 1 ] = ilat; - j = 0; - for( i = 2; i < nwcs; i++ ) { - while( j == ilon || j == ilat ) j++; - outperm[ i ] = j++; - } - for( i = 0; i < nwcs; i++ ) inperm[ outperm[ i ] ] = i; - tmap1 = (AstMapping *) astPermMap( nwcs, inperm, nwcs, outperm, - NULL, " ", status ); - -/* Use this PermMap (and its inverse) to permute the inputs (and outputs) - of the parallel CmpMap created above. */ - tmap3 = (AstMapping *) astCmpMap( tmap1, tmap2, 1, " ", status ); - tmap2 = astAnnul( tmap2 ); - astInvert( tmap1 ); - tmap2 = (AstMapping *) astCmpMap( tmap3, tmap1, 1, " ", status ); - tmap1 = astAnnul( tmap1 ); - tmap3 = astAnnul( tmap3 ); - -/* Now create a PermMap that permutes the WCS axes into the FITS axis order. */ - for( i = 0; i < nwcs; i++ ) { - inperm[ i ] = wperm[ i ]; - outperm[ wperm[ i ] ] = i; - } - tmap1 = (AstMapping *) astPermMap( nwcs, inperm, nwcs, outperm, - NULL, "", status ); - -/* Use this PermMap to permute the outputs of the "tmap2" Mapping. The - resulting Mapping is the Mapping from the current Frame to IWC and is - the Mapping to be returned as the function value. */ - ret = (AstMapping *) astCmpMap( tmap2, tmap1, 1, " ", status ); - tmap1 = astAnnul( tmap1 ); - tmap2 = astAnnul( tmap2 ); - } - -/* Free remaining resources. */ - inperm = astFree( inperm ); - outperm = astFree( outperm ); - tmap0 = astAnnul( tmap0 ); - } - if( table ) table = astAnnul( table ); - } - -/* Release resources. */ - ppcfid = astFree( ppcfid ); - } - -/* Release resources. */ - wcsfrm = astAnnul( wcsfrm ); - if( skyfrm ) skyfrm = astAnnul( skyfrm ); - if( map ) map = astAnnul( map ); - -/* If we have a Mapping to return, simplify it. Otherwise, create - a UnitMap to return. */ - if( ret ) { - tmap0 = ret; - ret = astSimplify( tmap0 ); - tmap0 = astAnnul( tmap0 ); - } else { - ret = (AstMapping *) astUnitMap( nwcs, "", status ); - } - -/* Return the result. */ - return ret; -} - -static void ChangePermSplit( AstMapping *map, int *status ){ -/* -* Name: -* ChangePermSplit - -* Purpose: -* Change all PermMaps in a Mapping to use the alternate -* implementation of the astMapSplit method. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void ChangePermSplit( AstMapping *map, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The PemMap class provides two implementations of the astMapSplit -* method. The implementation used by each PermMap is determined by -* the value of the PermMap's "PermSplit" attribute. This function -* searches the supplied Mapping for any PermMaps, and set their -* PermSplit attribute to 1, indicating that the alternate -* implementation of astMapSplit should be used. - -* Parameters: -* map -* Pointer to the Mapping. Modified on exit by setting all -* PermSplit attributes to 1. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - AstMapping *map1; - AstMapping *map2; - int series; - int invert1; - int invert2; - -/* Check inherited status */ - if( !astOK ) return; - -/* If the supplied Mapping is a PermMap, set its PermSplit attribute - non-zero. */ - if( astIsAPermMap( map ) ) { - astSetPermSplit( map, 1 ); - -/* If the supplied Mapping is not a PermMap, attempt to decompose the - Mapping into two component Mappings. */ - } else { - astDecompose( map, &map1, &map2, &series, &invert1, &invert2 ); - -/* If the Mapping could be decomposed, use this function recursively to - set the PermSplit attributes in each component Mapping. */ - if( map1 && map2 ) { - ChangePermSplit( map1, status ); - ChangePermSplit( map2, status ); - -/* Annul the component Mappings. */ - map1 = astAnnul( map1 ); - map2 = astAnnul( map2 ); - } else if( map1 ) { - map1 = astAnnul( map1 ); - } else if( map2 ) { - map2 = astAnnul( map2 ); - } - } -} - -static double *Cheb2Poly( double *c, int nx, int ny, double xmin, double xmax, - double ymin, double ymax, int *status ){ -/* -* Name: -* Cheb2Poly - -* Purpose: -* Converts a two-dimensional Chebyshev polynomial to standard form and -* scale the arguments. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* double *Cheb2Poly( double *c, int nx, int ny, double xmin, double xmax, -* double ymin, double ymax, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* Given the coefficients of a two-dimensional Chebychev polynomial P(u,v), -* find the coefficients of the equivalent standard two-dimensional -* polynomial Q(x,y). The allowed range of u and v is assumed to be the -* unit square, and this maps on to the rectangle in (x,y) given by -* (xmin:xmax,ymin:ymax). - -* Parameters: -* c -* An array of (nx,ny) elements supplied holding the coefficients of -* P, such that the coefficient of (Ti(u)*Tj(v)) is held in element -* (i + j*nx), where "Ti(u)" is the Chebychev polynomial (of the -* first kind) of order "i" evaluated at "u", and "Tj(v)" is the -* Chebychev polynomial of order "j" evaluated at "v". -* nx -* One more than the maximum power of u within P. -* ny -* One more than the maximum power of v within P. -* xmin -* X value corresponding to u = -1 -* xmax -* X value corresponding to u = +1 -* ymin -* Y value corresponding to v = -1 -* ymax -* Y value corresponding to v = +1 -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Pointer to a dynamically allocated array of (nx,ny) elements holding -* the coefficients of Q, such that the coefficient of (x^i*y^j) is held -* in element (i + j*nx). Free it using astFree when no longer needed. -*/ - -/* Local Variables: */ - double *d; - double *pa; - double *pw; - double *work1; - double *work2; - double *work3; - int *iw1; - int *iw2; - int i; - int j; - -/* Check the status and supplied value pointer. */ - if( !astOK ) return NULL; - -/* Allocate returned array. */ - d = astMalloc( sizeof( *d )*nx*ny ); - -/* Allocate workspace. */ - work1 = astMalloc( sizeof( *work1 )*ny ); - work2 = astMalloc( sizeof( *work2 )*ny ); - work3 = astMalloc( sizeof( *work2 )*nx ); - iw1 = astMalloc( sizeof(int)*( nx > ny ? nx : ny ) ); - iw2 = astMalloc( sizeof(int)*( nx > ny ? nx : ny ) ); - if( astOK ) { - -/* Thinking of P as a 1D polynomial in v, each coefficient would itself then - be a 1D polynomial in u: - - P = ( c[0] + c[1]*T1(u) + c[2]*T2(u) + ... ) + - ( c[nx] + c[nx+1]*T1(u) + c[nx+2]*T2(u) + ... )*T1(v) + - (c[2*nx] + c[2*nx+1]*T1(u) + c[2*nx+2]*T2(u) + ... )*T2(v) + - ... - (c[(ny-1)*nx] + c[(ny-1)*nx+1]*T1(u) + c[(ny-1)*nx+2]*T2(u) + ... )T{ny-1}(v) - - Use Chpc1 to convert these "polynomial coefficients" to standard - form, storing the result in the corresponding row of "d" . Also, - convert them from u to x. */ - - for( j = 0; j < ny; j++ ) { - Chpc1( c + j*nx, work3, nx, iw1, iw2, status ); - Shpc1( xmin, xmax, nx, work3, d + j*nx, status ); - } - -/* The polynomial value is now: - - ( d[0] + d[1]*x + d[2]*x*x + ... ) + - ( d[nx] + d[nx+1]*x + d[nx+2]*x*x + ... )*T1(v) + - (d[2*nx] + d[2*nx+1]*x + d[2*nx+2]*x*x + ... )*T2(v) + - ... - (d[(ny-1)*nx] + d[(ny-1)*nx+1]*x + d[(ny-1)*nx+2]*x*x + ... )*T{ny-1}(v) - - If we rearrange this expression to view it as a 1D polynomial in x, - rather than v, each coefficient of the new 1D polynomial is then - itself a polynomial in v: - - ( d[0] + d[nx]*T1(v) + d[2*nx]*T2(v) + ... d[(ny-1)*nx]*T{ny-1}(v) ) + - ( d[1] + d[nx+1]*T1(v) + d[2*nx+1]*T2(v) + ... d[(ny-1)*nx+1]T{ny-1}(v)... )*x + - ( d[2] + d[nx+2]*T1(v) + d[2*nx+2]*T2(v) + ... d[(ny-1)*nx+2]T{ny-1}(v)... )*x*x + - ... - ( d[nx-1] + d[2*nx-1]*T1(v) + d[3*nx-1]*T2(v) + ... d[ny*nx-1]*T{ny-1}(v) )*x*x*... - - - Now use Chpc1 to convert each of these "polynomial coefficients" - to standard form. We copy each column of the d array into a 1D work array, - use Shpc1 to modify the values in the work array, and then write - the modified values back into the current column of d. Also convert - from v to y. */ - - for( i = 0; i < nx; i++ ) { - pa = d + i; - pw = work1; - for( j = 0; j < ny; j++ ) { - *(pw++) = *pa; - pa += nx; - } - - Chpc1( work1, work2, ny, iw1, iw2, status ); - Shpc1( ymin, ymax, ny, work2, work1, status ); - - pa = d + i; - pw = work1; - for( j = 0; j < ny; j++ ) { - *pa = *(pw++); - pa += nx; - } - } - -/* So the polynomial is now: - - ( d[0] + d[nx]*y + d[2*nx]*y*y + ... d[(ny-1)*nx]*y*y*... ) + - ( d[1] + d[nx+1]*y + d[2*nx+1]*y*y + ... d[(ny-1)*nx+1]*y*y*... )*x + - ( d[2] + d[nx+2]*y + d[2*nx+2]*y*y + ... d[(ny-1)*nx+2]*y*y*... )*x*x + - ... - ( d[nx-1] + d[2*nx-1]*y + d[3*nx-1]*y*y + ... d[ny*nx-1]*y*y*... )*x*x*... - - Re-arranging, this is: - - ( d[0] + d[1]*x + d[2]*x*x + ... ) + - ( d[nx] + d[nx+1]*x + d[nx+2]*x*x + ... )*y + - (d[2*nx] + d[2*nx+1]*x + d[2*nx+2]*x*x + ... )*y*y + - ... - (d[(ny-1)*nx] + d[(ny-1)*nx+1]*x + d[(ny-1)*nx+2]*x*x + ... )*y*y*... - - as required. */ - - } - -/* Free the workspace. */ - work1 = astFree( work1 ); - work2 = astFree( work2 ); - work3 = astFree( work3 ); - iw1 = astFree( iw1 ); - iw2 = astFree( iw2 ); - -/* Return the result. */ - return d; -} - -static int CheckFitsName( AstFitsChan *this, const char *name, - const char *method, const char *class, int *status ){ -/* -* Name: -* CheckFitsName - -* Purpose: -* Check a keyword name conforms to FITS standards. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int CheckFitsName( AstFitsChan *this, const char *name, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* FITS keywords must contain between 1 and 8 characters, and each -* character must be an upper-case Latin alphabetic character, a digit, -* an underscore, or a hyphen. Leading, trailing or embedded white space -* is not allowed, with the exception of totally blank or null keyword -* names. -* -* If the supplied keyword name is invalid, either a warning is issued -* (for violations that can be handled - such as illegal characters in -* keywords), or an error is reported (for more major violations such -* as the keyname containing an equals sign). - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a string holding the name to check. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 0 is returned if the supplied name was blank. A value of 1 -* is returned otherwise. - -* Notes: -* - An error is reported if the supplied keyword name does not -* conform to FITS requirements, and zero is returned. -*/ - -/* Local Variables: */ - char buf[100]; /* Buffer for warning text */ - const char *c; /* Pointer to next character in name */ - size_t n; /* No. of characters in supplied name */ - int ret; /* Returned value */ - -/* Check the global status. */ - if( !astOK ) return 0; - -/* Initialise the returned value to indicate that the supplied name was - blank. */ - ret = 0; - -/* Check that the supplied pointer is not NULL. */ - if( name ){ - -/* Get the number of characters in the name. */ - n = strlen( name ); - -/* Report an error if the name has too many characters in it. */ - if( n > FITSNAMLEN ){ - astError( AST__BDFTS, "%s(%s): The supplied FITS keyword name ('%s') " - "has %d characters. FITS only allows up to %d.", status, method, - class, name, (int) n, FITSNAMLEN ); - -/* If the name has no characters in it, then assume it is a legal blank - keyword name. Otherwise, check that no illegal characters occur in the - name. */ - } else if( n != 0 ) { - -/* Whitespace is only allowed in the special case of a name consisting - entirely of whitespace. Such keywords are used to indicate that the rest - of the card is a comment. Find the first non-whitespace character in the - name. */ - c = name; - while( isspace( ( int ) *(c++) ) ); - -/* If the name is filled entirely with whitespace, then the name is acceptable - as the special case. Otherwise, we need to do more checks. */ - if( c - name - 1 < n ){ - -/* Indicate that the supplied name is not blank. */ - ret = 1; - -/* Loop round every character checking that it is one of the legal characters. - Report an error if any illegal characters are found. */ - c = name; - while( *c ){ - if( !isFits( (int) *c ) ){ - if( *c == '=' ){ - astError( AST__BDFTS, "%s(%s): An equals sign ('=') was found " - "before column %d within a FITS keyword name or header " - "card.", status, method, class, FITSNAMLEN + 1 ); - - } else if( *c < ' ' ) { - sprintf( buf, "The FITS keyword name ('%s') contains an " - "illegal non-printing character (ascii value " - "%d).", name, *c ); - Warn( this, "badkeyname", buf, method, class, status ); - - - } else if( *c > ' ' ) { - sprintf( buf, "The FITS keyword name ('%s') contains an " - "illegal character ('%c').", name, *c ); - Warn( this, "badkeyname", buf, method, class, status ); - } - break; - } - c++; - } - } - } - -/* Report an error if no pointer was supplied. */ - } else if( astOK ){ - astError( AST__INTER, "CheckFitsName(%s): AST internal error; a NULL " - "pointer was supplied for the keyword name. ", status, - astGetClass( this ) ); - } - -/* If an error has occurred, return 0. */ - if( !astOK ) ret = 0; - -/* Return the answer. */ - return ret; -} - -static void CheckZero( char *text, double value, int width, int *status ){ -/* -* Name: -* CheckZero - -* Purpose: -* Ensure that the formatted value zero has no minus sign. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void CheckZero( char *text, double value, int width, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* There is sometimes a problem (perhaps only on DEC UNIX) when formatting -* the floating-point value 0.0 using C. Sometimes it gives the string -* "-0". This function fixed this by checking the first character of -* the supplied string (if the supplied value is zero), and shunting the -* remaining text one character to the right if it is a minus sign. It -* returns without action if the supplied value is not zero. -* -* In addition, this function also rounds out long sequences of -* adjacent zeros or nines in the number. - -* Parameters: -* text -* The formatted value. -* value -* The floating value which was formatted. -* width -* The minimum field width to use. The value is right justified in -* this field width. Ignored if zero. -* status -* Pointer to the inherited status variable. - -* Notes: -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - char *c; - -/* Return if no text was supplied. */ - if( !text ) return; - -/* If the numerical value is zero, check for the leading minus sign. */ - if( value == 0.0 ) { - -/* Find the first non-space character. */ - c = text; - while( *c && isspace( (int) *c ) ) c++; - -/* If the first non-space character is a minus sign, replace it with a - space. */ - if( *c == '-' ) *c = ' '; - -/* Otherwise, round out sequences of zeros or nines. */ - } else { - RoundFString( text, width, status ); - } -} - -static double ChooseEpoch( AstFitsChan *this, FitsStore *store, char s, - const char *method, const char *class, int *status ){ -/* -* Name: -* ChooseEpoch - -* Purpose: -* Choose a FITS keyword value to use for the AST Epoch attribute. - -* Type: -* Private function. - -* Synopsis: -* double ChooseEpoch( AstFitsChan *this, FitsStore *store, char s, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function returns an MJD value in the TDB timescale, which can -* be used as the Epoch value in an AST Frame. It uses the following -* preference order: secondary MJD-AVG, primary MJD-AVG, secondary MJD-OBS, -* primary MJD-OBS. Note, DATE-OBS keywords are converted into MJD-OBS -* keywords by the SpecTrans function before this function is called. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* A structure containing values for FITS keywords relating to -* the World Coordinate System. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* method -* The calling method. Used only in error messages. -* class -* The object class. Used only in error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The MJD value. - -* Notes: -* - A value of AST__BAD is returned if an error occurs, or if none -* of the required keywords can be found in the FitsChan. -*/ - -/* Local Variables: */ - const char *timesys; /* The TIMESYS value in the FitsStore */ - double mjd; /* The returned MJD */ - -/* Initialise the returned value. */ - mjd = AST__BAD; - -/* Check the global status. */ - if( !astOK ) return mjd; - -/* Otherwise, try to get the secondary MJD-AVG value. */ - mjd = GetItem( &(store->mjdavg), 0, 0, s, NULL, method, class, status ); - -/* Otherwise, try to get the primary MJD-AVG value. */ - if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdavg), 0, 0, ' ', NULL, - method, class, status ); - -/* If the secondary MJD-OBS keyword is present in the FitsChan, gets its - value. */ - if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdobs), 0, 0, s, NULL, - method, class, status ); - -/* Otherwise, try to get the primary MJD-OBS value. */ - if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, - method, class, status ); - -/* Now convert the MJD value to the TDB timescale. */ - timesys = GetItemC( &(store->timesys), 0, 0, ' ', NULL, method, class, status ); - mjd = TDBConv( mjd, TimeSysToAst( this, timesys, method, class, status ), - 0, method, class, status ); - -/* Return the answer. */ - return mjd; -} - -static void Chpc1( double *c, double *d, int n, int *w0, int *w1, int *status ){ -/* -* Name: -* Chpc1 - -* Purpose: -* Converts a one-dimensional Chebyshev polynomial to standard form. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void Chpc1( double *c, double *d, int n, int *w0, int *w1, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* Given the coefficients of a one-dimensional Chebychev polynomial P(u), -* find the coefficients of the equivalent standard 1D polynomial Q(u). -* The allowed range of u is assumed to be the unit interval. - -* Parameters: -* c -* An array of n elements supplied holding the coefficients of -* P, such that the coefficient of (Ti(u)) is held in element -* (i), where "Ti(u)" is the Chebychev polynomial (of the -* first kind) of order "i" evaluated at "u". -* d -* An array of n elements returned holding the coefficients of -* Q, such that the coefficient of (u^i) is held in element (i). -* n -* One more than the highest power of u in P. -* w0 -* Pointer to a work array of n elements. -* w1 -* Pointer to a work array of n elements. -* status -* Inherited status value - -* Notes: -* - Vaguely inspired by the Numerical Recipes routine "chebpc". But the -* original had bugs, so I wrote this new version from first principles. - -*/ - -/* Local Variables: */ - int sv; - int j; - int k; - -/* Check inherited status */ - if( !astOK ) return; - -/* Initialise the returned coefficients array. */ - for( j = 0; j < n; j++ ) d[ j ] = 0.0; - -/* Use the recurrence relation - - T{k+1}(x) = 2.x.T{k}(x) - T{k-1}(x). - - w0[i] holds the coefficient of x^i in T{k-1}. w1[i] holds the - coefficient of x^i in T{k}. Initialise them for T0 (="1") and - T1 (="x"). */ - for( j = 0; j < n; j++ ) w0[ j ] = w1[ j ] = 0; - w0[ 0 ] = 1; - w1[ 1 ] = 1; - -/* Update the returned coefficients array to include the T0 and T1 terms. */ - d[ 0 ] = c[ 0 ]; - d[ 1 ] = c[ 1 ]; - -/* Loop round using the above recurrence relation until we have found - T{n-1}. */ - for( k = 1; k < n - 1; k++ ){ - -/* To get the coefficients of T{k+1} shift the contents of w1 up one - element, introducing a zero at the low end, and then double all the - values in w1. Finally subtract off the values in w0. This implements - the above recurrence relationship. Starting at the top end and working - down to the bottom, store a new value for each element of w1. */ - for( j = n - 1; j > 0; j-- ) { - -/* First save the original element of w1 in w0 for use next time. But we - also need the original w0 element later on so save it first. */ - sv = w0[ j ]; - w0[ j ] = w1[ j ]; - -/* Double the lower neighbouring w1 element and subtract off the w0 - element saved above. This forms the new value for w1. */ - w1[ j ] = 2*w1[ j - 1 ] - sv; - } - -/* Introduce a zero into the lowest element of w1, saving the original - value first in w0. Then subtract off the original value of w0. */ - sv = w0[ 0 ]; - w0[ 0 ] = w1[ 0 ]; - w1[ 0 ] = -sv; - -/* W1 now contains the coefficients of T{k+1} in w1, and the coefficients - of T{k} in w0. Multiply these by the supplied coefficient for T{k+1}, - and add them into the returned array. */ - for( j = 0; j <= k + 1; j++ ){ - d[ j ] += c[ k + 1 ]*w1[ j ]; - } - } -} - -static int ChrLen( const char *string, int *status ){ -/* -* Name: -* ChrLen - -* Purpose: -* Return the length of a string excluding any trailing white space. - -* Type: -* Private function. - -* Synopsis: -* int ChrLen( const char *string, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function returns the length of a string excluding any trailing -* white space, or non-printable characters. - -* Parameters: -* string -* Pointer to the string. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The length of a string excluding any trailing white space and -* non-printable characters. - -* Notes: -* - A value of zero is returned if a NULL pointer is supplied, or if an -* error has already occurred. -*/ - -/* Local Variables: */ - const char *c; /* Pointer to the next character to check */ - int ret; /* The returned string length */ - -/* Check the global status. */ - if( !astOK ) return 0; - -/* Initialise the returned string length. */ - ret = 0; - -/* Check a string has been supplied. */ - if( string ){ - -/* Check each character in turn, starting with the last one. */ - ret = strlen( string ); - c = string + ret - 1; - while( ret ){ - if( isprint( (int) *c ) && !isspace( (int) *c ) ) break; - c--; - ret--; - } - } - -/* Return the answer. */ - return ret; -} - -static int CLASSFromStore( AstFitsChan *this, FitsStore *store, - AstFrameSet *fs, double *dim, const char *method, - const char *class, int *status ){ - -/* -* Name: -* CLASSFromStore - -* Purpose: -* Store WCS keywords in a FitsChan using FITS-CLASS encoding. - -* Type: -* Private function. - -* Synopsis: - -* int CLASSFromStore( AstFitsChan *this, FitsStore *store, -* AstFrameSet *fs, double *dim, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function copies the WCS information stored in the supplied -* FitsStore into the supplied FitsChan, using FITS-CLASS encoding. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore. -* fs -* Pointer to the FrameSet from which the values in the FitsStore -* were derived. -* dim -* Pointer to an array holding the main array dimensions (AST__BAD -* if a dimension is not known). -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if succesfull, and zero is returned -* otherwise. -*/ - -/* Local Variables: */ - AstFrame *azelfrm; /* (az,el) frame */ - AstFrame *curfrm; /* Current Frame in supplied FrameSet */ - AstFrame *freqfrm; /* Frame for reference frequency value */ - AstFrame *radecfrm; /* Spatial frame for CRVAL values */ - AstFrame *velofrm; /* Frame for reference velocity value */ - AstFrameSet *fsconv1;/* FrameSet connecting "curfrm" & "radecfrm" */ - AstFrameSet *fsconv2;/* FrameSet connecting "curfrm" & "azelfrm" */ - AstMapping *map1; /* Axis permutation to get (lonaxis,lataxis) = (0,1) */ - AstMapping *map2; /* Mapping from FITS CTYPE to (az,el) */ - AstMapping *map3; /* Mapping from (lon,lat) to (az,el) */ - char *comm; /* Pointer to comment string */ - char *cval; /* Pointer to string keyword value */ - char attbuf[20]; /* Buffer for AST attribute name */ - char combuf[80]; /* Buffer for FITS card comment */ - char lattype[MXCTYPELEN];/* Latitude axis CTYPE */ - char lontype[MXCTYPELEN];/* Longitude axis CTYPE */ - char s; /* Co-ordinate version character */ - char sign[2]; /* Fraction's sign character */ - char spectype[MXCTYPELEN];/* Spectral axis CTYPE */ - double *cdelt; /* Pointer to CDELT array */ - double aval[ 2 ]; /* General purpose array */ - double azel[ 2 ]; /* Reference (az,el) values */ - double cdl; /* CDELT term */ - double crval[ 3 ]; /* CRVAL values converted to rads, etc */ - double delta; /* Spectral axis increment */ - double equ; /* Epoch of reference equinox */ - double fd; /* Fraction of a day */ - double latval; /* CRVAL for latitude axis */ - double lonpole; /* LONPOLE value */ - double lonval; /* CRVAL for longitude axis */ - double mjd99; /* MJD at start of 1999 */ - double p1, p2; /* Projection parameters */ - double radec[ 2 ]; /* Reference (lon,lat) values */ - double rf; /* Rest freq (Hz) */ - double specfactor; /* Factor for converting internal spectral units */ - double val; /* General purpose value */ - double xin[ 3 ]; /* Grid coords at centre of first pixel */ - double xout[ 3 ]; /* WCS coords at centre of first pixel */ - int axlat; /* Index of latitude FITS WCS axis */ - int axlon; /* Index of longitude FITS WCS axis */ - int axspec; /* Index of spectral FITS WCS axis */ - int i; /* Axis index */ - int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ - int iymdf[ 4 ]; /* Year, month, date, fractional day */ - int j; /* Axis index */ - int jj; /* SlaLib status */ - int naxis2; /* Length of pixel axis 2 */ - int naxis3; /* Length of pixel axis 3 */ - int naxis; /* No. of axes */ - int ok; /* Is FitsSTore OK for IRAF encoding? */ - int prj; /* Projection type */ - -/* Other initialisation to avoid compiler warnings. */ - lonval = 0.0; - latval = 0.0; - -/* Check the inherited status. */ - if( !astOK ) return 0; - -/* Initialise */ - specfactor = 1.0; - -/* First check that the values in the FitsStore conform to the - requirements of the CLASS encoding. Assume they do not to begin with. */ - ok = 0; - -/* Just do primary axes. */ - s = ' '; - -/* Look for the primary celestial axes. */ - FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); - -/* Get the current Frame from the supplied FrameSet. */ - curfrm = astGetFrame( fs, AST__CURRENT ); - -/* Spectral and celestial axes must be present in axes 1,2 and 3. */ - if( axspec >= 0 && axspec < 3 && - axlon >= 0 && axlon < 3 && - axlat >= 0 && axlat < 3 ) { - ok = 1; - -/* If the spatial pixel axes are degenerate (i.e. span only a single - pixel), modify the CRPIX and CRVAL values in the FitsStore to put - the reference point at the centre of the one and only spatial pixel. */ - if( store->naxis >= 3 && dim[ axlon ] == 1.0 && dim[ axlat ] == 1.0 ){ - xin[ 0 ] = 1.0; - xin[ 1 ] = 1.0; - xin[ 2 ] = 1.0; - astTranN( fs, 1, 3, 1, xin, 1, 3, 1, xout ); - if( xout[ axlon ] != AST__BAD && xout[ axlat ] != AST__BAD ) { - -/* The indices of the spatial axes in the FITS header may not be the same - as the indices of the spatial axes in the WCS Frame of the supplied - FrameSet. So search the current Frame for longitude and latitude axes, - and store the corresponding elements of the "xout" array for later use. */ - for( i = 0; i < 3; i++ ) { - sprintf( attbuf, "IsLonAxis(%d)", i + 1 ); - if( astHasAttribute( curfrm, attbuf ) ) { - if( astGetI( curfrm, attbuf ) ) { - lonval = xout[ i ]; - } else { - latval = xout[ i ]; - } - } - } - -/* Store them in the FitsStore. */ - SetItem( &(store->crval), axlon, 0, ' ', lonval*AST__DR2D, status ); - SetItem( &(store->crval), axlat, 0, ' ', latval*AST__DR2D, status ); - SetItem( &(store->crpix), 0, axlon, ' ', 1.0, status ); - SetItem( &(store->crpix), 0, axlat, ' ', 1.0, status ); - } - } - -/* Get the CRVAL values for both spatial axes. */ - latval = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status ); - if( latval == AST__BAD ) ok = 0; - lonval = GetItem( &( store->crval ), axlon, 0, s, NULL, method, class, status ); - if( lonval == AST__BAD ) ok = 0; - -/* Get the CTYPE values for both axes. Extract the projection type as - specified by the last 4 characters in the latitude CTYPE keyword value. */ - cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else { - strcpy( lontype, cval ); - } - cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - prj = AST__WCSBAD; - } else { - strcpy( lattype, cval ); - prj = astWcsPrjType( cval + 4 ); - } - -/* Check the projection type is OK. */ - if( prj == AST__WCSBAD ){ - ok = 0; - } else if( prj != AST__SIN ){ - -/* Check the projection code is OK. */ - ok = 0; - if( prj == AST__TAN || - prj == AST__ARC || - prj == AST__STG || - prj == AST__AIT || - prj == AST__SFL ) { - ok = 1; - -/* For AIT, and SFL, check that the reference point is the origin of - the celestial co-ordinate system. */ - if( prj == AST__AIT || - prj == AST__SFL ) { - if( latval != 0.0 || lonval != 0.0 ){ - ok = 0; - -/* Change the new SFL projection code to to the older equivalent GLS */ - } else if( prj == AST__SFL ){ - (void) strcpy( lontype + 4, "-GLS" ); - (void) strcpy( lattype + 4, "-GLS" ); - -/* Change the new AIT projection code to to the older equivalent ATF */ - } else if( prj == AST__AIT ){ - (void) strcpy( lontype + 4, "-ATF" ); - (void) strcpy( lattype + 4, "-ATF" ); - } - } - } - -/* SIN projections are only acceptable if the associated projection - parameters are both zero. */ - } else { - p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status ); - p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status ); - if( p1 == AST__BAD ) p1 = 0.0; - if( p2 == AST__BAD ) p2 = 0.0; - ok = ( p1 == 0.0 && p2 == 0.0 ); - } - -/* Identify the celestial coordinate system from the first 4 characters of the - longitude CTYPE value. Only RA and galactic longitude can be stored using - FITS-CLASS. */ - if( ok && strncmp( lontype, "RA--", 4 ) && - strncmp( lontype, "GLON", 4 ) ) ok = 0; - -/* Get the CTYPE values for the spectral axis, and find the CLASS equivalent, - if possible. */ - cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else { - if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) { - strcpy( spectype, "FREQ" ); - } else { - ok = 0; - } - } - -/* If OK, check the SPECSYS value is SOURCE. */ - cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else if( ok ) { - if( strncmp( cval, "SOURCE", astChrLen( cval ) ) ) ok = 0; - } - -/* If still OK, ensure the spectral axis units are Hz. */ - cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - } else if( ok ) { - if( !strcmp( cval, "Hz" ) ) { - specfactor = 1.0; - } else if( !strcmp( cval, "kHz" ) ) { - specfactor = 1.0E3; - } else if( !strcmp( cval, "MHz" ) ) { - specfactor = 1.0E6; - } else if( !strcmp( cval, "GHz" ) ) { - specfactor = 1.0E9; - } else { - ok = 0; - } - } - } - -/* Save the number of WCS axes */ - naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1; - -/* If this is larger than 3, ignore the surplus WCS axes. Note, the - above code has checked that the spatial and spectral axes are - WCS axes 0, 1 and 2. */ - if( naxis > 3 ) naxis = 3; - -/* Allocate memory to store the CDELT values */ - if( ok ) { - cdelt = (double *) astMalloc( sizeof(double)*naxis ); - if( !cdelt ) ok = 0; - } else { - cdelt = NULL; - } - -/* Check that there is no rotation, and extract the CDELT (diagonal) terms, - etc. If the spatial axes are degenerate (i.e. cover only a single pixel) - then ignore any rotation. */ - if( !GetValue( this, FormatKey( "NAXIS", axlon + 1, -1, s, status ), AST__INT, - &naxis2, 0, 0, method, class, status ) ) { - naxis2 = 0; - } - if( !GetValue( this, FormatKey( "NAXIS", axlat + 1, -1, s, status ), AST__INT, - &naxis3, 0, 0, method, class, status ) ) { - naxis3 = 0; - } - for( i = 0; i < naxis && ok; i++ ){ - cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - if( cdl == AST__BAD ) cdl = 1.0; - for( j = 0; j < naxis && ok; j++ ){ - val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; - val *= cdl; - if( i == j ){ - cdelt[ i ] = val; - } else if( val != 0.0 ){ - if( naxis2 != 1 || naxis3 != 1 ) ok = 0; - } - } - } - -/* Get RADECSYS and the reference equinox. */ - cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); - equ = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); - -/* If RADECSYS was available... */ - if( cval ){ - -/* Only FK4 and FK5 are supported in this encoding. */ - if( strcmp( "FK4", cval ) && strcmp( "FK5", cval ) ) ok = 0; - -/* If epoch was not available, set a default epoch. */ - if( equ == AST__BAD ){ - if( !strcmp( "FK4", cval ) ){ - equ = 1950.0; - } else if( !strcmp( "FK5", cval ) ){ - equ = 2000.0; - } else { - ok = 0; - } - -/* If an epoch was supplied, check it is consistent with the IAU 1984 - rule. */ - } else { - if( !strcmp( "FK4", cval ) ){ - if( equ >= 1984.0 ) ok = 0; - } else if( !strcmp( "FK5", cval ) ){ - if( equ < 1984.0 ) ok = 0; - } else { - ok = 0; - } - } - -/* Check we have a rest frequency */ - rf = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); - if( rf == AST__BAD ) ok = 0; - } - -/* If the spatial Frame covers more than a single Frame and requires a LONPOLE - or LATPOLE keyword, it cannot be encoded using FITS-CLASS. However since - FITS-CLASS imposes a no rotation restriction, it can tolerate lonpole - values of +/- 180 degrees. */ - if( ok && ( naxis2 != 1 || naxis3 != 1 ) ) { - lonpole = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); - if( lonpole != AST__BAD && lonpole != -180.0 && lonpole == 180 ) ok = 0; - if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ) - != AST__BAD ) ok = 0; - } - -/* Only create the keywords if the FitsStore conforms to the requirements - of the FITS-CLASS encoding. */ - if( ok ) { - -/* If celestial axes were added by MakeFitsFrameSet, we need to ensure - the header contains 3 main array axes. This is because the CLASS - encoding does not support the WCSAXES keyword. */ - if( store->naxis == 1 ) { - -/* Update the "NAXIS" value to 3 or put a new card in at the start. */ - astClearCard( this ); - i = 3; - SetValue( this, "NAXIS", &i, AST__INT, NULL, status ); - -/* Put NAXIS2/3 after NAXIS1, or after NAXIS if the FitsChan does not contain - NAXIS1. These are set to 1 since the spatial axes are degenerate. */ - if( FindKeyCard( this, "NAXIS1", method, class, status ) ) { - MoveCard( this, 1, method, class, status ); - } - i = 1; - SetValue( this, "NAXIS2", &i, AST__INT, NULL, status ); - SetValue( this, "NAXIS3", &i, AST__INT, NULL, status ); - } - -/* Find the last WCS related card. */ - FindWcs( this, 1, 1, 0, method, class, status ); - -/* Get and save CRPIX for all pixel axes. These are required, so break - if they are not available. */ - for( j = 0; j < naxis && ok; j++ ){ - val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - } else { - sprintf( combuf, "Reference pixel on axis %d", j + 1 ); - SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val, - AST__FLOAT, combuf, status ); - } - } - -/* Get and save CRVAL for all intermediate axes. These are required, so - break if they are not available. Note, the frequency axis CRVAL is - redefined by FITS-CLASS by reducing it by the RESTFREQ value. */ - for( i = 0; i < naxis && ok; i++ ){ - val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - } else { - crval[ i ] = val; - if( i == axspec ) { - val *= specfactor; - val -= rf; - } - sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); - SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, - AST__FLOAT, combuf, status ); - } - } - -/* Get and save CTYPE for all intermediate axes. These are required, so - break if they are not available. Use the potentially modified versions - saved above for the celestial axes. */ - for( i = 0; i < naxis && ok; i++ ){ - if( i == axlat ) { - cval = lattype; - } else if( i == axlon ) { - cval = lontype; - } else if( i == axspec ) { - cval = spectype; - } else { - cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - } - if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) { - comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); - if( !comm ) { - sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); - comm = combuf; - } - SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, - AST__STRING, comm, status ); - } else { - ok = 0; - } - } - -/* CDELT values */ - if( axspec != -1 ) cdelt[ axspec ] *= specfactor; - for( i = 0; i < naxis; i++ ){ - SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i, - AST__FLOAT, "Pixel size", status ); - } - -/* Reference equinox */ - if( equ != AST__BAD ) SetValue( this, "EQUINOX", &equ, AST__FLOAT, - "Epoch of reference equinox", status ); - -/* Date of observation. */ - val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status ); - if( val != AST__BAD ) { - -/* The format used for the DATE-OBS keyword depends on the value of the - keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. - Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ - palCaldj( 99, 1, 1, &mjd99, &jj ); - if( val < mjd99 ) { - palDjcal( 0, val, iymdf, &jj ); - sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], - iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); - } else { - palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); - palDd2tf( 3, fd, sign, ihmsf ); - sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", - iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], - ihmsf[2], ihmsf[3] ); - } - -/* Now store the formatted string in the FitsChan. */ - cval = combuf; - SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, - "Date of observation", status ); - } - -/* Rest frequency */ - SetValue( this, "RESTFREQ", &rf, AST__FLOAT, "[Hz] Rest frequency", status ); - -/* The image frequency corresponding to the rest frequency (only used for - double sideband data). */ - val = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - SetValue( this, "IMAGFREQ", &val, AST__FLOAT, "[Hz] Image frequency", status ); - } - -/* Ensure the FitsChan contains OBJECT and LINE headers */ - if( !HasCard( this, "OBJECT", method, class, status ) ) { - cval = " "; - SetValue( this, "OBJECT", &cval, AST__STRING, NULL, status ); - } - if( !HasCard( this, "LINE", method, class, status ) ) { - cval = " "; - SetValue( this, "LINE", &cval, AST__STRING, NULL, status ); - } - -/* CLASS expects the VELO-LSR keyword to hold the radio velocity of the - reference channel (NOT of the source as I was told!!) with respect to - the LSRK rest frame. The "crval" array holds the frequency of the - reference channel in the source rest frame, so we need to convert this - to get the value for VELO-LSR. Create a SpecFrame describing the - required frame (other attributes such as Epoch etc are left unset and - so will be picked up from the supplied FrameSet). We set MinAxes - and MaxAxes so that the Frame can be used as a template to match the - 1D or 3D current Frame in the supplied FrameSet. */ - velofrm = (AstFrame *) astSpecFrame( "System=vrad,StdOfRest=lsrk," - "Unit=m/s,MinAxes=1,MaxAxes=3", status ); - -/* Find the spectral axis within the current Frame of the supplied - FrameSet, using the above "velofrm" as a template. */ - fsconv1 = astFindFrame( curfrm, velofrm, "" ); - -/* If OK, extract the SpecFrame from the returned FraneSet (this will - have the attribute values that were assigned explicitly to "velofrm" - and will have inherited all unset attributes from the supplied - FrameSet). */ - if( fsconv1 ) { - velofrm = astAnnul( velofrm ); - velofrm = astGetFrame( fsconv1, AST__CURRENT ); - fsconv1 = astAnnul( fsconv1 ); - -/* Take a copy of the velofrm and modify its attributes so that it - describes frequency in the sources rest frame in units of Hz. This is - the system that CLASS expects for the CRVAL3 keyword. */ - freqfrm = astCopy( velofrm ); - astSet( freqfrm, "System=freq,StdOfRest=Source,Unit=Hz", status ); - -/* Get a Mapping from frequency to velocity. */ - fsconv1 = astConvert( freqfrm, velofrm, "" ); - if( fsconv1 ) { - -/* Use this Mapping to convert the spectral crval value from frequency to - velocity. Also convert the value for the neighbouring channel. */ - aval[ 0 ] = crval[ axspec ]*specfactor; - aval[ 1 ] = aval[ 0 ] + cdelt[ axspec ]*specfactor; - astTran1( fsconv1, 2, aval, 1, aval ); - -/* Store the value. Also store it as VLSR since this keyword seems to be - used for the same thing. */ - SetValue( this, "VELO-LSR", aval, AST__FLOAT, "[m/s] Reference velocity", status ); - SetValue( this, "VLSR", aval, AST__FLOAT, "[m/s] Reference velocity", status ); - -/* The DELTAV keyword holds the radio velocity channel spacing in the - LSR. */ - delta = aval[ 1 ] - aval[ 0 ]; - SetValue( this, "DELTAV", &delta, AST__FLOAT, "[m/s] Velocity resolution", status ); - -/* Free remaining resources. */ - fsconv1 = astAnnul( fsconv1 ); - } - } - velofrm = astAnnul( velofrm ); - -/* AZIMUTH and ELEVATIO - the (az,el) equivalent of CRVAL. We need a - Mapping from the CTYPE spatial system to (az,el). This depends on all - the extra info like telescope position, epoch, etc. This info is in - the current Frame in the supplied FrameSet. First get a conversion - from a sky frame with default axis ordering to the supplied Frame. All - the extra info is picked up from the supplied Frame since it is not set - in the template. */ - radecfrm = (AstFrame *) astSkyFrame( "Permute=0,MinAxes=3,MaxAxes=3", status ); - fsconv1 = astFindFrame( curfrm, radecfrm, "" ); - -/* Now get conversion from the an (az,el) Frame to the supplied Frame. */ - azelfrm = (AstFrame *) astSkyFrame( "System=AZEL,Permute=0,MinAxes=3,MaxAxes=3", status ); - fsconv2 = astFindFrame( curfrm, azelfrm, "" ); - -/* If both conversions werew possible, concatenate their Mappings to get - a Mapping from (lon,lat) in the CTYPE system, to (az,el). */ - if( fsconv1 && fsconv2 ) { - map1 = astGetMapping( fsconv1, AST__CURRENT, AST__BASE ); - map2 = astGetMapping( fsconv2, AST__BASE, AST__CURRENT ); - map3 = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); - -/* Store the CRVAL (ra,dec) values in the default order. */ - radec[ 0 ] = crval[ axlon ]*AST__DD2R; - radec[ 1 ] = crval[ axlat ]*AST__DD2R; - -/* Transform to (az,el), normalise, convert to degrees and store. */ - astTranN( map3, 1, 2, 1, radec, 1, 2, 1, azel ); - if( azel[ 0 ] != AST__BAD && azel[ 1 ] != AST__BAD ) { - astNorm( azelfrm, azel ); - azel[ 0 ] *= AST__DR2D; - azel[ 1 ] *= AST__DR2D; - SetValue( this, "AZIMUTH", azel, AST__FLOAT, "[Deg] Telescope azimuth", status ); - SetValue( this, "ELEVATIO", azel + 1, AST__FLOAT, "[Deg] Telescope elevation", status ); - } - -/* Free resources */ - map1 = astAnnul( map1 ); - map2 = astAnnul( map2 ); - map3 = astAnnul( map3 ); - fsconv1 = astAnnul( fsconv1 ); - fsconv2 = astAnnul( fsconv2 ); - } - radecfrm = astAnnul( radecfrm ); - azelfrm = astAnnul( azelfrm ); - } - curfrm = astAnnul( curfrm ); - -/* Release CDELT workspace */ - if( cdelt ) cdelt = (double *) astFree( (void *) cdelt ); - -/* Return zero or ret depending on whether an error has occurred. */ - return astOK ? ok : 0; -} - -static void ClassTrans( AstFitsChan *this, AstFitsChan *ret, int axlat, - int axlon, const char *method, const char *class, int *status ){ - -/* -* Name: -* ClassTrans - -* Purpose: -* Translated non-standard FITS-CLASS headers into equivalent standard -* ones. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void ClassTrans( AstFitsChan *this, AstFitsChan *ret, int axlat, -* int axlon, const char *method, const char *class ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function extends the functionality of the SpecTrans function, -* by converting non-standard WCS keywords into standard FITS-WCS -* keywords, using the conventions of the FITS-CLASS encoding. - -* Parameters: -* this -* Pointer to the FitsChan containing the original header cards. -* ret -* Pointer to a FitsChan in which to return the standardised header -* cards. -* axlat -* Zero-based index of the celestial latitude axis. -* axlon -* Zero-based index of the celestial longitude axis. -* method -* Pointer to string holding name of calling method. -* class -* Pointer to a string holding the name of the supplied object class. -*/ - -/* Local Variables: */ - char *cval; /* Pointer to character string */ - char newtype[ 10 ]; /* New CTYPE value */ - const char *keyname; /* Pointer to keyword name */ - const char *ssyssrc; /* Pointer to SSYSSRC keyword value string */ - double crval; /* CRVAL value */ - double restfreq; /* Rest frequency (Hz) */ - double v0; /* Ref channel velocity in source frame */ - double vref; /* Ref channel velocity in LSR or whatever */ - double vsource; /* Source velocity */ - double zsource; /* Source redshift */ - int axspec; /* Index of spectral axis */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get the rest frequency. */ - restfreq = AST__BAD; - if( !GetValue2( ret, this, "RESTFRQ", AST__FLOAT, (void *) &restfreq, 0, - method, class, status ) ){ - GetValue2( ret, this, "RESTFREQ", AST__FLOAT, (void *) &restfreq, 0, - method, class, status ); - } - if( restfreq == AST__BAD ) { - astError( AST__BDFTS, "%s(%s): Keyword RESTFREQ not found in CLASS " - "FITS header.", status, method, class ); - } - -/* Get the index of the spectral axis. */ - if( axlat + axlon == 1 ) { - axspec = 2; - } else if( axlat + axlon == 3 ) { - axspec = 0; - } else { - axspec = 1; - } - -/* Get the spectral CTYPE value */ - if( GetValue2( ret, this, FormatKey( "CTYPE", axspec + 1, -1, ' ', status ), - AST__STRING, (void *) &cval, 0, method, class, status ) ){ - -/* We can only handle frequency axes at the moment. */ - if( !astChrMatch( "FREQ", cval ) ) { - astError( AST__BDFTS, "%s(%s): FITS-CLASS keyword %s has value " - "\"%s\" - CLASS support in AST only includes \"FREQ\" axes.", status, - method, class, FormatKey( "CTYPE", axspec + 1, -1, ' ', status ), - cval ); - -/* CRVAL for the spectral axis needs to be incremented by RESTFREQ if the - axis represents frequency. */ - } else { - keyname = FormatKey( "CRVAL", axspec + 1, -1, ' ', status ); - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &crval, 1, - method, class, status ) ) { - crval += restfreq; - SetValue( ret, keyname, (void *) &crval, AST__FLOAT, NULL, status ); - } - } - -/* CLASS frequency axes describe source frame frequencies. */ - cval = "SOURCE"; - SetValue( ret, "SPECSYS", (void *) &cval, AST__STRING, NULL, status ); - } - -/* If no projection code is supplied for the longitude and latitude axes, - use "-GLS". This will be translated to "-SFL" by SpecTrans. */ - keyname = FormatKey( "CTYPE", axlon + 1, -1, ' ', status ); - if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method, - class, status ) ){ - if( strlen(cval) > 4 && !strncmp( " ", cval + 4, 4 ) ) { - strncpy( newtype, cval, 4 ); - strcpy( newtype + 4, "-GLS" ); - cval = newtype; - SetValue( ret, keyname, (void *) &cval, AST__STRING, NULL, status ); - } - } - keyname = FormatKey( "CTYPE", axlat + 1, -1, ' ', status ); - if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method, - class, status ) ){ - if( strlen(cval) > 4 && !strncmp( " ", cval + 4, 4 ) ) { - strncpy( newtype, cval, 4 ); - strcpy( newtype + 4, "-GLS" ); - cval = newtype; - SetValue( ret, keyname, (void *) &cval, AST__STRING, NULL, status ); - } - } - -/* Look for a keyword with name "VELO-...". This specifies the radio velocity - at the reference channel, in a standard of rest specified by the "..." - in the keyword name. If "VELO-..." is not found, look for "VLSR", - which is the same as "VELO-LSR". */ - if( GetValue2( ret, this, "VELO-%3c", AST__FLOAT, (void *) &vref, 0, - method, class, status ) || - GetValue2( ret, this, "VLSR", AST__FLOAT, (void *) &vref, 0, - method, class, status ) ){ - -/* Calculate the radio velocity (in the rest frame of the source) corresponding - to the frequency at the reference channel. */ - v0 = AST__C*( restfreq - crval )/restfreq; - -/* Assume that the source velocity is the difference between this velocity - and the reference channel velocity given by "VELO-..." */ - vsource = vref - v0; - -/* Get the keyword name and find the corresponding SSYSSRC keyword value. */ - keyname = CardName( this, status ); - if( !strcmp( keyname, "VELO-HEL" ) ) { - ssyssrc = "BARYCENT"; - } else if( !strcmp( keyname, "VELO-OBS" ) || !strcmp( keyname, "VELO-TOP" ) ) { - ssyssrc = "TOPOCENT"; - } else if( !strcmp( keyname, "VELO-EAR" ) || !strcmp( keyname, "VELO-GEO" ) ) { - ssyssrc = "GEOCENTR"; - } else { - ssyssrc = "LSRK"; - } - SetValue( ret, "SSYSSRC", (void *) &ssyssrc, AST__STRING, NULL, status ); - -/* Convert from radio velocity to redshift and store as ZSOURCE */ - zsource = ( AST__C / (AST__C - vsource) ) - 1.0; - SetValue( ret, "ZSOURCE", (void *) &zsource, AST__FLOAT, NULL, status ); - } -} - -static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { -/* -* Name: -* ClearAttrib - -* Purpose: -* Clear an attribute value for a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void ClearAttrib( AstObject *this, const char *attrib, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the astClearAttrib protected -* method inherited from the Channel class). - -* Description: -* This function clears the value of a specified attribute for a -* FitsChan, so that the default value will subsequently be used. - -* Parameters: -* this -* Pointer to the FitsChan. -* attrib -* Pointer to a null-terminated string specifying the attribute -* name. This should be in lower case with no surrounding white -* space. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_object; - -/* Check the attribute name and clear the appropriate attribute. */ - -/* Card. */ -/* ----- */ - if ( !strcmp( attrib, "card" ) ) { - astClearCard( this ); - -/* Encoding. */ -/* --------- */ - } else if ( !strcmp( attrib, "encoding" ) ) { - astClearEncoding( this ); - -/* CDMatrix */ -/* -------- */ - } else if ( !strcmp( attrib, "cdmatrix" ) ) { - astClearCDMatrix( this ); - -/* FitsAxisOrder. */ -/* ----------- */ - } else if ( !strcmp( attrib, "fitsaxisorder" ) ) { - astClearFitsAxisOrder( this ); - -/* FitsDigits. */ -/* ----------- */ - } else if ( !strcmp( attrib, "fitsdigits" ) ) { - astClearFitsDigits( this ); - -/* DefB1950 */ -/* -------- */ - } else if ( !strcmp( attrib, "defb1950" ) ) { - astClearDefB1950( this ); - -/* TabOK */ -/* ----- */ - } else if ( !strcmp( attrib, "tabok" ) ) { - astClearTabOK( this ); - -/* CarLin */ -/* ------ */ - } else if ( !strcmp( attrib, "carlin" ) ) { - astClearCarLin( this ); - -/* SipReplace */ -/* ---------- */ - } else if ( !strcmp( attrib, "sipreplace" ) ) { - astClearSipReplace( this ); - -/* FitsTol */ -/* ------- */ - } else if ( !strcmp( attrib, "fitstol" ) ) { - astClearFitsTol( this ); - -/* PolyTan */ -/* ------- */ - } else if ( !strcmp( attrib, "polytan" ) ) { - astClearPolyTan( this ); - -/* SipOK */ -/* ------- */ - } else if ( !strcmp( attrib, "sipok" ) ) { - astClearSipOK( this ); - -/* Iwc */ -/* --- */ - } else if ( !strcmp( attrib, "iwc" ) ) { - astClearIwc( this ); - -/* Clean */ -/* ----- */ - } else if ( !strcmp( attrib, "clean" ) ) { - astClearClean( this ); - -/* Warnings. */ -/* -------- */ - } else if ( !strcmp( attrib, "warnings" ) ) { - astClearWarnings( this ); - -/* If the name was not recognised, test if it matches any of the - read-only attributes of this class. If it does, then report an - error. */ - } else if ( astOK && ( !strcmp( attrib, "ncard" ) || - !strcmp( attrib, "allwarnings" ) ) ){ - astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " - "value for a %s.", status, attrib, astGetClass( this ) ); - astError( AST__NOWRT, "This is a read-only attribute." , status); - -/* If the attribute is still not recognised, pass it on to the parent - method for further interpretation. */ - } else { - (*parent_clearattrib)( this_object, attrib, status ); - } -} - -static void ClearCard( AstFitsChan *this, int *status ){ - -/* -*+ -* Name: -* astClearCard - -* Purpose: -* Clear the Card attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* void astClearCard( AstFitsChan *this ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function clears the Card attribute for the supplied FitsChan by -* setting it to the index of the first un-used card in the FitsChan. -* This causes the next read operation performed on the FitsChan to -* read the first card. Thus, it is equivalent to "rewinding" the FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Notes: -* - This function attempts to execute even if an error has occurred. -*- -*/ - -/* Local Variables; */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Check the supplied FitsChan. If its is empty, return. */ - if ( !this || !(this->head) ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Set the pointer to the current card so that it points to the card at - the head of the list. */ - this->card = this->head; - -/* If the current card has been read into an AST object, move on to the - first card which has not, unless we are not skipping such cards. */ - if( CARDUSED(this->card) ){ - MoveCard( this, 1, "astClearCard", astGetClass( this ), status ); - } -} - -static int CnvValue( AstFitsChan *this, int type, int undef, void *buff, - const char *method, int *status ){ - -/* -* -* Name: -* CnvValue - -* Purpose: -* Convert a data value into a given FITS data type. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int CnvValue( AstFitsChan *this, int type, int undef, void *buff, -* const char *method, int *status ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function produces a copy of the data value for the current card -* converted from its stored data type to the supplied data type. - -* Parameters: -* this -* Pointer to the FitsChan. -* type -* The FITS data type in which to return the data value of the -* current card. -* undef -* Determines what happens if the current card has an undefined -* value. If "undef" is zero, an error will be reported identifying -* the undefined keyword value. If "undef" is non-zero, no error is -* reported and the contents of the output buffer are left unchanged. -* buf -* A pointer to a buffer to recieve the converted value. It is the -* responsibility of the caller to ensure that a suitable buffer is -* supplied. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero if the conversion was not possible (in which case NO error is -* reported), one otherwise. - -* Notes: -* - When converting from floating point to integer, the floating -* point value is truncated using a C cast. -* - Non-zero numerical values are considered TRUE, and zero -* numerical values are considered FALSE. Any string starting with a -* 'T' or a 'Y' (upper or lower case) is considered TRUE, and anything -* starting with an 'F' or an 'N' (upper or lower case) is considered -* FALSE. In addition, a dot ('.') may be placed in front of a 'T' or an -* 'F'. -* - A logical TRUE value is represented as a real numerical value of -* one and the character string "Y". A logical FALSE value is represented -* by a real numerical value of zero and the character string "N". -* - When converting from a string to any numerical value, zero is -* returned if the string is not a formatted value which can be converted -* into the corresponding type using astSscanf. -* - Real and imaginary parts of a complex value should be separated by -* spaces within strings. If a string does contains only a single numerical -* value, it is assumed to be the real part, and the imaginary part is -* assumed to be zero. -* - When converting a complex numerical type to a non-complex numerical -* type, the returned value is derived from the real part only, the -* imaginary part is ignored. -* - Zero is returned if an error has occurred, or if this function -* should fail for any reason. -* - If the supplied value is undefined an error will be reported. -*/ - -/* Local Variables: */ - int otype; /* Stored data type */ - size_t osize; /* Size of stored data */ - void *odata; /* Pointer to stored data */ - -/* Check the global error status, and the supplied buffer. */ - if ( !astOK || !buff ) return 0; - -/* Get the type in which the data value is stored. */ - otype = CardType( this, status ); - -/* Get a pointer to the stored data value, and its size. */ - osize = 0; - odata = CardData( this, &osize, status ); - -/* Do the conversion. */ - return CnvType( otype, odata, osize, type, undef, buff, - CardName( this, status ), method, astGetClass( this ), - status ); -} - -static int CnvType( int otype, void *odata, size_t osize, int type, int undef, - void *buff, const char *name, const char *method, - const char *class, int *status ){ -/* -* -* Name: -* CnvType - -* Purpose: -* Convert a data value into a given FITS data type. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int CnvType( int otype, void *odata, size_t osize, int type, int undef, -* void *buff, const char *name, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function produces a copy of the data value for the current card -* converted from its stored data type to the supplied data type. - -* Parameters: -* otype -* The type of the supplied data value. -* odata -* Pointer to a buffer holding the supplied data value. -* osize -* The size of the data value (in bytes - strings include the -* terminating null). -* type -* The FITS data type in which to return the data value of the -* current card. -* undef -* Determines what happens if the supplied data value type is -* undefined If "undef" is zero, an error will be reported identifying -* the undefined keyword value. If "undef" is non-zero, no error is -* reported and the contents of the output buffer are left unchanged. -* buff -* A pointer to a buffer to recieve the converted value. It is the -* responsibility of the caller to ensure that a suitable buffer is -* supplied. -* name -* A pointer to a string holding a keyword name to include in error -* messages. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero if the conversion was not possible (in which case NO error is -* reported), one otherwise. - -* Notes: -* - When converting from floating point to integer, the floating -* point value is truncated using a C cast. -* - Non-zero numerical values are considered TRUE, and zero -* numerical values are considered FALSE. Any string starting with a -* 'T' or a 'Y' (upper or lower case) is considered TRUE, and anything -* starting with an 'F' or an 'N' (upper or lower case) is considered -* FALSE. In addition, a dot ('.') may be placed in front of a 'T' or an -* 'F'. -* - A logical TRUE value is represented as a real numerical value of -* one and the character string "Y". A logical FALSE value is represented -* by a real numerical value of zero and the character string "N". -* - When converting from a string to any numerical value, zero is -* returned if the string isn not a formatted value which can be converted -* into the corresponding type using astSscanf. -* - Real and imaginary parts of a complex value should be separated by -* spaces within strings. If a string does contains only a single numerical -* value, it is assumed to be the real part, and the imaginary part is -* assumed to be zero. -* - When converting a complex numerical type to a non-complex numerical -* type, the returned value is derived from the real part only, the -* imaginary part is ignored. -* - Zero is returned if an error has occurred, or if this function -* should fail for any reason. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - const char *c; /* Pointer to next character */ - const char *ostring; /* String data value */ - double odouble; /* Double data value */ - int oint; /* Integer data value */ - int ival; /* Integer value read from string */ - int len; /* Length of character string */ - int nc; /* No. of characetsr used */ - int ret; /* Returned success flag */ - -/* Check the global error status, and the supplied buffer. */ - if ( !astOK || !buff ) return 0; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(NULL); - -/* Assume success. */ - ret = 1; - -/* If the supplied data type is undefined, report an error unless the - returned data type is also undefined or an undefined value is - acceptable for the keyword. */ - if( otype == AST__UNDEF ) { - if( type != AST__UNDEF && !undef ) { - ret = 0; - astError( AST__FUNDEF, "The FITS keyword '%s' has an undefined " - "value.", status, name ); - } - -/* If the returned data type is undefined, the returned value is - immaterial, so leave the buffer contents unchanged. */ - } else if( type == AST__UNDEF ) { - -/* If there is no data value and this is not a COMMENT keyword, or if - there is a data value and this is a COMMENT card, conversion is not - possible. */ - } else if( ( odata && otype == AST__COMMENT ) || - ( !odata && otype != AST__COMMENT ) ) { - ret = 0; - -/* If there is no data value (and therefore this is a comment card), - conversion is only possible if the output type is also a comment. */ - } else if( !odata ) { - if( type != AST__COMMENT ) ret = 0; - -/* Otherwise we have a data value, so do each possible combination of - supplied and stored data types... */ - } else { - -/* Convert a AST__FLOAT data value to ... */ - if( otype == AST__FLOAT ){ - odouble = *( (double *) odata ); - if( type == AST__FLOAT ){ - (void) memcpy( buff, odata, osize ); - } else if( type == AST__STRING || type == AST__CONTINUE ){ - if( odouble != AST__BAD ) { - (void) sprintf( cnvtype_text, "%.*g", AST__DBL_DIG, odouble ); - CheckZero( cnvtype_text, odouble, 0, status ); - } else { - strcpy( cnvtype_text, BAD_STRING ); - } - *( (char **) buff ) = cnvtype_text; - } else if( type == AST__INT ){ - *( (int *) buff ) = (int) odouble; - } else if( type == AST__LOGICAL ){ - *( (int *) buff ) = ( odouble == 0.0 ) ? 0 : 1; - } else if( type == AST__COMPLEXF ){ - ( (double *) buff )[ 0 ] = odouble; - ( (double *) buff )[ 1 ] = 0.0; - } else if( type == AST__COMPLEXI ){ - ( (int *) buff )[ 0 ] = (int) odouble; - ( (int *) buff )[ 1 ] = 0; - } else if( astOK ){ - ret = 0; - astError( AST__INTER, "CnvType: AST internal programming error - " - "FITS data-type no. %d not yet supported.", status, type ); - } - -/* Convert a AST__STRING data value to ... */ - } else if( otype == AST__STRING || type == AST__CONTINUE ){ - ostring = (char *) odata; - len = (int) strlen( ostring ); - if( type == AST__FLOAT ){ - if( nc = 0, - ( 0 == astSscanf( ostring, BAD_STRING " %n", &nc ) ) - && (nc >= len ) ){ - *( (double *) buff ) = AST__BAD; - } else if( nc = 0, - ( 1 != astSscanf( ostring, "%lf %n", (double *) buff, &nc ) ) - || (nc < len ) ){ - ret = 0; - } - } else if( type == AST__STRING || type == AST__CONTINUE ){ - strncpy( cnvtype_text, (char *) odata, AST__FITSCHAN_FITSCARDLEN ); - *( (char **) buff ) = cnvtype_text; - } else if( type == AST__INT ){ - if( nc = 0, - ( 1 != astSscanf( ostring, "%d %n", (int *) buff, &nc ) ) - || (nc < len ) ){ - ret = 0; - } - } else if( type == AST__LOGICAL ){ - if( nc = 0, - ( 1 == astSscanf( ostring, "%d %n", &ival, &nc ) ) - && (nc >= len ) ){ - *( (int *) buff ) = ival ? 1 : 0; - } else { - c = ostring; - while( *c && isspace( (int) *c ) ) c++; - if( *c == 'y' || *c == 'Y' || *c == 't' || *c == 'T' || - ( *c == '.' && ( c[1] == 't' || c[1] == 'T' ) ) ){ - *( (int *) buff ) = 1; - } else if( *c == 'n' || *c == 'N' || *c == 'f' || *c == 'F' || - ( *c == '.' && ( c[1] == 'f' || c[1] == 'F' ) ) ){ - *( (int *) buff ) = 0; - } else { - ret = 0; - } - } - } else if( type == AST__COMPLEXF ){ - if( nc = 0, - ( 1 != astSscanf( ostring, "%lf %lf %n", (double *) buff, - (double *) buff + 1, &nc ) ) - || (nc < len ) ){ - if( nc = 0, - ( 1 != astSscanf( ostring, "%lf %n", (double *) buff, - &nc ) ) - || (nc < len ) ){ - ret = 0; - } else { - ( (double *) buff )[ 1 ] = 0.0; - } - } - } else if( type == AST__COMPLEXI ){ - if( nc = 0, - ( 1 != astSscanf( ostring, "%d %d %n", (int *) buff, - (int *) buff + 1, &nc ) ) - || (nc < len ) ){ - if( nc = 0, - ( 1 != astSscanf( ostring, "%d %n", (int *) buff, &nc ) ) - || (nc < len ) ){ - ret = 0; - } else { - ( (int *) buff )[ 1 ] = 0; - } - } - } else if( astOK ){ - ret = 0; - astError( AST__INTER, "CnvType: AST internal programming error - " - "FITS data-type no. %d not yet supported.", status, type ); - } - -/* Convert an AST__INT data value to ... */ - } else if( otype == AST__INT ){ - oint = *( (int *) odata ); - if( type == AST__FLOAT ){ - *( (double *) buff ) = (double) oint; - } else if( type == AST__STRING || type == AST__CONTINUE ){ - (void) sprintf( cnvtype_text, "%d", oint ); - *( (char **) buff ) = cnvtype_text; - } else if( type == AST__INT ){ - (void) memcpy( buff, odata, osize ); - } else if( type == AST__LOGICAL ){ - *( (int *) buff ) = oint ? 1 : 0; - } else if( type == AST__COMPLEXF ){ - ( (double *) buff )[ 0 ] = (double) oint; - ( (double *) buff )[ 1 ] = 0.0; - } else if( type == AST__COMPLEXI ){ - ( (int *) buff )[ 0 ] = oint; - ( (int *) buff )[ 1 ] = 0; - } else if( astOK ){ - ret = 0; - astError( AST__INTER, "CnvType: AST internal programming error - " - "FITS data-type no. %d not yet supported.", status, type ); - } - -/* Convert a LOGICAL data value to ... */ - } else if( otype == AST__LOGICAL ){ - oint = *( (int *) odata ); - if( type == AST__FLOAT ){ - *( (double *) buff ) = oint ? 1.0 : 0.0; - } else if( type == AST__STRING || type == AST__CONTINUE ){ - if( oint ){ - strcpy( cnvtype_text, "Y" ); - } else { - strcpy( cnvtype_text, "N" ); - } - *( (char **) buff ) = cnvtype_text; - } else if( type == AST__INT ){ - *( (int *) buff ) = oint; - } else if( type == AST__LOGICAL ){ - (void) memcpy( buff, odata, osize ); - } else if( type == AST__COMPLEXF ){ - ( (double *) buff )[ 0 ] = oint ? 1.0 : 0.0; - ( (double *) buff )[ 1 ] = 0.0; - } else if( type == AST__COMPLEXI ){ - ( (int *) buff )[ 0 ] = oint ? 1 : 0; - ( (int *) buff )[ 1 ] = 0; - } else if( astOK ){ - ret = 0; - astError( AST__INTER, "CnvType: AST internal programming error - " - "FITS data-type no. %d not yet supported.", status, type ); - } - -/* Convert a AST__COMPLEXF data value to ... */ - } else if( otype == AST__COMPLEXF ){ - odouble = ( (double *) odata )[ 0 ]; - if( type == AST__FLOAT ){ - *( (double *) buff ) = odouble; - } else if( type == AST__STRING || type == AST__CONTINUE ){ - (void) sprintf( cnvtype_text0, "%.*g", AST__DBL_DIG, ( (double *) odata )[ 0 ] ); - CheckZero( cnvtype_text0, ( (double *) odata )[ 0 ], 0, status ); - (void) sprintf( cnvtype_text1, "%.*g", AST__DBL_DIG, ( (double *) odata )[ 1 ] ); - CheckZero( cnvtype_text1, ( (double *) odata )[ 1 ], 0, status ); - (void) sprintf( cnvtype_text, "%s %s", cnvtype_text0, cnvtype_text1 ); - *( (char **) buff ) = cnvtype_text; - } else if( type == AST__INT ){ - *( (int *) buff ) = (int) odouble; - } else if( type == AST__LOGICAL ){ - *( (int *) buff ) = ( odouble == 0.0 ) ? 0 : 1; - } else if( type == AST__COMPLEXF ){ - (void) memcpy( buff, odata, osize ); - } else if( type == AST__COMPLEXI ){ - ( (int *) buff )[ 0 ] = (int) odouble; - ( (int *) buff )[ 1 ] = (int) ( (double *) odata )[ 1 ]; - } else if( astOK ){ - ret = 0; - astError( AST__INTER, "CnvType: AST internal programming error - " - "FITS data-type no. %d not yet supported.", status, type ); - } - -/* Convert a AST__COMPLEXI data value to ... */ - } else if( otype == AST__COMPLEXI ){ - oint = ( (int *) odata )[ 0 ]; - if( type == AST__FLOAT ){ - *( (double *) buff ) = (double) oint; - } else if( type == AST__STRING || type == AST__CONTINUE ){ - (void) sprintf( cnvtype_text, "%d %d", ( (int *) odata )[ 0 ], - ( (int *) odata )[ 1 ] ); - *( (char **) buff ) = cnvtype_text; - } else if( type == AST__INT ){ - *( (int *) buff ) = oint; - } else if( type == AST__LOGICAL ){ - *( (int *) buff ) = oint ? 1 : 0; - } else if( type == AST__COMPLEXF ){ - ( (double *) buff )[ 0 ] = (double) oint; - ( (double *) buff )[ 1 ] = (double) ( (int *) odata )[ 1 ]; - } else if( type == AST__COMPLEXI ){ - (void) memcpy( buff, odata, osize ); - } else if( astOK ){ - ret = 0; - astError( AST__INTER, "CnvType: AST internal programming error - " - "FITS data-type no. %d not yet supported.", status, type ); - } - } else if( astOK ){ - ret = 0; - astError( AST__INTER, "CnvType: AST internal programming error - " - "FITS data-type no. %d not yet supported.", status, type ); - } - } - return ret; -} - -static int ComBlock( AstFitsChan *this, int incr, const char *method, - const char *class, int *status ){ - -/* -* Name: -* ComBlock - -* Purpose: -* Delete a AST comment block in a Native-encoded FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int ComBlock( AstFitsChan *this, int incr, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function looks for a block of comment cards as defined below, -* and deletes all the cards in the block, if a suitable block is found. -* -* Comment blocks consist of a contiguous sequence of COMMENT cards. The -* text of each card should start and end with the 3 characters "AST". -* The block is delimited above by a card containing all +'s (except -* for the two "AST" strings), and below by a card containing all -'s. -* -* The block is assumed to start on the card which is adjacent to the -* current card on entry. - -* Parameters: -* this -* Pointer to the FitsChan. -* incr -* This should be either +1 or -1, and is the increment between -* adjacent cards in the comment block. A value of +1 means -* that the card following the current card is taken as the first in -* the block, and subsequent cards are checked. The block must then -* end with a line of -'s. If -1 is supplied, then the card -* preceding the current card is taken as the first in the block, -* and preceding cards are checked. The block must then end with -* a row of +'s. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* 1 if a block was found and deleted, 0 otherwise. - -* Notes: -* - The pointer to the current card is returned unchanged. -*/ - -/* Local Variables: */ - FitsCard *card0; /* Pointer to current FitsCard on entry */ - char del; /* Delimiter character */ - char *text; /* Pointer to the comment text */ - int i; /* Card index within the block */ - int ncard; /* No. of cards in the block */ - int ret; /* The returned flag */ - size_t len; /* Length of the comment text */ - -/* Check the global status. */ - if( !astOK ) return 0; - -/* Save the pointer to the current card. */ - card0 = this->card; - -/* Initialise the returned flag to indicate that we have not found a - comment block. */ - ret = 0; - -/* Move on to the first card in the block. If this is not possible (due to - us already being at the start or end of the FitsChan), then return. */ - if( MoveCard( this, incr, method, class, status ) == 1 ) { - -/* Store the character which is used in the delimiter line for the - comment block. */ - del = ( incr == 1 ) ? '-' : '+'; - -/* Initialise the number of cards in the comment block to zero. */ - ncard = 0; - -/* Loop round until the end (or start) of the comment block is found. - Leave the loop if an error occurs. */ - while( astOK ) { - -/* Is this card a comment card? If not, then we have failed to find a - complete comment block. Break out of the loop. */ - if( CardType( this, status ) != AST__COMMENT ) break; - -/* Increment the number of cards in the comment block. */ - ncard++; - -/* Get the text of the comment, and its length. */ - text = CardComm( this, status ); - if( text ){ - len = strlen( text ); - -/* Check the first 3 characters. Break out of the loop if they are not - "AST". */ - if( strncmp( "AST", text, 3 ) ) break; - -/* Check the last 3 characters. Break out of the loop if they are not - "AST". */ - if( strcmp( "AST", text + len - 3 ) ) break; - -/* If the comment is the appropriate block delimiter (a line of +'s or - -'s depending on the direction), then set the flag to indicate that we - have a complete comment block and leave the loop. Allow spaces to be - included. Exclude the "AST" strings at begining and end from the check. */ - ret = 1; - for( i = 3; i < len - 3; i++ ) { - if( text[ i ] != del && text[ i ] != ' ' ) { - ret = 0; - break; - } - } - } - if( ret ) break; - -/* Move on to the next card. If this is not possible (due to us already - being at the start or end of the FitsChan), then break out of the loop. */ - if( MoveCard( this, incr, method, class, status ) == 0 ) break; - } - -/* Re-instate the original current card. */ - this->card = card0; - -/* If we found a complete comment block, mark it (which is equivalent to - deleting it except that memory of the cards location within the - FitsChan is preserved for future use), and then re-instate the original - current card. */ - if( ret && astOK ) { - for( i = 0; i < ncard; i++ ) { - MoveCard( this, incr, method, class, status ); - MarkCard( this, status ); - } - this->card = card0; - } - } - -/* If an error occurred, indicate that coment block has been deleted. */ - if( !astOK ) ret = 0; - return ret; -} - -static char *ConcatWAT( AstFitsChan *this, int iaxis, const char *method, - const char *class, int *status ){ -/* -* Name: -* ConcatWAT - -* Purpose: -* Concatenate all the IRAF "WAT" keywords for an axis. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* char *ConcatWAT( AstFitsChan *this, int iaxis, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function searches the supplied FitsChan for any keywords of -* the form "WATi_j", where i and j are integers and i is equal to the -* supplied "iaxis" value plus one, and concatenates their string -* values into a single string. Such keywords are created by IRAF to -* describe their non-standard ZPX and TNX projections. - -* Parameters: -* this -* The FistChan. -* iaxis -* The zero-based index of the axis to be retrieved. -* method -* The name of the calling method to include in error messages. -* class -* The object type to include in error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a dynamically allocated, null terminated string -* containing a copy of the concatentated WAT values. This string must -* be freed by the caller (using astFree) when no longer required. -* -* A NULL pointer will be returned if there are no WAT kewyords for -* the requested axis in the FitsChan. - -* 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: */ - char keyname[ FITSNAMLEN + 5 ];/* Keyword name */ - char *wat; /* Pointer to a single WAT string */ - char *result; /* Returned string */ - int watlen; /* Length of total WAT string (inc. term null)*/ - int j; /* WAT index */ - size_t size; /* Length of string value */ - -/* Initialise returned value. */ - result = NULL; - -/* Check inherited status */ - if( !astOK ) return result; - -/* Rewind the FitsChan. */ - astClearCard( this ); - -/* Concatenate all the IRAF "WAT" keywords together for this axis. These - keywords are marked as having been used, so that they are not written - out when the FitsChan is deleted. */ - watlen = 1; - j = 1; - size = 0; - sprintf( keyname, "WAT%d_%.3d", iaxis + 1, j ); - while( astOK ) { - -/* Search forward from the current card for the next WAT card. If no - found, try searching again from the start of the FitsChan. If not found - evenm then, break. */ - if( ! FindKeyCard( this, keyname, method, class, status ) ) { - astClearCard( this ); - if( ! FindKeyCard( this, keyname, method, class, status ) ) break; - } - - wat = (char *) CardData( this, &size, status ); - result = (char *) astRealloc( (void *) result, - watlen - 1 + size ); - if( result ) { - strcpy( result + watlen - 1, wat ); - watlen += size - 1; - MarkCard( this, status ); - MoveCard( this, 1, method, class, status ); - j++; - sprintf( keyname, "WAT%d_%.3d", iaxis + 1, j ); - } else { - break; - } - } - -/* Return the result. */ - return result; -} - -static int CountFields( const char *temp, char type, const char *method, - const char *class, int *status ){ -/* -* Name: -* CountFields - -* Purpose: -* Count the number of field specifiers in a template string. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int CountFields( const char *temp, char type, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns the number of fields which include the -* specified character type in the supplied string. - -* Parameters: -* temp -* Pointer to a null terminated string holding the template. -* type -* A single character giving the field type to be counted (e.g. -* 'd', 'c' or 'f'). -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The number of fields. - -* Notes: -* - No error is reported if the parameter "type" is not a valid -* field type specifier, but zero will be returned. -* - An error is reported if the template has any invalid field -* specifiers in it. -* - A value of zero is returned if an error has already occurred, -* or if this function should fail for any reason. -*/ - -/* Local Variables: */ - const char *b; /* Pointer to next template character */ - int nf; /* No. of fields found so far */ - -/* Check global status. */ - if( !astOK ) return 0; - -/* Initialise a pointer to the start of the template string. */ - b = temp; - -/* Initialise the number of fields found so far. */ - nf = 0; - -/* Go through the string. */ - while( *b && astOK ){ - -/* If the current character is a '%', a field is starting. */ - if( *b == '%' ){ - -/* Skip over the field width (if supplied). */ - if( isdigit( (int) *(++b) ) ) b++; - -/* Report an error if the end of the string occurs within the field. */ - if( !*b ) { - astError( AST__BDFMT, "%s(%s): Incomplete field specifier found " - "at end of filter template '%s'.", status, method, class, - temp ); - break; - -/* Report an error if the field type is illegal. */ - } else if( *b != 'd' && *b != 'c' && *b != 'f' ) { - astError( AST__BDFMT, "%s(%s): Illegal field type or width " - "specifier '%c' found in filter template '%s'.", status, - method, class, *b, temp ); - break; - } - -/* Compare the field type with the supplied type, and increment the - number of fields found if it is the correct type. */ - if( *b == type ) nf++; - } - -/* Move on to the next character. */ - b++; - } - -/* If an error has occurred, return 0. */ - if( !astOK ) nf = 0; - -/* Return the answer. */ - return nf; -} - -static void CreateKeyword( AstFitsChan *this, const char *name, - char keyword[ FITSNAMLEN + 1 ], int *status ){ - -/* -* Name: -* CreateKeyword - -* Purpose: -* Create a unique un-used keyword for a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void CreateKeyword( AstFitsChan *this, const char *name, -* char keyword[ FITSNAMLEN + 1 ], int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function takes a name which forms the basis of a FITS -* keyword and appends a sequence number (encoded as a pair of -* legal FITS keyword characters) so as to generate a unique FITS -* keyword which has not previously been used in the FitsChan -* supplied. -* -* It is intended for use when several keywords with the same name -* must be stored in a FitsChan, since to comply strictly with the -* FITS standard keywords should normally be unique (otherwise -* external software which processes the keywords might omit one or -* other of the values). -* -* An attempt is also made to generate keywords in a form that is -* unlikely to clash with those from other sources (in as far as -* this is possible with FITS). In any event, a keyword that -* already appears in the FitsChan will not be re-used. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a constant null-terminated string containing the -* name on which the new keyword should be based. This should be -* a legal FITS keyword in itself, except that it should be at -* least two characters shorter than the maximum length, in -* order to accommodate the sequence number characters. -* -* If this string is too long, it will be silently -* truncated. Mixed case is permitted, as all characters -* supplied are converted to upper case before use. -* keyword -* A character array in which the generated unique keyword will -* be returned, null terminated. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - const char *seq_chars = SEQ_CHARS;/* Pointer to characters used for encoding */ - char seq_char; /* The first sequence character */ - const char *class; /* Object clas */ - int found; /* Keyword entry found in list? */ - int limit; /* Sequence number has reached limit? */ - int nc; /* Number of basic keyword characters */ - int seq; /* The sequence number */ - -/* Check the global error status. */ - if( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Store the object class. */ - class = astGetClass( this ); - -/* On the first invocation only, determine the number of characters - being used to encode sequence number information and save this - value. */ - if( createkeyword_seq_nchars < 0 ) createkeyword_seq_nchars = (int) strlen( seq_chars ); - -/* Copy the name supplied into the output array, converting to upper - case. Leave space for two characters to encode a sequence - number. Terminate the resulting string. */ - for( nc = 0; ( nc < ( FITSNAMLEN - 2 ) ) && name[ nc ]; nc++ ) { - keyword[ nc ] = toupper( name[ nc ] ); - } - keyword[ nc ] = '\0'; - -/* We now search the list of sequence numbers already allocated to - find the next one to use for this keyword. */ - if( this->keyseq ) { - found = astMapGet0I( this->keyseq, keyword, &seq ); - } else { - found = 0; - this->keyseq = astKeyMap( " ", status ); - } - -/* If the keyword was not found in the list, create a new list entry - to describe it. */ - if( !found ) seq = 0; - -/* If OK, loop to find a new sequence number which results in a FITS - keyword that hasn't already been used to store data in the - FitsChan. */ - if( astOK ) { - while( 1 ) { - -/* Determine if the sequence number just obtained has reached the - upper limit. This is unlikely to happen in practice, but if it - does, we simply re-use this maximum value. Otherwise, we increment - the sequence number last used for this keyword to obtain a new - one. */ - limit = ( seq >= ( createkeyword_seq_nchars * createkeyword_seq_nchars - 1 ) ); - if( !limit ) seq++; - -/* Encode the sequence number into two characters and append them to - the original keyword (with a terminating null). */ - seq_char = seq_chars[ seq / createkeyword_seq_nchars ]; - keyword[ nc ] = seq_char; - keyword[ nc + 1 ] = seq_chars[ seq % createkeyword_seq_nchars ]; - keyword[ nc + 2 ] = '\0'; - -/* If the upper sequence number limit has not been reached, try to - look up the resulting keyword in the FitsChan to see if it has - already been used. Quit searching when a suitable keyword is - found. */ - if ( limit || !HasCard( this, keyword, "astWrite", class, status ) ) break; - } - -/* Store the update sequence number in the keymap. The keys into this - keymap are the base keyword name without the appended sequence string, so - temporaily terminate the returned keyword name to exclude the sequence - string. */ - keyword[ nc ] = '\0'; - astMapPut0I( this->keyseq, keyword, seq, NULL ); - keyword[ nc ] = seq_char; - } -} - -static double DateObs( const char *dateobs, int *status ) { -/* -* Name: -* DateObs - -* Purpose: -* Convert a FITS DATE-OBS keyword value to a MJD. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* double DateObs( const char *dateobs, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Extracts the date and time fields from the supplied string and converts -* them into a modified Julian Date. Supports both old "dd/mm/yy" -* format, and the new "ccyy-mm-ddThh:mm:ss[.sss...]" format. - -* Parameters: -* dateobs -* Pointer to the DATE-OBS string. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The Modified Julian Date corresponding to the supplied DATE-OBS -* string. - -* Notes: -* - The value AST__BAD is returned (without error) if the supplied -* string does not conform to the requirements of a FITS DATE-OBS value, -* or if an error has already occurred. -*/ - -/* Local Variables: */ - double days; /* The hours, mins and secs as a fraction of a day */ - double ret; /* The returned MJD value */ - double secs; /* The total value of the two seconds fields */ - int dd; /* The day field from the supplied string */ - int fsc; /* The fractional seconds field from the supplied string */ - int hr; /* The hour field from the supplied string */ - int j; /* SLALIB status */ - int len; /* The length of the supplied string */ - int mm; /* The month field from the supplied string */ - int mn; /* The minute field from the supplied string */ - int nc; /* Number of characters used */ - int ok; /* Was the string of a legal format? */ - int rem; /* The least significant digit in fsc */ - int sc; /* The whole seconds field from the supplied string */ - int yy; /* The year field from the supplied string */ - -/* Check the global status. */ - if( !astOK ) return AST__BAD; - -/* Initialise the returned value. */ - ret = AST__BAD; - -/* Save the length of the supplied string. */ - len = (int) strlen( dateobs ); - -/* Extract the year, month, day, hour, minute, second and fractional - seconds fields from the supplied string. Assume initially that the - string does not match any format. */ - ok = 0; - -/* First check for the old "dd/mm/yy" format. */ - if( nc = 0, - ( astSscanf( dateobs, " %2d/%2d/%d %n", &dd, &mm, &yy, &nc ) == 3 ) && - ( nc >= len ) ){ - ok = 1; - hr = 0; - mn = 0; - sc = 0; - fsc = 0; - -/* Otherwise, check for the new short format "ccyy-mm-dd". */ - } else if( nc = 0, - ( astSscanf( dateobs, " %4d-%2d-%2d %n", &yy, &mm, &dd, &nc ) == 3 ) && - ( nc >= len ) ){ - ok = 1; - hr = 0; - mn = 0; - sc = 0; - fsc = 0; - -/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss" without a - fractional seconds field or the trailing Z. */ - } else if( nc = 0, - ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d %n", &yy, &mm, &dd, - &hr, &mn, &sc, &nc ) == 6 ) && ( nc >= len ) ){ - ok = 1; - fsc = 0; - -/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss.sss" with a - fractional seconds field but without the trailing Z. */ - } else if( nc = 0, - ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d.%d %n", &yy, &mm, &dd, - &hr, &mn, &sc, &fsc, &nc ) == 7 ) && ( nc >= len ) ){ - ok = 1; - -/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ssZ" without a - fractional seconds field but with the trailing Z. */ - } else if( nc = 0, - ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2dZ %n", &yy, &mm, &dd, - &hr, &mn, &sc, &nc ) == 6 ) && ( nc >= len ) ){ - ok = 1; - fsc = 0; - -/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss.sssZ" with a - fractional seconds field and the trailing Z. */ - } else if( nc = 0, - ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d.%dZ %n", &yy, &mm, &dd, - &hr, &mn, &sc, &fsc, &nc ) == 7 ) && ( nc >= len ) ){ - ok = 1; - } - -/* If the supplied string was legal, create a MJD from the separate fields. */ - if( ok ) { - -/* Get the MJD at the start of the day. */ - palCaldj( yy, mm, dd, &ret, &j ); - -/* If succesful, convert the hours, minutes and seconds to a fraction of - a day, and add it onto the MJD found above. */ - if( j == 0 ) { - -/* Obtain a floating point representation of the fractional seconds - field. */ - secs = 0.0; - while ( fsc > 0 ) { - rem = ( fsc % 10 ); - fsc /= 10; - secs = 0.1 * ( secs + (double) rem ); - } - -/* Add on the whole seconds field. */ - secs += (double) sc; - -/*Convert the hours, minutes and seconds to a fractional day. */ - palDtf2d( hr, mn, secs, &days, &j ); - -/* If succesful, add this onto the returned MJD. */ - if( j == 0 ) { - ret = ret + days; - -/* If the conversion to MJD failed, return AST__BAD. */ - } else { - ret = AST__BAD; - } - } else { - ret = AST__BAD; - } - } - -/* Return the result. */ - return ret; -} - -static void DeleteCard( AstFitsChan *this, const char *method, - const char *class, int *status ){ -/* -* Name: -* DeleteCard - -* Purpose: -* Delete the current card from a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void DeleteCard( AstFitsChan *this, const char *method, -* const char *class ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The current card is removed from the circular linked list of structures -* stored in the supplied FitsChan, and the memory used to store the -* structure is then freed. - -* Parameters: -* this -* Pointer to the FitsChan containing the list. -* method -* Name of calling method. -* class -* Object class. - -* Notes: -* - This function returns without action if the FitsChan is -* currently at "end-of-file". -* - The next card becomes the current card. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - FitsCard *card; /* Pointer to the current card */ - FitsCard *next; /* Pointer to next card in list */ - FitsCard *prev; /* Pointer to previous card in list */ - -/* Return if the supplied object or current card is NULL. */ - if( !this || !this->card ) return; - -/* Get a pointer to the card to be deleted (the current card). */ - card = (FitsCard *) this->card; - -/* Remove it from the KeyMap holding all keywords. */ - astMapRemove( this->keywords, card->name ); - -/* Move the current card on to the next card. */ - MoveCard( this, 1, method, class, status ); - -/* Save pointers to the previous and next cards in the list. */ - prev = GetLink( card, PREVIOUS, method, class, status ); - next = GetLink( card, NEXT, method, class, status ); - -/* If the backwards link points back to the supplied card, then it must - be the only one left on the list. */ - if( prev == card ) prev = NULL; - if( next == card ) next = NULL; - -/* If the list head is to be deleted, store a value for the new list - head. */ - if( this->head == (void *) card ) this->head = (void *) next; - -/* Free the memory used to hold the data value. */ - (void) astFree( card->data ); - -/* Free the memory used to hold any comment. */ - if( card->comment ) (void) astFree( (void *) card->comment ); - -/* Free the memory used to hold the whole structure. */ - (void) astFree( (void *) card ); - -/* Fix up the links between the two adjacent cards in the list, unless the - supplied card was the last one in the list. */ - if( prev && next ){ - next->prev = prev; - prev->next = next; - } else { - this->head = NULL; - this->card = NULL; - } - -/* Return. */ - return; -} - -static void DelFits( AstFitsChan *this, int *status ){ - -/* -*++ -* Name: -c astDelFits -f AST_DELFITS - -* Purpose: -* Delete the current FITS card in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" -c void astDelFits( AstFitsChan *this ) -f CALL AST_DELFITS( THIS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function deletes the current FITS card from a FitsChan. The -f This routine deletes the current FITS card from a FitsChan. The -* current card may be selected using the Card attribute (if its index -c is known) or by using astFindFits (if only the FITS keyword is -f is known) or by using AST_FINDFITS (if only the FITS keyword is -* known). -* -* After deletion, the following card becomes the current card. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - This function returns without action if the FitsChan is -* initially positioned at the "end-of-file" (i.e. if the Card -* attribute exceeds the number of cards in the FitsChan). -* - If there are no subsequent cards in the FitsChan, then the -* Card attribute is left pointing at the "end-of-file" after -* deletion (i.e. is set to one more than the number of cards in -* the FitsChan). -*-- -*/ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Delete the current card. The next card will be made the current card. */ - DeleteCard( this, "astDelFits", astGetClass( this ), status ); -} - -static void DistortMaps( AstFitsChan *this, FitsStore *store, char s, - int naxes, AstMapping **map1, AstMapping **map2, - AstMapping **map3, AstMapping **map4, - const char *method, const char *class, int *status ){ -/* -* Name: -* DistortMap - -* Purpose: -* Create a Mapping representing a FITS-WCS Paper IV distortion code. - -* Type: -* Private function. - -* Synopsis: -* void DistortMaps( AstFitsChan *this, FitsStore *store, char s, -* int naxes, AstMapping **map1, AstMapping **map2, -* AstMapping **map3, AstMapping **map4, -* const char *method, const char *class ) - -* Class Membership: -* FitsChan - -* Description: -* This function checks the CTYPE keywords in the supplied FitsStore to see -* if they contain a known distortion code (following the syntax described -* in FITS-WCS paper IV). If so, Mappings are returned which represent the -* distortions to be applied at each stage in the pixel->IWC chain. If -* any distortion codes are found in the FitsStore CTYPE values, whether -* recognised or not, the CTYPE values in the FitsStore are modified to -* remove the distortion code. Warnings about any unknown or inappropriate -* distortion codes are added to the FitsChan. - -* Parameters: -* this -* The FitsChan. ASTWARN cards may be added to this FitsChan if any -* anomalies are found in the keyword values in the FitsStore. -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* naxes -* The number of intermediate world coordinate axes (WCSAXES). -* map1 -* Address of a location at which to store a pointer to a Mapping -* which describes any distortion to be applied to pixel -* coordinates, prior to performing the translation specified by the -* CRPIXj keywords. NULL is returned if no distortion is necessary. -* map2 -* Address of a location at which to store a pointer to a Mapping -* which describes any distortion to be applied to translated pixel -* coordinates, prior to performing the PC matrix multiplication. -* NULL is returned if no distortion is necessary. -* map3 -* Address of a location at which to store a pointer to a Mapping -* which describes any distortion to be applied to unscaled IWC -* coordinates, prior to performing the CDELT matrix multiplication. -* NULL is returned if no distortion is necessary. -* map4 -* Address of a location at which to store a pointer to a Mapping -* which describes any distortion to be applied to scaled IWC -* coordinates, after performing the CDELT matrix multiplication. -* NULL is returned if no distortion is necessary. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -*/ - -/* Local Variables: */ - AstMapping *tmap1; /* Mapping pointer */ - AstMapping *tmap2; /* Mapping pointer */ - char *ctype; /* Pointer to CTYPE value */ - char code[ 4 ]; /* Projection code extracted from CTYPE */ - char dist[ 4 ]; /* Distortion code extracted from CTYPE */ - char msgbuf[ 250 ]; /* Buffer for warning message */ - char type[ 5 ]; /* Axis type extracted from CTYPE */ - double *dim; /* Array holding array dimensions */ - int found_axes[ 2 ]; /* Index of axes with the distortion code */ - int i; /* FITS axis index */ - int nc; /* No. of characters in CTYPE without "-SIP" */ - int nfound; /* No. of axes with the distortion code */ - int warned; /* Have any ASTWARN cards been issued? */ - -/* Initialise pointers to the returned Mappings. */ - *map1 = NULL; - *map2 = NULL; - *map3 = NULL; - *map4 = NULL; - -/* Check the global status. */ - if ( !astOK ) return; - -/* Allocate memory to hold the image dimensions. */ - dim = (double *) astMalloc( sizeof(double)*naxes ); - if( dim ){ - -/* Note the image dimensions, if known. If not, store AST__BAD values. */ - for( i = 0; i < naxes; i++ ){ - if( !astGetFitsF( this, FormatKey( "NAXIS", i + 1, -1, ' ', status ), - dim + i ) ) dim[ i ] = AST__BAD; - } - -/* First check each known distortion type... */ - -/* "-SIP": Spitzer (http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf) - ============= */ - -/* Spitzer distortion is limited to 2D. Check the first two axes to see if - they have "-SIP" codes at the end of their CTYPE values. If they do, - terminate the ctype string in order to exclude the distortion code (this - is so that later functions do not need to allow for the possibility of a - distortion code being present in the CTYPE value). */ - ctype = GetItemC( &(store->ctype), 0, 0, s, NULL, method, class, status ); - if( ctype ){ - nc = astChrLen( ctype ) - 4; - if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) { - ctype[ nc ] = 0; - ctype = GetItemC( &(store->ctype), 1, 0, s, NULL, method, class, status ); - if( ctype ) { - nc = astChrLen( ctype ) - 4; - if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) { - ctype[ nc ] = 0; - -/* Create a Mapping describing the distortion (other axes are passed - unchanged by this Mapping), and add it in series with the returned map2 - (Spitzer distortion is applied to the translated pixel coordinates). */ - tmap1 = SIPMapping( this, dim, store, s, naxes, method, class, status ); - if( ! *map2 ) { - *map2 = tmap1; - } else { - tmap2 = (AstMapping *) astCmpMap( *map2, tmap1, 1, "", status ); - *map2 = astAnnul( *map2 ); - tmap1 = astAnnul( tmap1 ); - *map2 = tmap2; - } - } - } - } - } - -/* Check that the "-SIP" code is not included in any axes other than axes - 0 and 1. Issue a warning if it is, and remove it. */ - warned = 0; - for( i = 2; i < naxes; i++ ){ - ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - if( ctype ){ - nc = astChrLen( ctype ) - 4; - if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) { - if( !warned ){ - warned = 1; - sprintf( msgbuf, "The \"-SIP\" distortion code can only be " - "used on axes 1 and 2, but was found in keyword " - "%s (='%s'). The distortion will be ignored.", - FormatKey( "CTYPE", i + 1, -1, ' ', status ), ctype ); - Warn( this, "distortion", msgbuf, method, class, status ); - } - ctype[ nc ] = 0; - } - } - } - -/* "-ZPX": IRAF (http://iraf.noao.edu/projects/ccdmosaic/zpx.html) - ============= */ - -/* An IRAF ZPX header uses a ZPX projection within each CTYPE value in place - of the basic ZPN projection. The SpecTrans function converts -ZPX" to - "-ZPN-ZPX" (i.e. a basic projection of ZPN with a distortion code of - "-ZPX"). This function then traps and processes the "-ZPX" distortion - code. */ - -/* Look for axes that have the "-ZPX" code in their CTYPE values. If any - are found, check that there are exactly two such axes, and terminate the - ctype strings in order to exclude the distortion code (this is so that - later functions do not need to allow for the possibility of a distortion - code being present in the CTYPE value)*/ - nfound = 0; - for( i = 0; i < naxes; i++ ){ - ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - if( ctype && 3 == astSscanf( ctype, "%4s-%3s-%3s", type, code, dist ) ){ - if( !strcmp( "ZPX", dist ) ){ - if( nfound < 2 ) found_axes[ nfound ] = i; - nfound++; - ctype[ 8 ] = 0; - } - } - } - -/* Issue a warning if more than two ZPX axes were found. */ - if( nfound > 2 ) { - Warn( this, "distortion", "More than two axes were found " - "with the \"-ZPX\" projection code. A ZPN projection " - "will be used instead.", method, class, status ); - -/* Otherwise, create a Mapping describing the distortion (other axes are passed - unchanged by this Mapping), and add it in series with the returned map4 - (ZPX distortion is applied to the translated, rotated, scaled IWC - coordinates). */ - } else if( nfound == 2 ){ - tmap1 = ZPXMapping( this, store, s, naxes, found_axes, method, - class, status ); - if( ! *map4 ) { - *map4 = tmap1; - } else { - tmap2 = (AstMapping *) astCmpMap( *map4, tmap1, 1, "", status ); - *map4 = astAnnul( *map4 ); - tmap1 = astAnnul( tmap1 ); - *map4 = tmap2; - } - } - -/* (There are currently no other supported distortion codes.) */ - -/* Finally, check all axes looking for any remaining (and therefore - unsupported) distortion codes. Issue a warning about them and remove - them. - =================================================================== */ - -/* Indicate that we have not yet issued a warning. */ - warned = 0; - -/* Do each IWC axis. */ - for( i = 0; i < naxes; i++ ){ - -/* Get the CTYPE value for this axis. */ - ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - if( ctype ) { - -/* See if has the "4-3-3" form described in FITS-WCS paper IV. */ - if( 3 == astSscanf( ctype, "%4s-%3s-%3s", type, code, dist ) ){ - -/* Add an ASTWARN card to the FitsChan. Only issue one warning (this avoids - multiple warnings about the same distortion code in multiple CTYPE values). */ - if( !warned ){ - warned = 1; - sprintf( msgbuf, "The header contains CTYPE values (e.g. " - "%s = '%s') which " - "include a distortion code \"-%s\". AST " - "currently ignores this distortion. The code " - "has been removed from the CTYPE values.", - FormatKey( "CTYPE", i + 1, -1, ' ', status ), ctype, dist ); - Warn( this, "distortion", msgbuf, method, class, status ); - } - -/* Terminate the CTYPE value in the FitsStore in order to exclude the distortion - code. This means that later functions will not need to take account of - distortion codes. */ - ctype[ 8 ] = 0; - } - } - } - } - -/* Free resources. */ - dim = astFree( dim ); -} - -static void DSBSetUp( AstFitsChan *this, FitsStore *store, - AstDSBSpecFrame *dsb, char s, double crval, - const char *method, const char *class, int *status ){ - -/* -* Name: -* DSBSetUp - -* Purpose: -* Modify an AstDSBSpecFrame object to reflect the contents of a FitsStore. - -* Type: -* Private function. - -* Synopsis: - -* void DSBSetUp( AstFitsChan *this, FitsStore *store, -* AstDSBSpecFrame *dsb, char s, double crval, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function sets the attributes of the supplied DSBSpecFrame to -* reflect the values in the supplied FitsStore. - -* Parameters: -* this -* The FitsChan. -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* dsb -* Pointer to the DSBSpecFrame. -* s -* Alternate axis code. -* crval -* The spectral CRVAL value, in the spectral system represented by -* the supplied DSBSPecFrame. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Notes: -* - This implementation follows the conventions of the FITS-CLASS encoding. -*/ - -/* Local Variables: */ - AstDSBSpecFrame *dsb_src; /* New DSBSpecFrame in which StdOfRest is source */ - AstDSBSpecFrame *dsb_topo;/* New DSBSpecFrame in which StdOfRest is topo */ - AstFrameSet *fs; /* FrameSet connecting two standards of rest */ - double dsbcentre; /* Topocentric reference (CRVAL) frequency */ - double in[2]; /* Source rest and image frequencies */ - double lo; /* Topocentric Local Oscillator frequency */ - double out[2]; /* Topocentric rest and image frequencies */ - -/* Check the global status. */ - if ( !astOK ) return; - -/* In order to determine the topocentric IF, we need the topocentric - frequencies corresponding to the RESTFREQ and IMAGFREQ values in the - FITS header. The values stored in the FITS header are measured in Hz, - in the source's rest frame, so we need a mapping from frequency in the - source rest frame to topocentric frequency. Take a copy of the supplied - DSBSpecFrame and then set its attributes to represent frequency in the - sources rest frame. */ - dsb_src = astCopy( dsb ); - astSetStdOfRest( dsb_src, AST__SCSOR ); - astSetSystem( dsb_src, AST__FREQ ); - astSetUnit( dsb_src, 0, "Hz" ); - -/* Take a copy of this DSBSpecFrame and set its standard of rest to - topocentric. */ - dsb_topo = astCopy( dsb_src ); - astSetStdOfRest( dsb_topo, AST__TPSOR ); - -/* Now get the Mapping between these. */ - fs = astConvert( dsb_src, dsb_topo, "" ); - dsb_src = astAnnul( dsb_src ); - dsb_topo = astAnnul( dsb_topo ); - -/* Check a conversion was found. */ - if( fs != NULL ) { - -/* Use this Mapping to transform the rest frequency and the image - frequency from the standard of rest of the source to that of the - observer. */ - in[ 0 ] = astGetRestFreq( dsb ); - in[ 1 ] = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status ); - astTran1( fs, 2, in, 1, out ); - -/* The intermediate frequency is half the distance between these two - frequencies. Note, the IF value is signed so as to put the rest - frequency in the observed sideband. */ - if( out[ 0 ] != AST__BAD && out[ 1 ] != AST__BAD ) { - -/* Store the spectral CRVAL value as the centre frequency of the - DSBSpecFrame. The public astSetD method interprets the supplied value - as a value in the spectral system described by the other SpecFrame - attributes. */ - astSetD( dsb, "DSBCentre", crval ); - -/* To calculate the topocentric IF we need the topocentric frequency - equivalent of CRVAL. So take a copy of the DSBSpecFrame, then set it to - represent topocentric frequency, and read back the DSBCentre value. */ - dsb_topo = astCopy( dsb ); - astSetStdOfRest( dsb_topo, AST__TPSOR ); - astSetSystem( dsb_topo, AST__FREQ ); - astSetUnit( dsb_topo, 0, "Hz" ); - dsbcentre = astGetD( dsb_topo, "DSBCentre" ); - dsb_topo = astAnnul( dsb_topo ); - -/* We also need the topocentric Local Oscillator frequency. This is - assumed to be half way between the topocentric IMAGFREQ and RESTFREQ - values. */ - lo = 0.5*( out[ 1 ] + out[ 0 ] ); - -/* Set the IF to be the difference between the Local Oscillator frequency - and the CRVAL frequency. */ - astSetIF( dsb, lo - dsbcentre ); - -/* Set the DSBSpecFrame to represent the observed sideband */ - astSetC( dsb, "SideBand", "observed" ); - } - -/* Free resources. */ - fs = astAnnul( fs ); - } -} - -static int DSSFromStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ){ - -/* -* Name: -* DSSFromStore - -* Purpose: -* Store WCS keywords in a FitsChan using DSS encoding. - -* Type: -* Private function. - -* Synopsis: - -* int DSSFromStore( AstFitsChan *this, FitsStore *store, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function copies the WCS information stored in the supplied -* FitsStore into the supplied FitsChan, using DSS encoding. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if succesfull, and zero is returned -* otherwise. -*/ - -/* Local Variables: */ - const char *comm; /* Pointer to comment string */ - char *cval; /* Pointer to string keyword value */ - const char *pltdecsn;/* PLTDECSN keyword value */ - double amdx[20]; /* AMDXi keyword value */ - double amdy[20]; /* AMDYi keyword value */ - double cdelt; /* CDELT element */ - double cnpix1; /* CNPIX1 keyword value */ - double cnpix2; /* CNPIX2 keyword value */ - double pc; /* PC element */ - double pltdecd; /* PLTDECD keyword value */ - double pltdecm; /* PLTDECM keyword value */ - double pltdecs; /* PLTDECS keyword value */ - double pltrah; /* PLTRAH keyword value */ - double pltram; /* PLTRAM keyword value */ - double pltras; /* PLTRAS keyword value */ - double pltscl; /* PLTSCL keyword value */ - double ppo1; /* PPO1 keyword value */ - double ppo2; /* PPO2 keyword value */ - double ppo3; /* PPO3 keyword value */ - double ppo4; /* PPO4 keyword value */ - double ppo5; /* PPO5 keyword value */ - double ppo6; /* PPO6 keyword value */ - double pvx[22]; /* X projection parameter values */ - double pvy[22]; /* Y projection parameter values */ - double val; /* General purpose value */ - double xpixelsz; /* XPIXELSZ keyword value */ - double ypixelsz; /* YPIXELSZ keyword value */ - int i; /* Loop count */ - int gottpn; /* Is the projection a "TPN" projection? */ - int m; /* Parameter index */ - int ret; /* Returned value. */ - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Check the image is 2 dimensional. */ - if( GetMaxJM( &(store->crpix), ' ', status ) != 1 ) return ret; - -/* Check the first axis is RA with a TAN or TPN projection. */ - cval = GetItemC( &(store->ctype), 0, 0, ' ', NULL, method, class, status ); - if( !cval ) return ret; - gottpn = !strcmp( "RA---TPN", cval ); - if( strcmp( "RA---TAN", cval ) && !gottpn ) return ret; - -/* Check the second axis is DEC with a TAN or TPN projection. */ - cval = GetItemC( &(store->ctype), 1, 0, ' ', NULL, method, class, status ); - if( !cval ) return ret; - if( gottpn ) { - if( strcmp( "DEC--TPN", cval ) ) return ret; - } else { - if( strcmp( "DEC--TAN", cval ) ) return ret; - } - -/* Check that LONPOLE is undefined or is 180 degrees. */ - val = GetItem( &(store->lonpole), 0, 0, ' ', NULL, method, class, status ); - if( val != AST__BAD && val != 180.0 ) return ret; - -/* Check that the RA/DEC system is FK5. */ - cval = GetItemC( &(store->radesys), 0, 0, ' ', NULL, method, class, status ); - if( !cval || strcmp( "FK5", cval ) ) return ret; - -/* Check that equinox is not defined or is 2000.0 */ - val = GetItem( &(store->equinox), 0, 0, ' ', NULL, method, class, status ); - if( val != AST__BAD && val != 2000.0 ) return ret; - -/* Get the pixel sizes from the PC/CDELT keywords. They must be defined and - not be zero. */ - cdelt = GetItem( &(store->cdelt), 0, 0, ' ', NULL, method, class, status ); - if( cdelt == AST__BAD ) return ret; - pc = GetItem( &(store->pc), 0, 0, ' ', NULL, method, class, status ); - if( pc == AST__BAD ) pc = 1.0; - xpixelsz = cdelt*pc; - cdelt = GetItem( &(store->cdelt), 1, 0, ' ', NULL, method, class, status ); - if( cdelt == AST__BAD ) return ret; - pc = GetItem( &(store->pc), 1, 1, ' ', NULL, method, class, status ); - if( pc == AST__BAD ) pc = 1.0; - ypixelsz = cdelt*pc; - if( xpixelsz == 0.0 || ypixelsz == 0.0 ) return ret; - xpixelsz *= -1000.0; - ypixelsz *= 1000.0; - -/* Check the off-diagonal PC terms are zero. DSS does not allow any rotation. */ - val = GetItem( &(store->pc), 0, 1, ' ', NULL, method, class, status ); - if( val != AST__BAD && val != 0.0 ) return ret; - val = GetItem( &(store->pc), 1, 0, ' ', NULL, method, class, status ); - if( val != AST__BAD && val != 0.0 ) return ret; - -/* Get the required projection parameter values from the store, supplying - appropriate values if a simple TAN projection is being used. */ - for( m = 0; m < 22; m++ ){ - pvx[ m ] = GetItem( &(store->pv), 0, m, ' ', NULL, method, class, status ); - if( pvx[ m ] == AST__BAD || !gottpn ) pvx[ m ] = ( m == 1 ) ? 1.0 : 0.0; - pvy[ m ] = GetItem( &(store->pv), 1, m, ' ', NULL, method, class, status ); - if( pvy[ m ] == AST__BAD || !gottpn ) pvy[ m ] = ( m == 1 ) ? 1.0 : 0.0; - } - -/* Check that no other projection parameters have been set. */ - if( GetMaxJM( &(store->pv), ' ', status ) > 21 ) return ret; - -/* Check that specific parameters take their required zero value. */ - if( pvx[ 3 ] != 0.0 || pvy[ 3 ] != 0.0 ) return ret; - for( m = 11; m < 17; m++ ){ - if( pvx[ m ] != 0.0 || pvy[ m ] != 0.0 ) return ret; - } - if( pvx[ 18 ] != 0.0 || pvy[ 18 ] != 0.0 ) return ret; - if( pvx[ 20 ] != 0.0 || pvy[ 20 ] != 0.0 ) return ret; - -/* Check that other projection parameters are related correctly. */ - if( !astEQUAL( 2*pvx[ 17 ], pvx[ 19 ] ) ) return ret; - if( !astEQUAL( pvx[ 17 ], pvx[ 21 ] ) ) return ret; - if( !astEQUAL( 2*pvy[ 17 ], pvy[ 19 ] ) ) return ret; - if( !astEQUAL( pvy[ 17 ], pvy[ 21 ] ) ) return ret; - -/* Initialise all polynomial co-efficients to zero. */ - for( m = 0; m < 20; m++ ){ - amdx[ m ] = 0.0; - amdy[ m ] = 0.0; - } - -/* Polynomial co-efficients. There is redundancy here too, so we - arbitrarily choose to leave AMDX/Y7 and AMDX/Y12 set to zero. */ - amdx[ 0 ] = 3600.0*pvx[ 1 ]; - amdx[ 1 ] = 3600.0*pvx[ 2 ]; - amdx[ 2 ] = 3600.0*pvx[ 0 ]; - amdx[ 3 ] = 3600.0*pvx[ 4 ]; - amdx[ 4 ] = 3600.0*pvx[ 5 ]; - amdx[ 5 ] = 3600.0*pvx[ 6 ]; - amdx[ 7 ] = 3600.0*pvx[ 7 ]; - amdx[ 8 ] = 3600.0*pvx[ 8 ]; - amdx[ 9 ] = 3600.0*pvx[ 9 ]; - amdx[ 10 ] = 3600.0*pvx[ 10 ]; - amdx[ 12 ] = 3600.0*pvx[ 17 ]; - amdy[ 0 ] = 3600.0*pvy[ 1 ]; - amdy[ 1 ] = 3600.0*pvy[ 2 ]; - amdy[ 2 ] = 3600.0*pvy[ 0 ]; - amdy[ 3 ] = 3600.0*pvy[ 4 ]; - amdy[ 4 ] = 3600.0*pvy[ 5 ]; - amdy[ 5 ] = 3600.0*pvy[ 6 ]; - amdy[ 7 ] = 3600.0*pvy[ 7 ]; - amdy[ 8 ] = 3600.0*pvy[ 8 ]; - amdy[ 9 ] = 3600.0*pvy[ 9 ]; - amdy[ 10 ] = 3600.0*pvy[ 10 ]; - amdy[ 12 ] = 3600.0*pvy[ 17 ]; - -/* The plate scale is the mean of the first X and Y co-efficients. */ - pltscl = 0.5*( amdx[ 0 ] + amdy[ 0 ] ); - -/* There is redundancy in the DSS encoding. We can choose an arbitrary - pixel corner (CNPIX1, CNPIX2) so long as we use the corresponding origin - for the cartesian co-ordinate system in which the plate centre is - specified (PPO3, PPO6). Arbitrarily set CNPIX1 and CNPIX2 to one. */ - cnpix1 = 1.0; - cnpix2 = 1.0; - -/* Find the corresponding plate centre PPO3 and PPO6 (other co-efficients - are set to zero). */ - ppo1 = 0.0; - ppo2 = 0.0; - val = GetItem( &(store->crpix), 0, 0, ' ', NULL, method, class, status ); - if( val == AST__BAD ) return ret; - ppo3 = xpixelsz*( val + cnpix1 - 0.5 ); - ppo4 = 0.0; - ppo5 = 0.0; - val = GetItem( &(store->crpix), 0, 1, ' ', NULL, method, class, status ); - if( val == AST__BAD ) return ret; - ppo6 = ypixelsz*( val + cnpix2 - 0.5 ); - -/* The reference RA. Get it in degrees. */ - val = GetItem( &(store->crval), 0, 0, ' ', NULL, method, class, status ); - if( val == AST__BAD ) return ret; - -/* Convert to hours and ensure it is in the range 0 to 24 */ - val /= 15.0; - while( val < 0 ) val += 24.0; - while( val >= 24.0 ) val -= 24.0; - -/* Split into hours, mins and seconds. */ - pltrah = (int) val; - val = 60.0*( val - pltrah ); - pltram = (int) val; - pltras = 60.0*( val - pltram ); - -/* The reference DEC. Get it in degrees. */ - val = GetItem( &(store->crval), 1, 0, ' ', NULL, method, class, status ); - if( val == AST__BAD ) return ret; - -/* Ensure it is in the range -180 to +180 */ - while( val < -180.0 ) val += 360.0; - while( val >= 180.0 ) val -= 360.0; - -/* Save the sign. */ - if( val > 0.0 ){ - pltdecsn = "+"; - } else { - pltdecsn = "-"; - val = -val; - } - -/* Split into degrees, mins and seconds. */ - pltdecd = (int) val; - val = 60.0*( val - pltdecd ); - pltdecm = (int) val; - pltdecs = 60.0*( val - pltdecm ); - -/* Store the DSS keywords in the FitsChan. */ - SetValue( this, "CNPIX1", &cnpix1, AST__FLOAT, "X corner (pixels)", status ); - SetValue( this, "CNPIX2", &cnpix2, AST__FLOAT, "Y corner (pixels)", status ); - SetValue( this, "PPO1", &ppo1, AST__FLOAT, "Orientation co-efficients", status ); - SetValue( this, "PPO2", &ppo2, AST__FLOAT, "", status ); - SetValue( this, "PPO3", &ppo3, AST__FLOAT, "", status ); - SetValue( this, "PPO4", &ppo4, AST__FLOAT, "", status ); - SetValue( this, "PPO5", &ppo5, AST__FLOAT, "", status ); - SetValue( this, "PPO6", &ppo6, AST__FLOAT, "", status ); - SetValue( this, "XPIXELSZ", &xpixelsz, AST__FLOAT, "X pixel size (microns)", status ); - SetValue( this, "YPIXELSZ", &ypixelsz, AST__FLOAT, "Y pixel size (microns)", status ); - SetValue( this, "PLTRAH", &pltrah, AST__FLOAT, "RA at plate centre", status ); - SetValue( this, "PLTRAM", &pltram, AST__FLOAT, "", status ); - SetValue( this, "PLTRAS", &pltras, AST__FLOAT, "", status ); - SetValue( this, "PLTDECD", &pltdecd, AST__FLOAT, "DEC at plate centre", status ); - SetValue( this, "PLTDECM", &pltdecm, AST__FLOAT, "", status ); - SetValue( this, "PLTDECS", &pltdecs, AST__FLOAT, "", status ); - SetValue( this, "PLTDECSN", &pltdecsn, AST__STRING, "", status ); - SetValue( this, "PLTSCALE", &pltscl, AST__FLOAT, "Plate scale (arcsec/mm)", status ); - comm = "Plate solution x co-efficients"; - for( i = 0; i < 20; i++ ){ - SetValue( this, FormatKey( "AMDX", i + 1, -1, ' ', status ), amdx + i, - AST__FLOAT, comm, status ); - comm = NULL; - } - comm = "Plate solution y co-efficients"; - for( i = 0; i < 20; i++ ){ - SetValue( this, FormatKey( "AMDY", i + 1, -1, ' ', status ), amdy + i, - AST__FLOAT, comm, status ); - comm = NULL; - } - -/* If no error has occurred, return one. */ - if( astOK ) ret = 1; - -/* Return the answer. */ - return ret; -} - -static void DSSToStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ){ - -/* -* Name: -* DSSToStore - -* Purpose: -* Extract WCS information from the supplied FitsChan using a DSS -* encoding, and store it in the supplied FitsStore. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void DSSToStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function extracts DSS keywords from the supplied FitsChan, and -* stores the corresponding WCS information in the supplied FitsStore. -* The conversion from DSS encoding to standard WCS encoding is -* described in an ear;y draft of the Calabretta & Greisen paper -* "Representations of celestial coordinates in FITS" (A&A, in prep.), -* and uses the now deprecated "TAN with polynomial corrections", -* which is still supported by the WcsMap class as type AST__TPN. -* Here we use "lambda=1" (i.e. plate co-ordinate are measured in mm, -* not degrees). -* -* It is assumed that DSS images are 2 dimensional. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore structure. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - char *text; /* Pointer to textual keyword value */ - char pltdecsn[11]; /* First 10 non-blank characters from PLTDECSN keyword */ - char keyname[10]; /* Buffer for keyword name */ - double amdx[20]; /* AMDXi keyword value */ - double amdy[20]; /* AMDYi keyword value */ - double cnpix1; /* CNPIX1 keyword value */ - double cnpix2; /* CNPIX2 keyword value */ - double crval2; /* Equivalent CRVAL2 keyword value */ - double dummy; /* Unused keyword value */ - double pltdecd; /* PLTDECD keyword value */ - double pltdecm; /* PLTDECM keyword value */ - double pltdecs; /* PLTDECS keyword value */ - double pltrah; /* PLTRAH keyword value */ - double pltram; /* PLTRAM keyword value */ - double pltras; /* PLTRAS keyword value */ - double ppo3; /* PPO3 keyword value */ - double ppo6; /* PPO6 keyword value */ - double pv; /* Projection parameter value */ - double xpixelsz; /* XPIXELSZ keyword value */ - double ypixelsz; /* YPIXELSZ keyword value */ - int i; /* Loop count */ - -/* Check the inherited status. */ - if( !astOK ) return; - -/* Get the optional DSS keywords, supplying defaults for any missing keywords. */ - cnpix1 = 0.0; - cnpix2 = 0.0; - GetValue( this, "CNPIX1", AST__FLOAT, &cnpix1, 0, 1, method, class, status ); - GetValue( this, "CNPIX2", AST__FLOAT, &cnpix2, 0, 1, method, class, status ); - -/* Get the required DSS keywords. Report an error if any are missing. */ - GetValue( this, "PPO3", AST__FLOAT, &ppo3, 1, 1, method, class, status ); - GetValue( this, "PPO6", AST__FLOAT, &ppo6, 1, 1, method, class, status ); - GetValue( this, "XPIXELSZ", AST__FLOAT, &xpixelsz, 1, 1, method, class, status ); - GetValue( this, "YPIXELSZ", AST__FLOAT, &ypixelsz, 1, 1, method, class, status ); - GetValue( this, "PLTRAH", AST__FLOAT, &pltrah, 1, 1, method, class, status ); - GetValue( this, "PLTRAM", AST__FLOAT, &pltram, 1, 1, method, class, status ); - GetValue( this, "PLTRAS", AST__FLOAT, &pltras, 1, 1, method, class, status ); - GetValue( this, "PLTDECD", AST__FLOAT, &pltdecd, 1, 1, method, class, status ); - GetValue( this, "PLTDECM", AST__FLOAT, &pltdecm, 1, 1, method, class, status ); - GetValue( this, "PLTDECS", AST__FLOAT, &pltdecs, 1, 1, method, class, status ); - -/* Copy the first 10 non-blank characters from the PLTDECSN keyword. */ - GetValue( this, "PLTDECSN", AST__STRING, &text, 1, 1, method, class, status ); - if( astOK ) { - text += strspn( text, " " ); - text[ strcspn( text, " " ) ] = 0; - strncpy( pltdecsn, text, 10 ); - } - -/* Read other related keywords. We do not need these, but we read them - so that they are not propagated to any output FITS file. */ - GetValue( this, "PLTSCALE", AST__FLOAT, &dummy, 0, 1, method, class, status ); - GetValue( this, "PPO1", AST__FLOAT, &dummy, 0, 1, method, class, status ); - GetValue( this, "PPO2", AST__FLOAT, &dummy, 0, 1, method, class, status ); - GetValue( this, "PPO4", AST__FLOAT, &dummy, 0, 1, method, class, status ); - GetValue( this, "PPO5", AST__FLOAT, &dummy, 0, 1, method, class, status ); - -/* Get the polynomial co-efficients. These can be defaulted if they are - missing, so do not report an error. */ - for( i = 0; i < 20; i++ ){ - (void) sprintf( keyname, "AMDX%d", i + 1 ); - amdx[i] = AST__BAD; - GetValue( this, keyname, AST__FLOAT, amdx + i, 0, 1, method, class, status ); - (void) sprintf( keyname, "AMDY%d", i + 1 ); - amdy[i] = AST__BAD; - GetValue( this, keyname, AST__FLOAT, amdy + i, 0, 1, method, class, status ); - } - -/* Check the above went OK. */ - if( astOK ) { - -/* Calculate and store the equivalent PV projection parameters. */ - if( amdx[2] != AST__BAD ) { - pv = amdx[2]/3600.0; - SetItem( &(store->pv), 0, 0, ' ', pv, status ); - } - if( amdx[0] != AST__BAD ) { - pv = amdx[0]/3600.0; - SetItem( &(store->pv), 0, 1, ' ', pv, status ); - } - if( amdx[1] != AST__BAD ) { - pv = amdx[1]/3600.0; - SetItem( &(store->pv), 0, 2, ' ', pv, status ); - } - if( amdx[3] != AST__BAD && amdx[6] != AST__BAD ) { - pv = ( amdx[3] + amdx[6] )/3600.0; - SetItem( &(store->pv), 0, 4, ' ', pv, status ); - } - if( amdx[4] != AST__BAD ) { - pv = amdx[4]/3600.0; - SetItem( &(store->pv), 0, 5, ' ', pv, status ); - } - if( amdx[5] != AST__BAD && amdx[6] != AST__BAD ) { - pv = ( amdx[5] + amdx[6] )/3600.0; - SetItem( &(store->pv), 0, 6, ' ', pv, status ); - } - if( amdx[7] != AST__BAD && amdx[11] != AST__BAD ) { - pv = ( amdx[7] + amdx[11] )/3600.0; - SetItem( &(store->pv), 0, 7, ' ', pv, status ); - } - if( amdx[8] != AST__BAD ) { - pv = amdx[8]/3600.0; - SetItem( &(store->pv), 0, 8, ' ', pv, status ); - } - if( amdx[9] != AST__BAD && amdx[11] != AST__BAD ) { - pv = ( amdx[9] + amdx[11] )/3600.0; - SetItem( &(store->pv), 0, 9, ' ', pv, status ); - } - if( amdx[10] != AST__BAD ) { - pv = amdx[10]/3600.0; - SetItem( &(store->pv), 0, 10, ' ', pv, status ); - } - if( amdx[12] != AST__BAD ) { - pv = amdx[12]/3600.0; - SetItem( &(store->pv), 0, 17, ' ', pv, status ); - SetItem( &(store->pv), 0, 19, ' ', 2*pv, status ); - SetItem( &(store->pv), 0, 21, ' ', pv, status ); - } - if( amdy[2] != AST__BAD ) { - pv = amdy[2]/3600.0; - SetItem( &(store->pv), 1, 0, ' ', pv, status ); - } - if( amdy[0] != AST__BAD ) { - pv = amdy[0]/3600.0; - SetItem( &(store->pv), 1, 1, ' ', pv, status ); - } - if( amdy[1] != AST__BAD ) { - pv = amdy[1]/3600.0; - SetItem( &(store->pv), 1, 2, ' ', pv, status ); - } - if( amdy[3] != AST__BAD && amdy[6] != AST__BAD ) { - pv = ( amdy[3] + amdy[6] )/3600.0; - SetItem( &(store->pv), 1, 4, ' ', pv, status ); - } - if( amdy[4] != AST__BAD ) { - pv = amdy[4]/3600.0; - SetItem( &(store->pv), 1, 5, ' ', pv, status ); - } - if( amdy[5] != AST__BAD && amdy[6] != AST__BAD ) { - pv = ( amdy[5] + amdy[6] )/3600.0; - SetItem( &(store->pv), 1, 6, ' ', pv, status ); - } - if( amdy[7] != AST__BAD && amdy[11] != AST__BAD ) { - pv = ( amdy[7] + amdy[11] )/3600.0; - SetItem( &(store->pv), 1, 7, ' ', pv, status ); - } - if( amdy[8] != AST__BAD ) { - pv = amdy[8]/3600.0; - SetItem( &(store->pv), 1, 8, ' ', pv, status ); - } - if( amdy[9] != AST__BAD && amdy[11] != AST__BAD ) { - pv = ( amdy[9] + amdy[11] )/3600.0; - SetItem( &(store->pv), 1, 9, ' ', pv, status ); - } - if( amdy[10] != AST__BAD ) { - pv = amdy[10]/3600.0; - SetItem( &(store->pv), 1, 10, ' ', pv, status ); - } - if( amdy[12] != AST__BAD ) { - pv = amdy[12]/3600.0; - SetItem( &(store->pv), 1, 17, ' ', pv, status ); - SetItem( &(store->pv), 1, 19, ' ', 2*pv, status ); - SetItem( &(store->pv), 1, 21, ' ', pv, status ); - } - -/* Calculate and store the equivalent CRPIX values. */ - if( xpixelsz != 0.0 ) { - SetItem( &(store->crpix), 0, 0, ' ', - ( ppo3/xpixelsz ) - cnpix1 + 0.5, status ); - } else if( astOK ){ - astError( AST__BDFTS, "%s(%s): FITS keyword XPIXELSZ has illegal " - "value 0.0", status, method, class ); - } - if( ypixelsz != 0.0 ) { - SetItem( &(store->crpix), 0, 1, ' ', - ( ppo6/ypixelsz ) - cnpix2 + 0.5, status ); - } else if( astOK ){ - astError( AST__BDFTS, "%s(%s): FITS keyword YPIXELSZ has illegal " - "value 0.0", status, method, class ); - } - -/* Calculate and store the equivalent CRVAL values. */ - SetItem( &(store->crval), 0, 0, ' ', - 15.0*( pltrah + pltram/60.0 + pltras/3600.0 ), status ); - crval2 = pltdecd + pltdecm/60.0 + pltdecs/3600.0; - if( !strcmp( pltdecsn, "-") ) crval2 = -crval2; - SetItem( &(store->crval), 1, 0, ' ', crval2, status ); - -/* Calculate and store the equivalent PC matrix. */ - SetItem( &(store->pc), 0, 0, ' ', -0.001*xpixelsz, status ); - SetItem( &(store->pc), 1, 1, ' ', 0.001*ypixelsz, status ); - -/* Set values of 1.0 for the CDELT values. */ - SetItem( &(store->cdelt), 0, 0, ' ', 1.0, status ); - SetItem( &(store->cdelt), 1, 0, ' ', 1.0, status ); - -/* Store remaining constant items */ - SetItem( &(store->lonpole), 0, 0, ' ', 180.0, status ); - SetItem( &(store->equinox), 0, 0, ' ', 2000.0, status ); - SetItemC( &(store->radesys), 0, 0, ' ', "FK5", status ); - SetItem( &(store->wcsaxes), 0, 0, ' ', 2.0, status ); - store->naxis = 2; - SetItemC( &(store->ctype), 0, 0, ' ', "RA---TPN", status ); - SetItemC( &(store->ctype), 1, 0, ' ', "DEC--TPN", status ); - } -} - -static void EmptyFits( AstFitsChan *this, int *status ){ - -/* -*++ -* Name: -c astEmptyFits -f AST_EMPTYFITS - -* Purpose: -* Delete all cards in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" -c void astEmptyFits( AstFitsChan *this ) -f CALL AST_EMPTYFITS( THIS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* deletes all cards and associated information from a FitsChan. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - This method simply deletes the cards currently in the FitsChan. -c Unlike astWriteFits, -f Unlike AST_WRITEFITS, -* they are not first written out to the sink function or sink file. -* - Any Tables or warnings stored in the FitsChan are also deleted. -* - This method attempt to execute even if an error has occurred -* previously. -*-- -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - const char *class; /* Pointer to string holding object class */ - const char *method; /* Pointer to string holding calling method */ - int old_ignore_used; /* Original setting of ignore_used variable */ - -/* Check a FitsChan was supplied. */ - if( !this ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Store the method and class strings. */ - method = "astEmpty"; - class = astGetClass( this ); - -/* Delete all cards from the circular linked list stored in the FitsChan, - starting with the card at the head of the list. */ - old_ignore_used = ignore_used; - ignore_used = 0; - astClearCard( this ); - while( !astFitsEof( this ) ) DeleteCard( this, method, class, status ); - ignore_used = old_ignore_used; - -/* Delete the KeyMap which holds keywords and the latest sequence number - used by each of them. */ - if( this->keyseq ) this->keyseq = astAnnul( this->keyseq ); - -/* Delete the KeyMap holding the keyword names. */ - if( this->keywords ) this->keywords = astAnnul( this->keywords ); - -/* Free any memory used to hold the Warnings attribute value. */ - this->warnings = astFree( this->warnings ); - -/* Other objects in the FitsChan structure. */ - if( this->tables ) this->tables = astAnnul( this->tables ); -} - -static int EncodeFloat( char *buf, int digits, int width, int maxwidth, - double value, int *status ){ -/* -* -* Name: -* EncodeFloat - -* Purpose: -* Formats a floating point value. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int EncodeFloat( char *buf, int digits, int width, int maxwidth, -* double value, int *status ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function formats the value using a G format specified in order -* to use the minimum field width (trailing zeros are not printed). -* However, the G specifier does not include a decimal point unless it -* is necessary. FITS requires that floating point values always include -* a decimal point, so this function inserts one, if necessary. - -* Parameters: -* buf -* A character string into which the value is written. -* digits -* The number of digits after the decimal point. If the supplied value -* is negative, the number of digits actually used may be reduced if -* the string would otherwise extend beyond the number of columns -* allowed by the FITS standard. If the value is positive, the -* specified number of digits are always produced, even if it means -* breaking the FITS standard. -* width -* The minimum field width to use. The value is right justified in -* this field width. -* maxwidth -* The maximum field width to use. A value of zero is returned if -* the maximum field width is exceeded. -* value -* The value to format. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The field width actually used, or zero if the value could not be -* formatted. This does not include the trailing null character. - -* Notes: -* - If there is room, a trailing zero is also added following the -* inserted decimal point. -*/ - -/* Local Variables: */ - char *c; - char *w, *r; - int i; - int ldigits; - int n; - int ret; - -/* Check the global error status. */ - if ( !astOK ) return 0; - -/* The supplied value of "digits" may be negative. Obtain the positive - value giving the initial number of decimal digits to use. */ - ldigits = ( digits > 0 ) ? digits : -digits; - -/* Loop until a suitably encoded value has been obtained. */ - while( 1 ){ - -/* Write the value into the buffer. Most are formatted with a G specifier. - This will result in values between -0.001 and -0.0001 being formatted - without an exponent, and thus occupying (ldigits+6) characters. With - an exponent, these values would be formatted in (ldigits+5) characters - thus saving one character. This is important because the default value - of ldigits is 15, resulting in 21 characters being used by the G - specifier. This is one more than the maximum allowed by the FITS - standard. Using an exponent instead would result in 20 characters - being used without any loss of precision, thus staying within the FITS - limit. Note, the precision used with the E specifier is one less than - with the G specifier because the digit to the left of the decimal place - is significant with the E specifier, and so we only need (ldigits-1) - significant digits to the right of the decimal point. */ - if( value > -0.001 && value < -0.0001 ) { - (void) sprintf( buf, "%*.*E", width, ldigits - 1, value ); - } else { - (void) sprintf( buf, "%*.*G", width, ldigits, value ); - } - -/* Check that the value zero is not encoded with a minus sign (e.g. "-0."). - This also rounds out long sequences of zeros or nines. */ - CheckZero( buf, value, width, status ); - -/* If the formatted value includes an exponent, it will have 2 digits. - If the exponent includes a leading zero, remove it. */ - if( ( w = strstr( buf, "E-0" ) ) ) { - w += 2; - } else if( ( w = strstr( buf, "E+0" ) ) ){ - w += 2; - } else if( ( w = strstr( buf, "E0" ) ) ){ - w += 1; - } - -/* If a leading zero was found, shuffle everything down from the start of - the string by one character, over-writing the redundant zero, and insert - a space at the start of the string. */ - if( w ) { - r = w - 1 ; - while( w != buf ) *(w--) = *(r--); - *w = ' '; - } - -/* If the used field width was too large, reduce it and try again, so - long as we are allowed to change the number of digits being used. */ - ret = strlen( buf ); - if( ret > width && digits < 0 ){ - ldigits -= ( ret - width ); - -/* Otherwise leave the loop. Return zero field width if the maximum field - width was exceeded. */ - } else { - if( ret > maxwidth ) ret = 0; - break; - } - } - -/* If a formatted value was obtained, we need to ensure that the it includes - a decimal point. */ - if( ret ){ - -/* Get a pointer to the first digit in the buffer. */ - c = strpbrk( buf, "0123456789" ); - -/* Something funny is going on if there are no digits in the buffer, - so return a zero field width. */ - if( !c ){ - ret = 0; - -/* Otherwise... */ - } else { - -/* Find the number of digits following and including the first digit. */ - n = strspn( c, "0123456789" ); - -/* If the first non-digit character is a decimal point, do nothing. */ - if( c[ n ] != '.' ){ - -/* If there are two or more leading spaces, move the start of the string - two character to the left, and insert ".0" in the gap created. This - keeps the field right justified within the desired field width. */ - if( buf[ 0 ] == ' ' && buf[ 1 ] == ' ' ){ - for( i = 2; i < c - buf + n; i++ ) buf[ i - 2 ] = buf[ i ]; - c[ n - 2 ] = '.'; - c[ n - 1 ] = '0'; - -/* If there is just one leading space, move the start of the string - one character to the left, and insert "." in the gap created. This - keeps the field right justified within the desired field width. */ - } else if( buf[ 0 ] == ' ' ){ - for( i = 0; i < n; i++ ) c[ i - 1 ] = c[ i ]; - c[ n - 1 ] = '.'; - -/* If there are no leading spaces we need to move the end of the string - to the right. This will result in the string no longer being right - justified in the required field width. Return zero if there is - insufficient room for an extra character. */ - } else { - ret++; - if( ret > maxwidth ){ - ret = 0; - -/* Otherwise, more the end of the string one place to the right and insert - the decimal point. */ - } else { - for( i = strlen( c ); i >= n; i-- ) c[ i + 1 ] = c[ i ]; - c[ n ] = '.'; - } - } - } - } - } - -/* Return the field width. */ - return ret; -} - -static int EncodeValue( AstFitsChan *this, char *buf, int col, int digits, - const char *method, int *status ){ - -/* -* Name: -* EncodeValue - -* Purpose: -* Encode the current card's keyword value into a string. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int EncodeValue( AstFitsChan *this, char *buf, int col, int digits, -* const char *method, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function encodes the keyword value defined in the current card -* of the supplied FitsChan and stores it at the start of the supplied -* buffer. The number of characters placed in the buffer is returned -* (not including a terminating null). - -* Parameters: -* this -* Pointer to the FitsChan. -* buf -* The buffer to receive the formatted value. This should be at least -* 70 characters long. -* col -* The column number within the FITS header card corresponding to the -* start of "buf". -* digits -* The number of digits to use when formatting floating point -* values. If the supplied value is negative, the number of digits -* actually used may be reduced if the string would otherwise extend -* beyond the number of columns allowed by the FITS standard. If the -* value is positive, the specified number of digits are always -* produced, even if it means breaking the FITS standard. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The number of columns used by the encoded value. - -* Notes: -* - The function returns 0 if an error has already occurred -* or if an error occurs for any reason within this function. -*/ - -/* Local Variables: */ - char *c; /* Pointer to next character */ - char *name; /* Pointer to the keyword name */ - double dval; /* Keyword value */ - void *data; /* Pointer to keyword value */ - int i; /* Loop count */ - int ilen; /* Length of imaginary part */ - int len; /* Returned length */ - int quote; /* Quote character found? */ - int rlen; /* Length of real part */ - int type; /* Data type for keyword in current card */ - -/* Check the global status. */ - if( !astOK ) return 0; - -/* Initialise returned length. */ - len = 0; - -/* Get the data type of the keyword. */ - type = CardType( this, status ); - -/* Get a pointer to the data value in the current card. */ - data = CardData( this, NULL, status ); - -/* Return if there is no defined value associated with the keyword in the - current card. */ - if( type != AST__UNDEF ) { - -/* Get the name of the keyword. */ - name = CardName( this, status ); - -/* Go through each supported data type (roughly in the order of - decreasing usage)... */ - -/* AST__FLOAT - stored internally in a variable of type "double". Right - justified to column 30 in the header card. */ - if( type == AST__FLOAT ){ - dval = *( (double *) data ); - len = EncodeFloat( buf, digits, FITSRLCOL - FITSNAMLEN - 2, - AST__FITSCHAN_FITSCARDLEN - col + 1, dval, status ); - if( len <= 0 && astOK ) { - astError( AST__BDFTS, "%s(%s): Cannot encode floating point value " - "%g into a FITS header card for keyword '%s'.", status, method, - astGetClass( this ), dval, name ); - } - -/* AST__STRING & AST__CONTINUE - stored internally in a null terminated array of - type "char". The encoded string is enclosed in single quotes, starting - at FITS column 11 and ending in at least column 20. Single quotes - in the string are replaced by two adjacent single quotes. */ - } else if( type == AST__STRING || type == AST__CONTINUE ){ - c = (char *) data; - -/* Enter the opening quote. */ - len = 0; - buf[ len++ ] = '\''; - -/* Inspect each character, looking for quotes. */ - for ( i = 0; c[ i ]; ) { - quote = ( c[ i ] == '\'' ); - -/* If it will not fit into the header card (allowing for doubled - quotes), give up here. */ - if ( len + ( quote ? 2 : 1 ) > AST__FITSCHAN_FITSCARDLEN - col ) break; - -/* Otherwise, copy it into the output buffer and double any quotes. */ - buf[ len++ ] = c[ i ]; - if ( quote ) buf[ len++ ] = '\''; - -/* Look at the next character. */ - i++; - } - -/* Pad the string out to the required minimum length with blanks and - add the final quote. */ - while( len < FITSSTCOL - col ) buf[ len++ ] = ' '; - buf[ len++ ] = '\''; - -/* Inspect any characters that weren't used. If any are non-blank, - report an error. */ - for ( ; c[ i ]; i++ ) { - if ( !isspace( c[ i ] ) ) { - astError( AST__BDFTS, - "%s(%s): Cannot encode string '%s' into a FITS " - "header card for keyword '%s'.", status, method, astGetClass( this ), - (char *) data, name ); - break; - } - } - -/* INTEGER - stored internally in a variable of type "int". Right justified - to column 30 in the header card. */ - } else if( type == AST__INT ){ - len = sprintf( buf, "%*d", FITSRLCOL - col + 1, - *( (int *) data ) ); - if( len < 0 || len > AST__FITSCHAN_FITSCARDLEN - col ) { - astError( AST__BDFTS, "%s(%s): Cannot encode integer value %d into a " - "FITS header card for keyword '%s'.", status, method, astGetClass( this ), - *( (int *) data ), name ); - } - -/* LOGICAL - stored internally in a variable of type "int". Represented by - a "T" or "F" in column 30 of the FITS header card. */ - } else if( type == AST__LOGICAL ){ - for( i = 0; i < FITSRLCOL - col; i++ ) buf[ i ] = ' '; - if( *( (int *) data ) ){ - buf[ FITSRLCOL - col ] = 'T'; - } else { - buf[ FITSRLCOL - col ] = 'F'; - } - len = FITSRLCOL - col + 1; - -/* AST__COMPLEXF - stored internally in an array of two "doubles". The real - part is right justified to FITS column 30. The imaginary part is right - justified to FITS column 50. */ - } else if( type == AST__COMPLEXF ){ - dval = ( (double *) data )[ 0 ]; - rlen = EncodeFloat( buf, digits, FITSRLCOL - FITSNAMLEN - 2, - AST__FITSCHAN_FITSCARDLEN - col + 1, dval, status ); - if( rlen <= 0 || rlen > AST__FITSCHAN_FITSCARDLEN - col ) { - astError( AST__BDFTS, "%s(%s): Cannot encode real part of a complex " - "floating point value [%g,%g] into a FITS header card " - "for keyword '%s'.", status, method, astGetClass( this ), dval, - ( (double *) data )[ 1 ], name ); - } else { - dval = ( (double *) data )[ 1 ]; - ilen = EncodeFloat( buf + rlen, digits, - FITSIMCOL - FITSRLCOL, - AST__FITSCHAN_FITSCARDLEN - col - rlen, dval, status ); - if( ilen <= 0 ) { - astError( AST__BDFTS, "%s(%s): Cannot encode imaginary part of a " - "complex floating point value [%g,%g] into a FITS header " - "card for keyword '%s'.", status, method, astGetClass( this ), - ( (double *) data )[ 0 ], dval, name ); - } else { - len = ilen + rlen; - } - } - -/* AST__COMPLEXI - stored internally in a an array of two "ints". */ - } else if( type == AST__COMPLEXI ){ - rlen = sprintf( buf, "%*d", FITSRLCOL - col + 1, - ( (int *) data )[ 0 ] ); - if( rlen < 0 || rlen > AST__FITSCHAN_FITSCARDLEN - col ) { - astError( AST__BDFTS, "%s(%s): Cannot encode real part of a complex " - "integer value [%d,%d] into a FITS header card " - "for keyword '%s'.", status, method, astGetClass( this ), - ( (int *) data )[ 0 ], - ( (int *) data )[ 1 ], name ); - } else { - ilen = sprintf( buf + rlen, "%*d", FITSIMCOL - FITSRLCOL + 1, - ( (int *) data )[ 1 ] ); - if( ilen < 0 || ilen > AST__FITSCHAN_FITSCARDLEN - col - rlen ) { - astError( AST__BDFTS, "%s(%s): Cannot encode imaginary part of a " - "complex integer value [%d,%d] into a FITS header card " - "for keyword '%s'.", status, method, astGetClass( this ), - ( (int *) data )[ 0 ], - ( (int *) data )[ 1 ], name ); - } else { - len = ilen + rlen; - } - } - -/* Report an internal (ast) programming error if the keyword is of none of the - above types. */ - } else if( astOK ){ - astError( AST__INTER, "EncodeValue: AST internal programming error - " - "FITS %s data-type not yet supported.", status, - type_names[ type ] ); - } - } - -/* If an error has occurred, return zero length. */ - if( !astOK ) len = 0; - -/* Return the answer. */ - return len; -} - -static AstGrismMap *ExtractGrismMap( AstMapping *map, int iax, - AstMapping **new_map, int *status ){ -/* -* Name: -* ExtractGrismMap - -* Purpose: -* Extract a GrismMap from the end of the supplied Mapping. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstGrismMap *ExtractGrismMap( AstMapping *map, int iax, -* AstMapping **new_map, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function examines the supplied Mapping; if the specified output -* coordinate of the Mapping is created directly by an un-inverted GrismMap, -* then a pointer to the GrismMap is returned as the function value. A new -* Mapping is also returned via parameter "new_map" which is a copy of -* the supplied Mapping, except that the GrismMap is replaced with a -* UnitMap. If no GrismMap is found, NULL is returned for both Mappings. -* The condition that "the specified output coordinate of the Mapping is -* created directly by an un-inverted GrismMap" means that the output -* of the GrismMap is no subsequently modified by any further Mappings -* before being returned as the "iax"th output of the supplied Mapping. -* This means the GrismMap must be "at the end of" a CmpMap, not in -* the middle of the CmpMap. - -* Parameters: -* map -* Pointer to the Mapping to check. -* iax -* The index for the output coordinate to be checked. -* new_map -* Pointer to a location at which to return a pointer to a new -* Mapping which is a copy of "map" except that the GrismMap is -* replaced by a UnitMap. NULL is returned if the specified output -* was not created by a GrismMap. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The extracted GrismMap, or NULL if the specified output was not -* created by a GrismMap. -*/ - -/* Local Variables: */ - AstMapping *mapa; /* First component Mapping */ - AstMapping *mapb; /* Second component Mapping */ - AstMapping *new_mapa; /* Replacement for first component Mapping */ - AstMapping *new_mapb; /* Replacement for second component Mapping */ - AstGrismMap *ret; /* Returned GrismMap */ - int inva; /* Invert attribute for mapa within the CmpMap */ - int invb; /* Invert attribute for mapb within the CmpMap */ - int na; /* Number of outputs for mapa */ - int old_inva; /* Current Invert attribute for mapa */ - int old_invb; /* Current Invert attribute for mapb */ - int series; /* Are component Mappings applied in series? */ - -/* Initialise */ - ret = NULL; - *new_map = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* If the supplied Mapping is a GrismMap which has not been inverted, - return it as the function value and return a UnitMap as the new - Mapping. */ - if( astIsAGrismMap( map ) ) { - if( !astGetInvert( map ) ) { - ret = astClone( map ); - *new_map = (AstMapping *) astUnitMap( 1, "", status ); - } - -/* If the supplied Mapping is a CmpMap, get its two component Mappings, - see if they are applied in parallel or series, and get the Invert - attribute values which the component Mappings had at the time the - CmpMap was created. */ - } else if( astIsACmpMap( map ) ) { - astDecompose( map, &mapa, &mapb, &series, &inva, &invb ); - -/* Temporaily reset the Invert attributes of the component Mappings back to - the values they had when the CmpMap was created. */ - old_inva = astGetInvert( mapa ); - old_invb = astGetInvert( mapb ); - astSetInvert( mapa, inva ); - astSetInvert( mapb, invb ); - -/* If the supplied Mapping is a series CmpMap, attempt to extract a - GrismMap from the second component Mapping ("mapb"). The first - component Mapping ("mapa") is unchanged. We do not need to consdier - the first component since we are only interested in GrismMaps which are - at the end of the CmpMap. */ - if( series ) { - ret = ExtractGrismMap( mapb, iax, &new_mapb, status ); - if( ret ) new_mapa = astClone( mapa ); - -/* If the supplied Mapping is a parallel CmpMap, attempt to extract a - GrismMap from the component Mapping which produces output "iax". The - other component Mapping is unchanged. */ - } else { - na = astGetNout( mapa ); - if( iax < na ) { - ret = ExtractGrismMap( mapa, iax, &new_mapa, status ); - if( ret ) new_mapb = astClone( mapb ); - } else { - ret = ExtractGrismMap( mapb, iax - na, &new_mapb, status ); - if( ret ) new_mapa = astClone( mapa ); - } - } - -/* If succesful, create a new CmpMap to return. */ - if( ret ) { - *new_map = (AstMapping *) astCmpMap( new_mapa, new_mapb, series, "", status ); - new_mapa = astAnnul( new_mapa ); - new_mapb = astAnnul( new_mapb ); - } - -/* Re-instate the original Invert attributes of the component Mappings. */ - astSetInvert( mapa, old_inva ); - astSetInvert( mapb, old_invb ); - -/* Annul the component Mapping pointers. */ - mapa = astAnnul( mapa ); - mapb = astAnnul( mapb ); - } - -/* Return the result. */ - return ret; -} - -static int MakeBasisVectors( AstMapping *map, int nin, int nout, - double *g0, AstPointSet *psetg, - AstPointSet *psetw, int *status ){ -/* -* Name: -* MakeBasisVectors - -* Purpose: -* Create a set of basis vectors in grid coordinates - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int MakeBasisVectors( AstMapping *map, int nin, int nout, -* double *g0, AstPointSet *psetg, -* AstPointSet *psetw, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns a set of unit vectors in grid coordinates, -* one for each grid axis. Each unit vector is parallel to the -* corresponding grid axis, and rooted at a specified grid position -* ("g0"). The IWC coordinates corresponding to "g0" and to the end of -* each of the unit vectors are also returned, together with a flag -* indicating if all the IWC coordinate values are good. - -* Parameters: -* map -* A pointer to a Mapping which transforms grid coordinates into -* intermediate world coordinates (IWC). The number of outputs must -* be greater than or equal to the number of inputs. -* nin -* The number of inputs for "map" (i.e. the number of grid axes). -* nout -* The number of outputs for "map" (i.e. the number of IWC axes). -* g0 -* Pointer to an array of holding the grid coordinates at the -* "root" position. -* psetg -* A pointer to a PointSet which can be used to hold the required -* grid positions. This should have room for nin+1 positions. On -* return, the first position holds "g0", and the subsequent "nin" -* positions hold are offset from "g0" by unit vectors along the -* corresponding grid axis. -* psetw -* A pointer to a PointSet which can be used to hold the required -* IWC position. This should also have room for nin+1 positions. On -* return, the values are the IWC coordinates corresponding to the -* grid positions returned in "psetg". -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if all the axis values in "psetw" are good. -* Zero is returned otherwise. - -* Notes: -* - Zero is returned if an error occurs. -*/ - -/* Local Variables: */ - double **ptrg; - double **ptrw; - double *c; - int i; - int ii; - int j; - int ret; - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Get pointers to the data in the two supplied PointSets. */ - ptrg = astGetPoints( psetg ); - ptrw = astGetPoints( psetw ); - -/* Check the pointers can be used safely. */ - if( astOK ) { - -/* Assume success. */ - ret = 1; - -/* Store the required grid positions in PointSet "pset1". The first - position is the supplied root grid position, g0. The next "nin" - positions are offset from the root position by a unit vector along - each grid axis in turn. Store values for each grid axis in turn. */ - for( i = 0; i < nin; i++ ) { - -/* Get a pointer to the first axis value for this grid axis. */ - c = ptrg[ i ]; - -/* Initially set all values for this axis to the supplied root grid value. */ - for( ii = 0; ii < nin + 1; ii++ ) c[ ii ] = g0[ i ]; - -/* Modify the value corresponding to the vector along this grid axis. */ - c[ i + 1 ] += 1.0; - } - -/* Transform these grid positions in IWC positions using the supplied - Mapping. */ - (void) astTransform( map, psetg, 1, psetw ); - -/* Check that all the transformed positions are good. */ - for( j = 0; j < nout; j++ ) { - c = ptrw[ j ]; - for( ii = 0; ii < nin + 1; ii++, c++ ) { - if( *c == AST__BAD ) { - ret = 0; - break; - } - } - } - } - -/* Return the result. */ - return ret; -} - -static int FindBasisVectors( AstMapping *map, int nin, int nout, - double *dim, AstPointSet *psetg, - AstPointSet *psetw, int *status ){ -/* -* Name: -* FindBasisVectors - -* Purpose: -* Find the a set of basis vectors in grid coordinates - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int FindBasisVectors( AstMapping *map, int nin, int nout, -* double *dim, AstPointSet *psetg, -* AstPointSet *psetw, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns a set of unit vectors in grid coordinates, -* one for each grid axis. Each unit vector is parallel to the -* corresponding grid axis, and rooted at a specified grid position -* ("g0"). The IWC coordinates corresponding to "g0" and to the end of -* each of the unit vectors are also returned, together with a flag -* indicating if all the IWC coordinate values are good. - -* Parameters: -* map -* A pointer to a Mapping which transforms grid coordinates into -* intermediate world coordinates (IWC). The number of outputs must -* be greater than or equal to the number of inputs. -* nin -* The number of inputs for "map" (i.e. the number of grid axes). -* nout -* The number of outputs for "map" (i.e. the number of IWC axes). -* dim -* Array dimensions, in pixels, if known (otherwise supplied a NULL -* pointer to values of AST__BAD). -* psetg -* A pointer to a PointSet which can be used to hold the required -* grid position. This should have room for nin+1 positions. On -* return, the first position holds the "root" position and the -* subsequent "nin" positions hold are offset from root position -* by unit vectors along the corresponding grid axis. -* psetw -* A pointer to a PointSet which can be used to hold the required -* IWC position. This should also have room for nin+1 positions. On -* return, the values are the IWC coordinates corresponding to the -* grid positions returned in "psetg". -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if a set of basis vectors was found -* succesfully. Zero is returned otherwise. - -* Notes: -* - Zero is returned if an error occurs. -*/ - -/* Local Variables: */ - double *g0; - double dd; - double ddlim; - int i; - int ii; - int ret; - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Allocate an array to store the candidate root position. */ - g0 = astMalloc( sizeof( double )*(size_t) nin ); - if( astOK ) { - -/* First try the grid centre, if known. */ - ddlim = 0; - ret = 0; - if( dim ) { - ret = 1; - for( i = 0; i < nin; i++ ) { - if( dim[ i ] != AST__BAD ) { - g0[ i ] = 0.5*( 1 + dim[ i ] ); - if( dim[ i ] > ddlim ) ddlim = dim[ i ]; - } else { - ret = 0; - break; - } - } - } - if( ret ) ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status ); - -/* If this did not produce a set of good IWC positions, try grid position - (1,1,1...). */ - if( !ret ) { - for( i = 0; i < nin; i++ ) g0[ i ] = 1.0; - ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status ); - } - -/* If this did not produce a set of good IWC positions, try a sequence of - grid positions which move an increasing distance along each grid axis - from (1,1,1,...). Stop when we get further than "ddlim" from the - origin. */ - dd = 10.0; - if( ddlim == 0.0 ) ddlim = 10240.0; - while( !ret && dd <= ddlim ) { - -/* First try positions which extend across the middle of the data set. - If the image dimensions are known, make the line go from the "bottom - left corner" towards the "top right corner", taking the aspect ratio - of the image into account. Otherise, just use a vector of (1,1,1,..) */ - for( i = 0; i < nin; i++ ) { - if( dim && dim[ i ] != AST__BAD ) { - g0[ i ] = dd*dim[ i ]/ddlim; - } else { - g0[ i ] = dd; - } - } - ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status ); - -/* If the above didn't produce good positions, try moving out along each - grid axis in turn. */ - for( ii = 0; !ret && ii < nin; ii++ ) { - for( i = 0; i < nin; i++ ) g0[ i ] = 1.0; - g0[ ii ] = dd; - ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status ); - } - -/* Go further out from the origin for the next set of tests (if any). */ - dd *= 2.0; - } - } - -/* Free resources. */ - g0 = astFree( g0 ); - -/* Return the result. */ - return ret; -} - -static int FindLonLatSpecAxes( FitsStore *store, char s, int *axlon, int *axlat, - int *axspec, const char *method, const char *class, int *status ) { -/* -* Name: -* FindLonLatSpecAxes - -* Purpose: -* Search the CTYPE values in a FitsStore for celestial and spectral axes. - -* Type: -* Private function. - -* Synopsis: -* int FindLonLatSpecAxes( FitsStore *store, char s, int *axlon, int *axlat, -* int *axspec, const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* The supplied FitsStore is searched for axes with a specified axis -* description character which describe celestial longitude or latitude -* or spectral position. - -* Parameters: -* store -* A structure containing values for FITS keywords relating to -* the World Coordinate System. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* axlon -* Address of a location at which to return the index of the -* longitude axis (if found). This is the value of "i" within the -* keyword name "CTYPEi". A value of -1 is returned if no longitude -* axis is found. -* axlat -* Address of a location at which to return the index of the -* latitude axis (if found). This is the value of "i" within the -* keyword name "CTYPEi". A value of -1 is returned if no latitude -* axis is found. -* axspec -* Address of a location at which to return the index of the -* spectral axis (if found). This is the value of "i" within the -* keyword name "CTYPEi". A value of -1 is returned if no spectral -* axis is found. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* One is returned if both celestial axes were found. Zero is returned if -* either axis was not found. The presence of a spectral axis does not -* affect the returned value. - -* Notes: -* - If an error occurs, zero is returned. -*/ - -/* Local Variables: */ - char *assys; - char *astype; - char algcode[5]; - char stype[5]; - const char *ctype; - double dval; - int i; - int wcsaxes; - -/* Initialise */ - *axlon = -1; - *axlat = -1; - *axspec = -1; - -/* Check the global status. */ - if ( !astOK ) return 0; - -/* Obtain the number of FITS WCS axes in the header. If the WCSAXES header - was specified, use it. Otherwise assume it is the same as the number - of pixel axes. */ - dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); - if( dval != AST__BAD ) { - wcsaxes = (int) dval + 0.5; - } else { - wcsaxes = store->naxis; - } - -/* Loop round the FITS WCS axes, getting each CTYPE value. */ - for( i = 0; i < wcsaxes && astOK; i++ ){ - ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - -/* Check a value was found. */ - if( ctype ) { - -/* First check for spectral axes, either FITS-WCS or AIPS-like. */ - if( IsSpectral( ctype, stype, algcode, status ) || - IsAIPSSpectral( ctype, &astype, &assys, status ) ) { - *axspec = i; - -/* Otherwise look for celestial axes. Celestial axes must have a "-" as the - fifth character in CTYPE. */ - } else if( ctype[4] == '-' ) { - -/* See if this is a longitude axis (e.g. if the first 4 characters of CTYPE - are "RA--" or "xLON" or "yzLN" ). */ - if( !strncmp( ctype, "RA--", 4 ) || - !strncmp( ctype, "AZ--", 4 ) || - !strncmp( ctype + 1, "LON", 3 ) || - !strncmp( ctype + 2, "LN", 2 ) ){ - *axlon = i; - -/* Otherwise see if it is a latitude axis. */ - } else if( !strncmp( ctype, "DEC-", 4 ) || - !strncmp( ctype, "EL--", 4 ) || - !strncmp( ctype + 1, "LAT", 3 ) || - !strncmp( ctype + 2, "LT", 2 ) ){ - *axlat = i; - } - } - } - } - -/* Indicate failure if an error occurred. */ - if( !astOK ) { - *axlon = -1; - *axlat = -1; - *axspec = -1; - } - -/* Return the result. */ - return ( *axlat != -1 && *axlon != -1 ); -} - -static void FindWcs( AstFitsChan *this, int last, int all, int rewind, - const char *method, const char *class, int *status ){ - -/* -* Name: -* FindWcs - -* Purpose: -* Find the first or last FITS WCS related keyword in a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void FindWcs( AstFitsChan *this, int last, int all, int rewind, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A search is made through the FitsChan for the first or last card -* which relates to a FITS WCS keyword (any encoding). If "last" is -* non-zero, the next card becomes the current card. If "last" is -* zero, the WCS card is left as the current card. Cards marked as -* having been read are included or not, as specified by "all". - -* Parameters: -* this -* Pointer to the FitsChan. -* last -* If non-zero, the last WCS card is searched for. Otherwise, the -* first WCS card is searched for. -* all -* If non-zero, then cards marked as having been read are included -* in the search. Otherwise such cards are ignored. -* rewind -* Only used if "last" is zero (i.e. the first card is being -* searched for). If "rewind" is non-zero, then the search starts -* from the first card in the FitsChan. If zero, the search starts -* from the current card. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Notes: -* - The FitsChan is left at end-of-file if no FITS-WCS keyword cards -* are found in the FitsChan. -*- -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - const char *keyname; /* Keyword name from current card */ - int nfld; /* Number of fields in keyword template */ - int old_ignore_used; /* Original value of variable ignore_used */ - -/* Check the global status. Also check the FitsChan is not empty. */ - if( !astOK || !this->head ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Indicate that we should, or should not, skip over cards marked as having - been read. */ - old_ignore_used = ignore_used; - ignore_used = all ? 0 : 1; - -/* If required, set the FitsChan to start or end of file. */ - if( last ) { - astSetCard( this, INT_MAX ); - } else if( rewind ) { - astClearCard( this ); - } - -/* If the current card is marked as used, and we are skipping used cards, - move on to the next unused card */ - if( CARDUSED( this->card ) ) MoveCard( this, last?-1:1, method, class, status ); - -/* Check each card moving backwards from the end to the start, or - forwards from the start to the end, until a WCS keyword is found, - or the other end of the FitsChan is reached. */ - while( astOK ){ - -/* Get the keyword name from the current card. */ - keyname = CardName( this, status ); - -/* Save a pointer to the keyword if it is the first non-null, non-comment - card. */ - if( keyname ) { - -/* If it matches any of the WCS keywords, move on one card - and break out of the loop. */ - if( Match( keyname, "CRVAL%d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CRPIX%d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CDELT%d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CROTA%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CTYPE%d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CUNIT%d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PC%3d%3d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CD%3d%3d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CD%1d_%1d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PC%1d_%1d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "LONGPOLE", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "LONPOLE%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "LATPOLE%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PROJP%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PV%d_%d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PS%d_%d%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "EPOCH", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "EQUINOX%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "MJD-OBS", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "DATE-OBS", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "TIMESYS", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "RADECSYS", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "RADESYS%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "C%1dVAL%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "C%1dPIX%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "C%1dELT%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "C%1dYPE%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "C%1dNIT%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CNPIX1", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "CNPIX2", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PPO%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "AMDX%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "AMDY%d", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "XPIXELSZ", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "YPIXELSZ", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PLTRAH", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PLTRAM", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PLTRAS", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PLTDECD", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PLTDECM", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PLTDECS", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PLTDECSN", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PLTSCALE", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PPO1", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PPO2", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PPO4", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "PPO5", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "WCSNAME%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "SPECSYS%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "SSYSSRC%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "ZSOURCE%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "VELOSYS%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "RESTFRQ%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "MJD_AVG%0c", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "OBSGEO-X", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "OBSGEO-Y", 0, NULL, &nfld, method, class, status ) || - Match( keyname, "OBSGEO-Z", 0, NULL, &nfld, method, class, status ) ) { - if( last ) MoveCard( this, 1, method, class, status ); - break; - } - } - -/* Leave the FitsChan at end-of-file if no WCS cards were found. */ - if( (last && FitsSof( this, status ) ) || - (!last && astFitsEof( this ) ) ) { - astSetCard( this, INT_MAX ); - break; - } else { - MoveCard( this, last?-1:1, method, class, status ); - } - } - -/* Re-instate the original flag indicating if cards marked as having been - read should be skipped over. */ - ignore_used = old_ignore_used; - -/* Return. */ - return; -} - -static int FindString( int n, const char *list[], const char *test, - const char *text, const char *method, - const char *class, int *status ){ -/* -* Name: -* FindString - -* Purpose: -* Find a given string within an array of character strings. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int FindString( int n, const char *list[], const char *test, -* const char *text, const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function identifies a supplied string within a supplied -* array of valid strings, and returns the index of the string within -* the array. The test option may not be abbreviated, but case is -* insignificant. - -* Parameters: -* n -* The number of strings in the array pointed to be "list". -* list -* A pointer to an array of legal character strings. -* test -* A candidate string. -* text -* A string giving a description of the object, parameter, -* attribute, etc, to which the test value refers. -* This is only for use in constructing error messages. It should -* start with a lower case letter. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The index of the identified string within the supplied array, starting -* at zero. - -* Notes: -* - A value of -1 is returned if an error has already occurred, or -* if this function should fail for any reason (for instance if the -* supplied option is not specified in the supplied list). -*/ - -/* Local Variables: */ - int ret; /* The returned index */ - -/* Check global status. */ - if( !astOK ) return -1; - -/* Compare the test string with each element of the supplied list. Leave - the loop when a match is found. */ - for( ret = 0; ret < n; ret++ ) { - if( !Ustrcmp( test, list[ ret ], status ) ) break; - } - -/* Report an error if the supplied test string does not match any element - in the supplied list. */ - if( ret >= n && astOK ) { - astError( AST__RDERR, "%s(%s): Illegal value '%s' supplied for %s.", status, - method, class, test, text ); - ret = -1; - } - -/* Return the answer. */ - return ret; -} - -static int FitOK( int n, double *act, double *est, double tol, int *status ) { -/* -* Name: -* FitOK - -* Purpose: -* See if a fit is usable. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int FitOK( int n, double *act, double *est, double tol, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function is supplied with a set of actual data values, and the -* corresponding values estimated by some fitting process. It tests -* that the RMS residual between them is no more than "tol". - -* Parameters: -* n -* Number of data points. -* act -* Pointer to the start of the actual data values. -* est -* Pointer to the start of the estimated data values. -* tol -* The largest acceptable RMS error between "act" and "est". -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if the two sets of values agree. Zero is -* returned otherwise. - -* Notes: -* - Zero is returned if an error occurs. -*/ - -/* Local Variables: */ - int ret, i; - double s1, s2; - double *px, *py, diff, mserr; - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Initialise the sum of the squared residuals, and the number summed. */ - s1 = 0.0; - s2 = 0.0; - -/* Initialise pointers to the next actual and estimated values to use. */ - px = act; - py = est; - -/* Loop round all pairs of good actual and estimate value. */ - for( i = 0; i < n; i++, px++, py++ ){ - if( *px != AST__BAD && *py != AST__BAD ) { - -/* Increment the sums need to find the RMS residual between the actual - and estimated values. */ - diff = *px - *py; - s1 += diff*diff; - s2 += 1.0; - } - } - -/* If the sums are usable... */ - if( s2 > 0.0 ) { - -/* Form the mean squared residual, and check if it is less than the - squared error limit. */ - mserr = s1/s2; - if( mserr < tol*tol ) ret = 1; - } - -/* Return the result. */ - return ret; -} - -static int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm, - int *perm, int *status ){ -/* -* Name: -* FitsAxisOrder - -* Purpose: -* Return the order of WCS axes specified by attribute FitsAxisOrder. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm, -* int *perm, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns an array indicating the order of the WCS axes -* within the output FITS header, as specified by the FitsAxisOrder -* attribute. - -* Parameters: -* this -* Pointer to the FitsChan. -* nwcs -* The number of axes in "wcsfrm". -* wcsfrm -* The Frame containing the output WCS axes. -* perm -* Pointer to an array of "nwcs" integers. On exit, element "k" -* of this array holds the zero-based index of the FITS-WCS axis -* (i.e. one less than the value of "i" in the keyword names -* "CTYPEi", "CRVALi", etc) that describes the k'th axis in "wcsfrm". -* In other words, "perm[ast_index] = fits_index". The order is -* determined by the FitsAxisOrder attribute. If this attribute is -* "<copy>" or "<auto>", then "perm[k]=k" for all k on exit (i.e. -* a unit mapping between axes in "wcsfrm" and the FITS header). -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Returns zero if the FitsAxisOrder attribute is "<auto">, and -* non-zero otherwise. This is a flag indicating if the returned -* values in "perm" can be used s they are. - -*/ - -/* Local Variables: */ - AstKeyMap *km; /* KeyMap holding axis indices keyed by axis symbols */ - char **words; /* Pointer to array of words from FitsAxisOrder */ - char attr_name[15];/* Attribute name */ - const char *attr; /* Pointer to a string holding the FitsAxisOrder value */ - int i; /* Loop count */ - int j; /* Zero-based axis index */ - int k; /* Zero-based axis index */ - int nword; /* Number of words in FitsAxisOrder */ - int result; /* Retrned value */ - -/* Check the inherited status. */ - if( !astOK ) return 0; - -/* Initialise the returned array to a unit mapping from Frame axis to - FITS axis. */ - for( i = 0; i < nwcs; i++ ) perm[ i ] = i; - -/* Get the FitsAxisOrder attribute value, and set the returned value to - indicate if it is "<auto>". */ - attr = astGetFitsAxisOrder( this ); - result = !astChrMatch( attr, "<auto>" ); - -/* Return immediately if it is "<auto>" or "<copy>". */ - if( result && !astChrMatch( attr, "<copy>" ) ) { - -/* Create a KeyMap in which each key is the Symbol for an axis and the - associated value is the zero based index of the axis within "wcsfrm". */ - km = astKeyMap( "KeyCase=0", status ); - for( i = 0; i < nwcs; i++ ){ - sprintf( attr_name, "Symbol(%d)", i + 1 ); - astMapPut0I( km, astGetC( wcsfrm, attr_name ), i, NULL ); - } - -/* Split the FitsAxisOrder value into a collection of space-separated words. */ - words = astChrSplit( attr, &nword ); - -/* Loop round them all. */ - k = 0; - for( i = 0; i < nword; i++ ) { - -/* Get the zero based index within "wcsfrm" of the axis that has a Symbol - equal to the current word from FitsAxisOrder. */ - if( astMapGet0I( km, words[ i ], &j ) ) { - -/* If this "wcsfrm" axis has already been used, report an error. */ - if( j < 0 ) { - if( astOK ) astError( AST__ATTIN, "astWrite(fitschan): " - "attribute FitsAxisOrder (%s) refers to axis " - "%s more than once.", status, attr, words[ i ] ); - -/* Otherwise, set the corresponding element of the returned array, and - ensure this axis cannot be used again by assigning it an index of -1 - in the KeyMap. */ - } else { - perm[ j ] = k++; - astMapPut0I( km, words[ i ], -1, NULL ); - } - } - -/* Free the memory holding the copy of the word. */ - words[ i ] = astFree( words[ i ] ); - } - -/* Report an error if any wcsfrm axes were not included in FitsAxisOrder. */ - if( astOK ) { - for( i = 0; i < nwcs; i++ ){ - sprintf( attr_name, "Symbol(%d)", i + 1 ); - if( astMapGet0I( km, astGetC( wcsfrm, attr_name ), &j ) ) { - if( j >= 0 ) { - astError( AST__ATTIN, "astWrite(fitschan): attribute FitsAxisOrder " - "(%s) does not specify a position for WCS axis '%s'.", - status, attr, astGetC( wcsfrm, attr_name ) ); - break; - } - } - } - } - -/* Free resources. */ - words = astFree( words ); - km = astAnnul( km ); - } - - return result; -} - -static int FitsFromStore( AstFitsChan *this, FitsStore *store, int encoding, - double *dim, AstFrameSet *fs, const char *method, - const char *class, int *status ){ - -/* -* Name: -* FitsFromStore - -* Purpose: -* Store WCS keywords in a FitsChan. - -* Type: -* Private function. - -* Synopsis: - -* int FitsFromStore( AstFitsChan *this, FitsStore *store, int encoding, -* double *dim, AstFrameSet *fs, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function copies the WCS information stored in the supplied -* FitsStore into the supplied FitsChan, using a specified encoding. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore. -* encoding -* The encoding to use. -* dim -* Pointer to an array holding the array dimensions (AST__BAD -* indicates that the dimenson is not known). -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if succesfull, and zero is returned -* otherwise. -*/ - -/* Local Variables: */ - int ret; - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Set the current card so that it points to the last WCS-related keyword - in the FitsChan (whether previously read or not). Any new WCS related - keywords either over-write pre-existing cards for the same keyword, or - (if no pre-existing card exists) are inserted after the last WCS related - keyword. */ - FindWcs( this, 1, 1, 0, method, class, status ); - -/* Do each non-standard FITS encoding... */ - if( encoding == DSS_ENCODING ){ - ret = DSSFromStore( this, store, method, class, status ); - } else if( encoding == FITSPC_ENCODING ){ - ret = PCFromStore( this, store, method, class, status ); - } else if( encoding == FITSIRAF_ENCODING ){ - ret = IRAFFromStore( this, store, method, class, status ); - } else if( encoding == FITSAIPS_ENCODING ){ - ret = AIPSFromStore( this, store, method, class, status ); - } else if( encoding == FITSAIPSPP_ENCODING ){ - ret = AIPSPPFromStore( this, store, method, class, status ); - } else if( encoding == FITSCLASS_ENCODING ){ - ret = CLASSFromStore( this, store, fs, dim, method, class, status ); - -/* Standard FITS-WCS encoding */ - } else { - ret = WcsFromStore( this, store, method, class, status ); - } - -/* If there are any Tables in the FitsStore move the KeyMap that contains - them from the FitsStore to the FitsChan, from where they can be - retrieved using the public astGetTables method. */ - if( astMapSize( store->tables ) > 0 ) { - if( !this->tables ) this->tables = astKeyMap( " ", status ); - astMapCopy( this->tables, store->tables ); - (void) astAnnul( store->tables ); - store->tables = astKeyMap( " ", status ); - } - -/* If an error has occurred, return zero. */ - if( !astOK ) ret = 0; - -/* Return the answer. */ - return ret; -} - -static FitsStore *FitsToStore( AstFitsChan *this, int encoding, - const char *method, const char *class, int *status ){ - -/* -* Name: -* FitsToStore - -* Purpose: -* Return a pointer to a FitsStore structure containing WCS information -* read from the supplied FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* FitsStore *FitsToStore( AstFitsChan *this, int encoding, -* const char *method, const char *class ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function creates a new FitsStore containing WCS information -* read from the supplied FitsChan using the specified encoding. An -* error is reported and a null pointer returned if the FitsChan does -* not contain usable WCS information with the specified encoding. - -* Parameters: -* this -* Pointer to the FitsChan. -* encoding -* The encoding to use. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. - -* Returned Value: -* A pointer to a new FitsStore, or NULL if an error has occurred. The -* FitsStore should be released using FreeStore function when it is no -* longer needed. -*/ - -/* Local Variables: */ - AstFitsChan *trans; - FitsStore *ret; - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Allocate memory for the new FitsStore, and store NULL pointers in it. */ - ret = (FitsStore *) astMalloc( sizeof(FitsStore) ); - if( ret ) { - ret->cname = NULL; - ret->ctype = NULL; - ret->ctype_com = NULL; - ret->cunit = NULL; - ret->ps = NULL; - ret->radesys = NULL; - ret->wcsname = NULL; - ret->wcsaxes = NULL; - ret->pc = NULL; - ret->cdelt = NULL; - ret->crpix = NULL; - ret->crval = NULL; - ret->equinox = NULL; - ret->latpole = NULL; - ret->lonpole = NULL; - ret->mjdobs = NULL; - ret->mjdavg = NULL; - ret->dut1 = NULL; - ret->dtai = NULL; - ret->pv = NULL; - ret->specsys = NULL; - ret->ssyssrc = NULL; - ret->obsgeox = NULL; - ret->obsgeoy = NULL; - ret->obsgeoz = NULL; - ret->restfrq = NULL; - ret->restwav = NULL; - ret->zsource = NULL; - ret->velosys = NULL; - ret->asip = NULL; - ret->bsip = NULL; - ret->apsip = NULL; - ret->bpsip = NULL; - ret->imagfreq = NULL; - ret->axref = NULL; - ret->naxis = 0; - ret->timesys = NULL; - ret->tables = astKeyMap( " ", status ); - ret->skyref = NULL; - ret->skyrefp = NULL; - ret->skyrefis = NULL; - } - -/* Call the routine apropriate to the encoding. */ - if( encoding == DSS_ENCODING ){ - DSSToStore( this, ret, method, class, status ); - -/* All other foreign encodings are treated as variants of FITS-WCS. */ - } else { - -/* Create a new FitsChan containing standard translations for any - non-standard keywords in the supplied FitsChan. The non-standard - keywords are marked as provisionally read in the supplied FitsChan. */ - trans = SpecTrans( this, encoding, method, class, status ); - -/* Copy the required values to the FitsStore, using keywords in "trans" - in preference to those in "this". */ - WcsToStore( this, trans, ret, method, class, status ); - -/* Delete the temporary FitsChan holding translations of non-standard - keywords. */ - if( trans ) trans = (AstFitsChan *) astDelete( trans ); - -/* Store the number of pixel axes. This is taken as the highest index used - in any primary CRPIX keyword. */ - ret->naxis = GetMaxJM( &(ret->crpix), ' ', status ) + 1; - } - -/* If an error has occurred, free the returned FitsStore, and return a null - pointer. */ - if( !astOK ) ret = FreeStore( ret, status ); - -/* Return the answer. */ - return ret; -} - -static void FreeItem( double ****item, int *status ){ -/* -* Name: -* FreeItem - -* Purpose: -* Frees all dynamically allocated memory associated with a specified -* item in a FitsStore. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void FreeItem( double ****item, int *status ); - -* Class Membership: -* FitsChan member function. - -* Description: -* Frees all dynamically allocated memory associated with the specified -* item in a FitsStore. A NULL pointer is stored in the FitsStore. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->crval) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (j), and the pointer locates an -* array of axis keyword values. These arrays of keyword values have -* one element for every pixel axis (i) or projection parameter (m). -* status -* Pointer to the inherited status variable. - -* Notes: -* - This function attempt to execute even if an error has occurred. -*/ - -/* Local Variables: */ - int si; /* Integer co-ordinate version index */ - int j; /* Intermediate co-ordinate axis index */ - int oldstatus; /* Old error status value */ - int oldreport; /* Old error reporting value */ - -/* Other initialisation to avoid compiler warnings. */ - oldreport = 0; - -/* Check the supplied pointer */ - if( item && *item ){ - -/* Start a new error reporting context. */ - oldstatus = astStatus; - if( !astOK ) { - oldreport = astReporting( 0 ); - astClearStatus; - } - -/* Loop round each coordinate version. */ - for( si = 0; si < astSizeOf( (void *) *item )/sizeof(double **); - si++ ){ - -/* Check the pointer stored for this co-ordinate version is not null. */ - if( (*item)[si] ) { - -/* Loop round the intermediate axes. */ - for( j = 0; j < astSizeOf( (void *) (*item)[si] )/sizeof(double *); - j++ ){ - -/* Free the pixel axis/parameter index pointer. */ - (*item)[si][j] = (double *) astFree( (void *) (*item)[si][j] ); - } - -/* Free the intermediate axes pointer */ - (*item)[si] = (double **) astFree( (void *) (*item)[si] ); - } - } - -/* Free the co-ordinate versions pointer */ - *item = (double ***) astFree( (void *) *item ); - -/* If there was an error status on entry to this function, re-instate it. - Otherwise, allow any new error status to remain. */ - if( oldstatus ){ - if( !astOK ) astClearStatus; - astSetStatus( oldstatus ); - astReporting( oldreport ); - } - } -} - -static void FreeItemC( char *****item, int *status ){ -/* -* Name: -* FreeItemC - -* Purpose: -* Frees all dynamically allocated memory associated with a specified -* string item in a FitsStore. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void FreeItemC( char *****item, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Frees all dynamically allocated memory associated with the specified -* string item in a FitsStore. A NULL pointer is stored in the FitsStore. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->ctype) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (j), and the pointer locates an -* array of axis keyword values. These arrays of keyword values have -* one element (a char pointyer) for every pixel axis (i) or -* projection parameter (m). -* status -* Pointer to the inherited status variable. - -* Notes: -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - int si; /* Integer co-ordinate version index */ - int i; /* Intermediate co-ordinate axis index */ - int jm; /* Pixel co-ordinate axis or parameter index */ - int oldstatus; /* Old error status value */ - int oldreport; /* Old error reporting value */ - -/* Other initialisation to avoid compiler warnings. */ - oldreport = 0; - -/* Check the supplied pointer */ - if( item && *item ){ - -/* Start a new error reporting context. */ - oldstatus = astStatus; - if( !astOK ) { - oldreport = astReporting( 0 ); - astClearStatus; - } - -/* Loop round each coordinate version. */ - for( si = 0; si < astSizeOf( (void *) *item )/sizeof(char ***); - si++ ){ - -/* Check the pointer stored for this co-ordinate version is not null. */ - if( (*item)[si] ) { - -/* Loop round the intermediate axes. */ - for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(char **); - i++ ){ - -/* Check the pointer stored for this intermediate axis is not null. */ - if( (*item)[si][i] ) { - -/* Loop round the pixel axes or parameter values. */ - for( jm = 0; jm < astSizeOf( (void *) (*item)[si][i] )/sizeof(char *); - jm++ ){ - -/* Free the string. */ - (*item)[si][i][jm] = (char *) astFree( (void *) (*item)[si][i][jm] ); - } - -/* Free the pixel axes/parameter pointer */ - (*item)[si][i] = (char **) astFree( (void *) (*item)[si][i] ); - } - } - -/* Free the intermediate axes pointer */ - (*item)[si] = (char ***) astFree( (void *) (*item)[si] ); - } - } - -/* Free the co-ordinate versions pointer */ - *item = (char ****) astFree( (void *) *item ); - -/* If there was an error status on entry to this function, re-instate it. - Otherwise, allow any new error status to remain. */ - if( oldstatus ){ - if( !astOK ) astClearStatus; - astSetStatus( oldstatus ); - astReporting( oldreport ); - } - } -} - -static FitsStore *FreeStore( FitsStore *store, int *status ){ -/* -* Name: -* FreeStore - -* Purpose: -* Free dynamic arrays stored in a FitsStore structure. - -* Type: -* Private function. - -* Synopsis: -* FitsStore *FreeStore( FitsStore *store, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function frees all dynamically allocated arrays stored in the -* supplied FitsStore structure, and returns a NULL pointer. - -* Parameters: -* store -* Pointer to the structure to clean. -* status -* Pointer to the inherited status variable. - -* Notes: -* - This function attempts to execute even if an error exists on entry. -*/ - -/* Return if no FitsStore was supplied. */ - if( !store ) return NULL; - -/* Free each of the dynamic arrays stored in the FitsStore. */ - FreeItemC( &(store->cname), status ); - FreeItemC( &(store->ctype), status ); - FreeItemC( &(store->ctype_com), status ); - FreeItemC( &(store->cunit), status ); - FreeItemC( &(store->radesys), status ); - FreeItemC( &(store->wcsname), status ); - FreeItemC( &(store->specsys), status ); - FreeItemC( &(store->ssyssrc), status ); - FreeItemC( &(store->ps), status ); - FreeItemC( &(store->timesys), status ); - FreeItem( &(store->pc), status ); - FreeItem( &(store->cdelt), status ); - FreeItem( &(store->crpix), status ); - FreeItem( &(store->crval), status ); - FreeItem( &(store->equinox), status ); - FreeItem( &(store->latpole), status ); - FreeItem( &(store->lonpole), status ); - FreeItem( &(store->mjdobs), status ); - FreeItem( &(store->dut1), status ); - FreeItem( &(store->dtai), status ); - FreeItem( &(store->mjdavg), status ); - FreeItem( &(store->pv), status ); - FreeItem( &(store->wcsaxes), status ); - FreeItem( &(store->obsgeox), status ); - FreeItem( &(store->obsgeoy), status ); - FreeItem( &(store->obsgeoz), status ); - FreeItem( &(store->restfrq), status ); - FreeItem( &(store->restwav), status ); - FreeItem( &(store->zsource), status ); - FreeItem( &(store->velosys), status ); - FreeItem( &(store->asip), status ); - FreeItem( &(store->bsip), status ); - FreeItem( &(store->apsip), status ); - FreeItem( &(store->bpsip), status ); - FreeItem( &(store->imagfreq), status ); - FreeItem( &(store->axref), status ); - store->tables = astAnnul( store->tables ); - FreeItem( &(store->skyref), status ); - FreeItem( &(store->skyrefp), status ); - FreeItemC( &(store->skyrefis), status ); - return (FitsStore *) astFree( (void *) store ); -} - -static char *FormatKey( const char *key, int c1, int c2, char s, int *status ){ -/* -* Name: -* FormatKey - -* Purpose: -* Format a keyword name with indices and co-ordinate version character. - -* Type: -* Private function. - -* Synopsis: -* char *FormatKey( const char *key, int c1, int c2, char s, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function formats a keyword name by including the supplied -* axis/parameter indices and co-ordinate version character. - -* Parameters: -* key -* The base name of the keyword (e.g. "CD", "CRVAL", etc). -* c1 -* An integer value to append to the end of the keyword. Ignored if -* less than zero. -* c2 -* A second integer value to append to the end of the keyword. Ignored if -* less than zero. This second integer is preceded by an underscore. -* s -* The co-ordinate version character to append to the end of the -* final string. Ignored if blank. -* status -* Pointer to the inherited status variable. -* Returned Value; -* A pointer to a static character buffer containing the final string. -* NULL if an error occurs. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS - char *ret; - int len; - int nc; - -/* Initialise */ - ret = NULL; - -/* Check inherited status */ - if( !astOK ) return ret; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(NULL); - -/* No characters stored yet. A value of -1 is used to indicate that an - error has occurred. */ - len = 0; - -/* Store the supplied keyword base name. */ - if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%s", key ) ) >= 0 ){ - len += nc; - } else { - len = -1; - } - -/* If index c1 has been supplied, append it to the end of the string. */ - if( c1 >= 0 ) { - if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%d", c1 ) ) >= 0 ){ - len += nc; - } else { - len = -1; - } - -/* If index c2 has been supplied, append it to the end of the string, - preceded by an underscore. */ - if( c2 >= 0 ) { - if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "_%d", c2 ) ) >= 0 ){ - len += nc; - } else { - len = -1; - } - } - } - -/* If a co-ordinate version character has been supplied, append it to the end - of the string. */ - if( s != ' ' ) { - if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%c", s ) ) >= 0 ){ - len += nc; - } else { - len = -1; - } - } - -/* Report an error if necessary */ - if( len < 0 && astOK ) { - astError( AST__INTER, "FormatKey(fitschan): AST internal error; failed " - "to format the keyword %s with indices %d and %d, and " - "co-ordinate version %c.", status, key, c1, c2, s ); - ret = NULL; - } else { - ret = formatkey_buff; - } - return formatkey_buff; -} - -static AstObject *FsetFromStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ){ -/* -* Name: -* FsetFromStore - -* Purpose: -* Create a FrameSet using the the information previously stored in -* the suppllied FitsStore structure. - -* Type: -* Private function. - -* Synopsis: -* AstObject *FsetFromStore( AstFitsChan *this, FitsStore *store, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function creates a new FrameSet containing WCS information -* stored in the supplied FitsStore. A null pointer is returned and no -* error is reported if this is not possible. - -* Parameters: -* this -* The FitsChan from which the keywords were read. Warning messages -* are added to this FitsChan if the celestial co-ordinate system is -* not recognized. -* store -* Pointer to the FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the new FrameSet, or a null pointer if no FrameSet -* could be constructed. - -* Notes: -* - The pixel Frame is given a title of "Pixel Coordinates", and -* each axis in the pixel Frame is given a label of the form "Pixel -* axis <n>", where <n> is the axis index (starting at one). -* - The FITS CTYPE keyword values are used to set the labels for any -* non-celestial axes in the physical coordinate Frames, and the FITS -* CUNIT keywords are used to set the corresponding units strings. -* - On exit, the pixel Frame is the base Frame, and the physical -* Frame derived from the primary axis descriptions is the current Frame. -* - Extra Frames are added to hold any secondary axis descriptions. All -* axes within such a Frame refer to the same coordinate version ('A', -* 'B', etc). -*/ - -/* Local Variables: */ - AstFrame *frame; /* Pointer to pixel Frame */ - AstFrameSet *ret; /* Pointer to returned FrameSet */ - char buff[ 20 ]; /* Buffer for axis label */ - char s; /* Co-ordinate version character */ - int i; /* Pixel axis index */ - int physical; /* Index of primary physical co-ordinate Frame */ - int pixel; /* Index of pixel Frame in returned FrameSet */ - int use; /* Has this co-ordinate version been used? */ - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return (AstObject *) ret; - -/* Only proceed if there are some axes. */ - if( store->naxis ) { - -/* Create a Frame describing the pixel coordinate system. Give it the Domain - GRID. */ - frame = astFrame( store->naxis, "Title=Pixel Coordinates,Domain=GRID", status ); - -/* Store labels for each pixel axis. */ - if( astOK ){ - for( i = 0; i < store->naxis; i++ ){ - sprintf( buff, "Pixel axis %d", i + 1 ); - astSetLabel( frame, i, buff ); - } - } - -/* Create the FrameSet initially holding just the pixel coordinate frame - (this becomes the base Frame). */ - ret = astFrameSet( frame, "", status ); - -/* Annul the pointer to the pixel coordinate Frame. */ - frame = astAnnul( frame ); - -/* Get the index of the pixel Frame in the FrameSet. */ - pixel = astGetCurrent( ret ); - -/* Produce the Frame describing the primary axis descriptions, and add it - into the FrameSet. */ - AddFrame( this, ret, pixel, store->naxis, store, ' ', method, class, status ); - -/* Get the index of the primary physical co-ordinate Frame in the FrameSet. */ - physical = astGetCurrent( ret ); - -/* Loop, producing secondary axis Frames for each of the co-ordinate - versions stored in the FitsStore. */ - for( s = 'A'; s <= GetMaxS( &(store->crval), status ) && astOK; s++ ){ - -/* Only use this co-ordinate version character if any of the required - keywords (for any axis) are stored in the FitsStore. */ - use = 0; - for( i = 0; i < store->naxis; i++ ){ - if( GetItem( &(store->crval), i, 0, s, NULL, method, class, status ) != AST__BAD || - GetItem( &(store->crpix), 0, i, s, NULL, method, class, status ) != AST__BAD || - GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ) != NULL ){ - use = 1; - break; - } - } - -/* If this co-ordinate version has been used, add a Frame to the returned - FrameSet holding this co-ordinate version. */ - if( use ) AddFrame( this, ret, pixel, store->naxis, store, s, method, class, status ); - } - -/* Ensure the pixel Frame is the Base Frame and the primary physical - Frame is the Current Frame. */ - astSetBase( ret, pixel ); - astSetCurrent( ret, physical ); - -/* Remove any unneeded Frames that hold a FITS representation of offset - coordinates. */ - TidyOffsets( ret, status ); - -/* If an error has occurred, free the returned FrameSet and return a null - pointer. */ - if( !astOK ) ret = astAnnul( ret ); - } - -/* Return the answer. */ - return (AstObject *) ret; -} - -static FitsStore *FsetToStore( AstFitsChan *this, AstFrameSet *fset, int naxis, - double *dim, int encoding, const char *class, - const char *method, int *status ){ - -/* -* Name: -* FsetToStore - -* Purpose: -* Fill a FitsStore structure with a description of the supplied -* FrameSet. - -* Type: -* Private function. - -* Synopsis: - -* FitsStore *FsetToStore( AstFitsChan *this, AstFrameSet *fset, int naxis, -* double *dim, int encoding, const char *class, -* const char *method, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function creates a new FitsStore containing WCS information -* read from the supplied FitsChan using the specified encoding. An -* error is reported and a null pointer returned if the FitsChan does -* not contain usable WCS information with the specified encoding. - -* Parameters: -* this -* Pointer to the FitsChan. -* fset -* Pointer to the FrameSet. -* naxis -* The number of axes in the Base Frame of the supplied FrameSet. -* dim -* Pointer to an array of pixel axis dimensions. Individual elements -* will be AST__BAD if dimensions are not known. -* encoding -* The encoding being used. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a new FitsStore, or NULL if an error has occurred. The -* FitsStore should be released using FreeStore function when it is no -* longer needed. - -* Notes: -* - A NULL pointer will be returned if this function is invoked -* with the AST error status set, or if it should fail for any -* reason. -* - The Base Frame in the FrameSet is used as the pixel Frame, and -* the Current Frame is used to create the primary axis descriptions. -* Attempts are made to create secondary axis descriptions for any -* other Frames in the FrameSet (up to a total of 26). -*/ - -/* Local Variables: */ - AstFrame *frame; /* A Frame */ - const char *id; /* Frame Ident string */ - int nfrm; /* Number of Frames in FrameSet */ - char *sid; /* Pointer to array of version letters */ - int frms[ 'Z' + 1 ]; /* Array of Frame indices */ - FitsStore *ret; /* Returned FitsStore */ - char s; /* Next available co-ordinate version character */ - char s0; /* Co-ordinate version character */ - int ibase; /* Base Frame index */ - int icurr; /* Current Frame index */ - int ifrm; /* Next Frame index */ - int isoff; /* Is the Frame an offset SkyFrame? */ - int primok; /* Primary Frame stored succesfully? */ - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Allocate memory for the new FitsStore, and store NULL pointers in it. */ - ret = (FitsStore *) astMalloc( sizeof(FitsStore) ); - if( astOK ) { - ret->cname = NULL; - ret->ctype = NULL; - ret->ctype_com = NULL; - ret->cunit = NULL; - ret->ps = NULL; - ret->radesys = NULL; - ret->wcsname = NULL; - ret->wcsaxes = NULL; - ret->pc = NULL; - ret->cdelt = NULL; - ret->crpix = NULL; - ret->crval = NULL; - ret->equinox = NULL; - ret->latpole = NULL; - ret->lonpole = NULL; - ret->dut1 = NULL; - ret->dtai = NULL; - ret->mjdobs = NULL; - ret->mjdavg = NULL; - ret->pv = NULL; - ret->specsys = NULL; - ret->ssyssrc = NULL; - ret->obsgeox = NULL; - ret->obsgeoy = NULL; - ret->obsgeoz = NULL; - ret->restfrq = NULL; - ret->restwav = NULL; - ret->zsource = NULL; - ret->velosys = NULL; - ret->asip = NULL; - ret->bsip = NULL; - ret->apsip = NULL; - ret->bpsip = NULL; - ret->imagfreq = NULL; - ret->axref = NULL; - ret->naxis = naxis; - ret->timesys = NULL; - ret->tables = astKeyMap( " ", status ); - ret->skyref = NULL; - ret->skyrefp = NULL; - ret->skyrefis = NULL; - -/* Obtain the index of the Base Frame (i.e. the pixel frame ). */ - ibase = astGetBase( fset ); - -/* Obtain the index of the Current Frame (i.e. the Frame to use as the - primary physical coordinate frame). */ - icurr = astGetCurrent( fset ); - -/* Does the current Frame contain a SkyFrame that describes offset - coordinates? */ - isoff = IsSkyOff( fset, icurr, status ); - -/* Add a description of the primary axes to the FitsStore, based on the - Current Frame in the FrameSet. */ - primok = AddVersion( this, fset, ibase, icurr, ret, dim, ' ', - encoding, isoff, method, class, status ); - -/* Do not add any alternate axis descriptions if the primary axis - descriptions could not be produced. */ - if( primok && astOK ) { - -/* Get the number of Frames in the FrameSet. */ - nfrm = astGetNframe( fset ); - -/* We now need to allocate a version letter to each Frame. Allocate - memory to hold the version letter assigned to each Frame. */ - sid = (char *) astMalloc( ( nfrm + 1 )*sizeof( char ) ); - -/* The frms array has an entry for each of the 26 possible version - letters (starting at A and ending at Z). Each entry holds the index of - the Frame which has been assigned that version character. Initialise - this array to indicate that no version letters have yet been assigned. */ - for( s = 'A'; s <= 'Z'; s++ ) { - frms[ (int) s ] = 0; - } - -/* Loop round all frames (excluding the current and base and IWC Frames which - do not need version letters). If the Frame has an Ident attribute consisting - of a single upper case letter, use it as its version letter unless that - letter has already been given to an earlier frame. IWC Frames are not - written out - identify them by giving them a "sid" value of 1 (an - illegal FITS axis description character). */ - for( ifrm = 1; ifrm <= nfrm; ifrm++ ){ - sid[ ifrm ] = 0; - if( ifrm != icurr && ifrm != ibase ) { - frame = astGetFrame( fset, ifrm ); - if( astChrMatchN( astGetDomain( frame ), "IWC", 3 ) ) { - sid[ ifrm ] = 1; - } else { - id = astGetIdent( frame ); - if( strlen( id ) == 1 && isupper( id[ 0 ] ) ) { - if( frms[ (int) id[ 0 ] ] == 0 ) { - frms[ (int) id[ 0 ] ] = ifrm; - sid[ ifrm ] = id[ 0 ]; - } - } - } - (void) astAnnul( frame ); - } - } - -/* Now go round all the Frames again, looking for Frames which did not - get a version letter assigned to it on the previous loop. Assign them - letters now, selected them from the letters not already assigned - (lowest to highest). */ - s = 'A' - 1; - for( ifrm = 1; ifrm <= nfrm; ifrm++ ){ - if( ifrm != icurr && ifrm != ibase && sid[ ifrm ] != 1 ) { - if( sid[ ifrm ] == 0 ){ - while( frms[ (int) ++s ] != 0 ); - if( s <= 'Z' ) { - sid[ ifrm ] = s; - frms[ (int) s ] = ifrm; - } - } - } - } - -/* If the primary headers describe offset coordinates, create an alternate - axis description for the correspondsing absolute coordinate system. */ - if( isoff && ++s <= 'Z' ) { - (void) AddVersion( this, fset, ibase, icurr, ret, dim, - s, encoding, -1, method, class, status ); - } - -/* Now go through all the other Frames in the FrameSet, attempting to - create alternate axis descriptions for each one. */ - for( ifrm = 1; ifrm <= nfrm; ifrm++ ){ - s0 = sid[ ifrm ]; - if( s0 != 0 && s0 != 1 ) { - -/* Does it contain an offset sky frame? */ - isoff = IsSkyOff( fset, ifrm, status ); - -/* Write out the Frame - offset if it is offset, absolute otherwise. */ - (void) AddVersion( this, fset, ibase, ifrm, ret, dim, - s0, encoding, isoff, method, class, status ); - -/* If the Frame is offset, create an extra alternate axis description for - the correspondsing absolute coordinate system. */ - if( isoff && ++s <= 'Z' ) { - (void) AddVersion( this, fset, ibase, ifrm, ret, dim, - s, encoding, -1, method, class, status ); - } - } - } - -/* Free memory holding version letters */ - sid = (char *) astFree( (void *) sid ); - } - -/* If an error has occurred, or if the primary Frame could not be cerated, - free the returned FitsStore, and return a null pointer. */ - if( !astOK || !primok ) ret = FreeStore( ret, status ); - } - -/* Return the answer. */ - return ret; -} - -static int GetClean( AstFitsChan *this, int *status ) { - -/* -* Name: -* GetClean - -* Purpose: -* Return the value of the Clean attribute. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int GetClean( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns the value of the Clean attribute. Since this -* attribute controls the behaviour of the FitsChan in the event of an -* error condition, it is is necessary to ignore any inherited error -* condition when getting the attribute value. This is why the -* astMAKE_GET macro is not used. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The Clean value to use. -*/ - -/* Return if no FitsChan pointer was supplied. */ - if ( !this ) return 0; - -/* Return the attribute value, supplying a default value of 0 (false). */ - return ( this->clean == -1 ) ? 0 : (this->clean ? 1 : 0 ); -} - -static int GetObjSize( AstObject *this_object, int *status ) { -/* -* Name: -* GetObjSize - -* Purpose: -* Return the in-memory size of an Object. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetObjSize( AstObject *this, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the astGetObjSize protected -* method inherited from the parent class). - -* Description: -* This function returns the in-memory size of the supplied FitsChan, -* in bytes. - -* Parameters: -* this -* Pointer to the FitsChan. -* 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: */ - AstFitsChan *this; /* Pointer to FitsChan structure */ - FitsCard *card; /* Pointer to next FitsCard */ - int result; /* Result value to return */ - -/* Initialise. */ - result = 0; - -/* Check the global error status. */ - if ( !astOK ) return result; - -/* Obtain a pointers to the FitsChan structure. */ - this = (AstFitsChan *) this_object; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* 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 ); - result += astTSizeOf( this->warnings ); - result += astGetObjSize( this->keyseq ); - result += astGetObjSize( this->keywords ); - result += astGetObjSize( this->tables ); - card = (FitsCard *) ( this->head ); - while( card ) { - result += astTSizeOf( card ); - result += card->size; - result += astTSizeOf( card->comment ); - card = GetLink( card, NEXT, "astGetObjSize", "FitsChan", status ); - if( (void *) card == this->head ) break; - } - -/* If an error occurred, clear the result value. */ - if ( !astOK ) result = 0; - -/* Return the result, */ - return result; -} - -static int GetCDMatrix( AstFitsChan *this, int *status ){ - -/* -* Name: -* GetCDMatrix - -* Purpose: -* Get the value of the CDMatrix attribute. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int GetCDMatrix( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* If the CDMatrix attribute has been set, then its value is returned. -* Otherwise, the supplied FitsChan is searched for keywords of the -* form CDi_j. If any are found a non-zero value is returned. Otherwise -* a zero value is returned. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The attribute value to use. - -* Notes: -* - A value of zero is returned if an error has already occurred -* or if an error occurs for any reason within this function. -*/ - -/* Local Variables... */ - int ret; /* Returned value */ - int icard; /* Index of current card on entry */ - -/* Check the global status. */ - if( !astOK ) return 0; - -/* If a value has been supplied for the CDMatrix attribute, use it. */ - if( astTestCDMatrix( this ) ) { - ret = this->cdmatrix; - -/* Otherwise, check for the existence of CDi_j keywords... */ - } else { - -/* Save the current card index, and rewind the FitsChan. */ - icard = astGetCard( this ); - astClearCard( this ); - -/* If the FitsChan contains any keywords with the format "CDi_j" then return - 1. Otherwise return zero. */ - ret = astKeyFields( this, "CD%1d_%1d", 0, NULL, NULL ) ? 1 : 0; - -/* Reinstate the original current card index. */ - astSetCard( this, icard ); - } - -/* Return the result. */ - return astOK ? ret : 0; -} - -static int GetEncoding( AstFitsChan *this, int *status ){ - -/* -* Name: -* GetEncoding - -* Purpose: -* Get the value of the Encoding attribute. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetEncoding( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* If the Encoding attribute has been set, then its value is returned. -* Otherwise, an attempt is made to determine the encoding scheme by -* looking for selected keywords within the FitsChan. Checks are made -* for the following keywords in the order specified, and the -* corresponding encoding is adopted when the first one is found ( where - -* i, j and m are integers and s is a single upper case character): -* -* 1) Any keywords starting with "BEGAST" = Native encoding -* 2) DELTAV and VELO-xxx (or VLSR) keywords = FITS-CLASS. -* 3) Any AIPS spectral CTYPE values: - -* Any of CDi_j, PROJP, LONPOLE, LATPOLE = FITS-AIPS++ encoding: -* None of the above = FITS-AIPS encoding. -* 4) Any keywords matching PCiiijjj = FITS-PC encoding -* 5) Any keywords matching CDiiijjj = FITS-IRAF encoding -* 6) Any keywords matching CDi_j, AND at least one of RADECSYS, PROJPi -* or CmVALi = FITS-IRAF encoding -* 7) Any keywords RADECSYS, PROJPi or CmVALi, and no CDi_j or PCi_j -* keywords, = FITS-PC encoding -* 8) Any keywords matching CROTAi = FITS-AIPS encoding -* 9) Keywords matching CRVALi = FITS-WCS encoding -* 10) The PLTRAH keyword = DSS encoding -* 11) If none of the above keywords are found, Native encoding is assumed. -* -* For cases 2) to 9), a check is also made that the header contains -* at least one of each keyword CTYPE, CRPIX and CRVAL. If not, then -* the checking process continues to the next case. This goes some way -* towards ensuring that the critical keywords used to determine the -* encoding are part of a genuine WCS description and have not just been -* left in the header by accident. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The encoding scheme identifier. - -* Notes: -* - The function returns UNKNOWN_ENCODING if an error has already occurred -* or if an error occurs for any reason within this function. -*/ - -/* Local Variables... */ - int hascd; /* Any CDi_j keywords found? */ - int haspc; /* Any PCi_j keywords found? */ - int haswcs; /* Any CRVAL, CTYPE and CRPIX found? */ - int icard; /* Index of current card on entry */ - int ret; /* Returned value */ - -/* Check the global status. */ - if( !astOK ) return UNKNOWN_ENCODING; - -/* If a value has been supplied for the Encoding attribute, use it. */ - if( astTestEncoding( this ) ) { - ret = this->encoding; - -/* Otherwise, check for the existence of certain critcal keywords... */ - } else { - -/* See if the header contains some CTYPE, CRPIX and CRVAL keywords. */ - haswcs = astKeyFields( this, "CTYPE%d", 0, NULL, NULL ) && - astKeyFields( this, "CRPIX%d", 0, NULL, NULL ) && - astKeyFields( this, "CRVAL%d", 0, NULL, NULL ); - -/* See if there are any CDi_j keywords. */ - hascd = astKeyFields( this, "CD%1d_%1d", 0, NULL, NULL ); - -/* See if there are any PCi_j keywords. */ - haspc = astKeyFields( this, "PC%1d_%1d", 0, NULL, NULL ); - -/* Save the current card index, and rewind the FitsChan. */ - icard = astGetCard( this ); - astClearCard( this ); - -/* If the FitsChan contains any keywords starting with "BEGAST", then return - "Native" encoding. */ - if( astKeyFields( this, "BEGAST%2f", 0, NULL, NULL ) ){ - ret = NATIVE_ENCODING; - -/* Otherwise, look for a FITS-CLASS signature... */ - } else if( haswcs && LooksLikeClass( this, "astGetEncoding", "AstFitsChan", status ) ){ - ret = FITSCLASS_ENCODING; - -/* Otherwise, if the FitsChan contains any CTYPE keywords which have the - peculiar form used by AIPS, then use "FITS-AIPS" or "FITS-AIPS++" encoding. */ - } else if( haswcs && HasAIPSSpecAxis( this, "astGetEncoding", "AstFitsChan", status ) ){ - if( hascd || - astKeyFields( this, "PROJP%d", 0, NULL, NULL ) || - astKeyFields( this, "LONPOLE", 0, NULL, NULL ) || - astKeyFields( this, "LATPOLE", 0, NULL, NULL ) ) { - ret = FITSAIPSPP_ENCODING; - } else { - ret = FITSAIPS_ENCODING; - } - -/* Otherwise, if the FitsChan contains the "PLTRAH" keywords, use "DSS" - encoding. */ - } else if( astKeyFields( this, "PLTRAH", 0, NULL, NULL ) ){ - ret = DSS_ENCODING; - -/* Otherwise, if the FitsChan contains any keywords with the format - "PCiiijjj" then return "FITS-PC" encoding. */ - } else if( haswcs && astKeyFields( this, "PC%3d%3d", 0, NULL, NULL ) ){ - ret = FITSPC_ENCODING; - -/* Otherwise, if the FitsChan contains any keywords with the format - "CDiiijjj" then return "FITS-IRAF" encoding. */ - } else if( haswcs && astKeyFields( this, "CD%3d%3d", 0, NULL, NULL ) ){ - ret = FITSIRAF_ENCODING; - -/* Otherwise, if the FitsChan contains any keywords with the format - "CDi_j" AND there is a RADECSYS. PROJPi or CmVALi keyword, then return - "FITS-IRAF" encoding. If "CDi_j" is present but none of the others - are, return "FITS-WCS" encoding. */ - } else if( haswcs && hascd ) { - if( ( astKeyFields( this, "RADECSYS", 0, NULL, NULL ) && - !astKeyFields( this, "RADESYS", 0, NULL, NULL ) ) || - ( astKeyFields( this, "PROJP%d", 0, NULL, NULL ) && - !astKeyFields( this, "PV%d_%d", 0, NULL, NULL ) ) || - ( astKeyFields( this, "C%1dVAL%d", 0, NULL, NULL )) ){ - ret = FITSIRAF_ENCODING; - } else { - ret = FITSWCS_ENCODING; - } - -/* Otherwise, if the FitsChan contains any keywords with the format - RADECSYS. PROJPi or CmVALi keyword, then return "FITS-PC" encoding, - so long as there are no FITS-WCS equivalent keywords. */ - } else if( haswcs && !haspc && !hascd && ( - ( astKeyFields( this, "RADECSYS", 0, NULL, NULL ) && - !astKeyFields( this, "RADESYS", 0, NULL, NULL ) ) || - ( astKeyFields( this, "PROJP%d", 0, NULL, NULL ) && - !astKeyFields( this, "PV%d_%d", 0, NULL, NULL ) ) || - astKeyFields( this, "C%1dVAL%d", 0, NULL, NULL ) ) ) { - ret = FITSPC_ENCODING; - -/* Otherwise, if the FitsChan contains any keywords with the format - "CROTAi" then return "FITS-AIPS" encoding. */ - } else if( haswcs && astKeyFields( this, "CROTA%d", 0, NULL, NULL ) ){ - ret = FITSAIPS_ENCODING; - -/* Otherwise, if the FitsChan contains any keywords with the format - "CRVALi" then return "FITS-WCS" encoding. */ - } else if( haswcs && astKeyFields( this, "CRVAL%d", 0, NULL, NULL ) ){ - ret = FITSWCS_ENCODING; - -/* If none of these conditions is met, assume Native encoding. */ - } else { - ret = NATIVE_ENCODING; - } - -/* Reinstate the original current card index. */ - astSetCard( this, icard ); - } - -/* Return the encoding scheme. */ - return astOK ? ret : UNKNOWN_ENCODING; -} - -static void GetFiducialNSC( AstWcsMap *map, double *phi, double *theta, int *status ){ -/* -* Name: -* GetFiducialNSC - -* Purpose: -* Return the Native Spherical Coordinates at the fiducial point of a -* WcsMap projection. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void GetFiducialNSC( AstWcsMap *map, double *phi, double *theta, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns the native spherical coords corresponding at -* the fiducial point of a WcsMap. -* -* The values of parameters 1 and 2 on the longitude axis of the WcsMap -* are usually used as the native spherical coordinates of the -* fiducial point. The default values for these parameters are equal -* to the native spherical coordinates of the projection reference point. -* The exception is that a TPN projection always uses the default -* values, since the projection parameters are used to store polynomial -* coefficients. - -* Parameters: -* map -* Pointer to the WcsMap. -* phi -* Address of a location at which to return the native spherical -* longitude at the fiducial point (radians). -* theta -* Address of a location at which to return the native spherical -* latitude at the fiducial point (radians). -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - int axlon; /* Index of longitude axis */ - -/* Initialise */ - *phi = AST__BAD; - *theta = AST__BAD; - -/* Check the inherited status. */ - if( !astOK ) return; - -/* If this is not a TPN projection get he value of the required - projection parameters (the default values for these are equal to the - fixed native shperical coordinates at the projection reference point). */ - if( astGetWcsType( map ) != AST__TPN ) { - axlon = astGetWcsAxis( map, 0 ); - if( astGetPV( map, axlon, 0 ) != 0.0 ) { - *phi = AST__DD2R*astGetPV( map, axlon, 1 ); - *theta = AST__DD2R*astGetPV( map, axlon, 2 ); - } else { - *phi = astGetNatLon( map ); - *theta = astGetNatLat( map ); - } - -/* If this is a TPN projection, the returned values are always the fixed - native shperical coordinates at the projection reference point). */ - } else { - *phi = astGetNatLon( map ); - *theta = astGetNatLat( map ); - } -} - -static void GetFiducialPPC( AstWcsMap *map, double *x0, double *y0, int *status ){ -/* -* Name: -* GetFiducialPPC - -* Purpose: -* Return the IWC at the fiducial point of a WcsMap projection. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void GetFiducialPPC( AstWcsMap *map, double *x0, double *y0, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns the projection plane coords corresponding to -* the native spherical coords of the fiducial point of a FITS-WCS -* header. Note, projection plane coordinates (PPC) are equal to -* Intermediate World Coordinates (IWC) except for cases where the -* fiducial point does not correspond to the projection reference point. -* In these cases, IWC and PPC will be connected by a translation -* which ensures that the fiducial point corresponds to the origin of -* IWC. -* -* The values of parameters 1 and 2 on the longitude axis of -* the WcsMap are used as the native spherical coordinates of the -* fiducial point. The default values for these parameters are equal -* to the native spherical coordinates of the projection reference point. - -* Parameters: -* map -* Pointer to the WcsMap. -* x0 -* Address of a location at which to return the PPC X axis value at -* the fiducial point (radians). -* y0 -* Address of a location at which to return the PPC Y axis value at -* the fiducial point (radians). -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - AstPointSet *pset1; /* Pointer to the native spherical PointSet */ - AstPointSet *pset2; /* Pointer to the intermediate world PointSet */ - double **ptr1; /* Pointer to pset1 data */ - double **ptr2; /* Pointer to pset2 data */ - int axlat; /* Index of latitude axis */ - int axlon; /* Index of longitude axis */ - int i; /* Loop count */ - int naxes; /* Number of axes */ - -/* Initialise */ - *x0 = AST__BAD; - *y0 = AST__BAD; - -/* Check the inherited status. */ - if( !astOK ) return; - -/* Save number of axes in the WcsMap. */ - naxes = astGetNin( map ); - -/* Allocate resources. */ - pset1 = astPointSet( 1, naxes, "", status ); - ptr1 = astGetPoints( pset1 ); - pset2 = astPointSet( 1, naxes, "", status ); - ptr2 = astGetPoints( pset2 ); - -/* Check pointers can be used safely. */ - if( astOK ) { - -/* Get the indices of the longitude and latitude axes in WcsMap. */ - axlon = astGetWcsAxis( map, 0 ); - axlat = astGetWcsAxis( map, 1 ); - -/* Use zero on all non-celestial axes. */ - for( i = 0; i < naxes; i++ ) ptr1[ i ][ 0 ] = 0.0; - -/* Get the native spherical coords at the fiducial point. */ - GetFiducialNSC( map, ptr1[ axlon ], ptr1[ axlat ], status ); - -/* Use the inverse WcsMap to convert the native longitude and latitude of - the fiducial point into PPC (x,y). */ - (void) astTransform( map, pset1, 0, pset2 ); - -/* Return the calculated PPC coords. */ - *x0 = ptr2[ axlon ][ 0 ]; - *y0 = ptr2[ axlat ][ 0 ]; - } - -/* Free resources. */ - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); -} - -static int GetFiducialWCS( AstWcsMap *wcsmap, AstMapping *map2, int colon, - int colat, double *fidlon, double *fidlat, int *status ){ -/* -* Name: -* GetFiducialWCS - -* Purpose: -* Decide on the celestial coordinates of the fiducial point. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetFiducialWCS( AstWcsMap wcsmap, AstMapping map2, int colon, -* int colat, double *fidlon, double *fidlat, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns the celestial longitude and latitude values -* to use for the fiducial point. These are the values stored in FITS -* keywords CRVALi. - -* Parameters: -* wcsmap -* The WcsMap which converts Projection Plane Coordinates into -* native spherical coordinates. The number of outputs from this -* Mapping should match the number of inputs to "map2". -* map2 -* The Mapping which converts native spherical coordinates into WCS -* coordinates. -* colon -* The index of the celestial longitude output from "map2". -* colat -* The index of the celestial latitude output from "map2". -* fidlon -* Pointer to a location at which to return the celestial longitude -* value at the fiducial point. The value is returned in radians. -* fidlat -* Pointer to a location at which to return the celestial latitude -* value at the fiducial point. The value is returned in radians. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero if the fiducial point longitude or latitude could not be -* determined. One otherwise. -*/ - -/* Local Variables: */ - AstPointSet *pset1; /* Pointer to the native spherical PointSet */ - AstPointSet *pset2; /* Pointer to the WCS PointSet */ - double **ptr1; /* Pointer to pset1 data */ - double **ptr2; /* Pointer to pset2 data */ - int axlat; /* Index of latitude axis */ - int axlon; /* Index of longitude axis */ - int iax; /* Axis index */ - int naxin; /* Number of IWC axes */ - int naxout; /* Number of WCS axes */ - int ret; /* The returned FrameSet */ - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Allocate resources. */ - naxin = astGetNin( map2 ); - naxout = astGetNout( map2 ); - pset1 = astPointSet( 1, naxin, "", status ); - ptr1 = astGetPoints( pset1 ); - pset2 = astPointSet( 1, naxout, "", status ); - ptr2 = astGetPoints( pset2 ); - if( astOK ) { - -/* Get the indices of the latitude and longitude outputs in the WcsMap. - These are not necessarily the same as "colat" and "colon" because "map2" - may contain a PermMap. */ - axlon = astGetWcsAxis( wcsmap, 0 ); - axlat = astGetWcsAxis( wcsmap, 1 ); - -/* Use zero on all non-celestial axes. */ - for( iax = 0; iax < naxin; iax++ ) ptr1[ iax ][ 0 ] = 0.0; - -/* Get the native spherical coords at the fiducial point. */ - GetFiducialNSC( wcsmap, ptr1[ axlon ], ptr1[ axlat ], status ); - -/* The fiducial point in the celestial coordinate system is found by - transforming the fiducial point in native spherical co-ordinates - into absolute physical coordinates using map2. */ - (void) astTransform( map2, pset1, 1, pset2 ); - -/* Store the returned WCS values. */ - *fidlon = ptr2[ colon ][ 0 ]; - *fidlat = ptr2[ colat ][ 0 ]; - -/* Indicate if we have been succesfull. */ - if( astOK && *fidlon != AST__BAD && *fidlat != AST__BAD ) ret = 1; - } - -/* Free resources. */ - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - -/* Return the result. */ - return ret; -} - -static const char *GetFitsSor( const char *string, int *status ) { -/* -* Name: -* GetFitsSor - -* Purpose: -* Get the string used to represent an AST spectral standard of rest. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* const char *GetFitsSor( const char *string, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns a pointer to a static string which is the -* FITS equivalent to a given SpecFrame StdOfRest value. - -* Parameters: -* string -* Pointer to a constant null-terminated string containing the -* SpecFrame StdOfRest value. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Pointer to a static null-terminated string containing the FITS -* equivalent to the supplied string. NULL is returned if the supplied -* string has no FITS equivalent. - -* Notes: -* - A NULL pointer value will be returned if this function is -* invoked wth the global error status set, or if it should fail -* for any reason. -*/ - -/* Local Variables: */ - const char *result; /* Pointer value to return */ - -/* Check the global error status. */ - if ( !astOK ) return NULL; - -/* Compare the supplied string with SpecFrame value for which there is a - known FITS equivalent. */ - if( !strcmp( string, "Topocentric" ) ){ - result = "TOPOCENT"; - } else if( !strcmp( string, "Geocentric" )){ - result = "GEOCENTR"; - } else if( !strcmp( string, "Barycentric" )){ - result = "BARYCENT"; - } else if( !strcmp( string, "Heliocentric" )){ - result = "HELIOCEN"; - } else if( !strcmp( string, "LSRK" )){ - result = "LSRK"; - } else if( !strcmp( string, "LSRD" )){ - result = "LSRD"; - } else if( !strcmp( string, "Galactic" )){ - result = "GALACTOC"; - } else if( !strcmp( string, "Local_group" )){ - result = "LOCALGRP"; - } else if( !strcmp( string, "Source" )){ - result = "SOURCE"; - } else { - result = NULL; - } - -/* Return the answer. */ - return result; -} - -static double GetItem( double ****item, int i, int jm, char s, char *name, - const char *method, const char *class, int *status ){ -/* -* Name: -* GetItem - -* Purpose: -* Retrieve a value for a axis keyword value from a FitStore structure. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* double GetItem( double ****item, int i, int jm, char s, char *name, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The requested keyword value is retrieved from the specified array, -* at a position indicated by the axis and co-ordinate version. -* AST__BAD is returned if the array does not contain the requested -* value. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->crval) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (i), and the pointer locates an -* array of axis keyword values. These arrays of keyword values have -* one element for every pixel axis (j) or projection parameter (m). -* i -* The zero based intermediate axis index in the range 0 to 98. Set -* this to zero for keywords (e.g. CRPIX) which are not indexed by -* intermediate axis number. -* jm -* The zero based pixel axis index (in the range 0 to 98) or parameter -* index (in the range 0 to WCSLIB_MXPAR-1). Set this to zero for -* keywords (e.g. CRVAL) which are not indexed by either pixel axis or -* parameter number. -* s -* The co-ordinate version character (A to Z, or space), case -* insensitive -* name -* A string holding a name for the item of information. A NULL -* pointer may be supplied, in which case it is ignored. If a -* non-NULL pointer is supplied, an error is reported if the item -* of information has not been stored, and the supplied name is -* used to identify the information within the error message. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The required keyword value, or AST__BAD if no value has previously -* been stored for the keyword (or if an error has occurred). -*/ - -/* Local Variables: */ - double ret; /* Returned keyword value */ - int si; /* Integer co-ordinate version index */ - -/* Initialise */ - ret = AST__BAD; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Convert the character co-ordinate version into an integer index, and - check it is within range. The primary axis description (s=' ') is - given index zero. 'A' is 1, 'B' is 2, etc. */ - if( s == ' ' ) { - si = 0; - } else if( islower(s) ){ - si = (int) ( s - 'a' ) + 1; - } else { - si = (int) ( s - 'A' ) + 1; - } - if( si < 0 || si > 26 ) { - astError( AST__INTER, "GetItem(fitschan): AST internal error; " - "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); - -/* Check the intermediate axis index is within range. */ - } else if( i < 0 || i > 98 ) { - astError( AST__INTER, "GetItem(fitschan): AST internal error; " - "intermediate axis index %d is invalid.", status, i ); - -/* Check the pixel axis or parameter index is within range. */ - } else if( jm < 0 || jm > 99 ) { - astError( AST__INTER, "GetItem(fitschan): AST internal error; " - "pixel axis or parameter index %d is invalid.", status, jm ); - -/* Otherwise, if the array holding the required keyword is not null, - proceed... */ - } else if( *item ){ - -/* Find the number of coordinate versions in the supplied array. - Only proceed if it encompasses the requested co-ordinate - version. */ - if( astSizeOf( (void *) *item )/sizeof(double **) > si ){ - -/* Find the number of intermediate axes in the supplied array. - Only proceed if it encompasses the requested intermediate axis. */ - if( astSizeOf( (void *) (*item)[si] )/sizeof(double *) > i ){ - -/* Find the number of pixel axes or parameters in the supplied array. - Only proceed if it encompasses the requested index. */ - if( astSizeOf( (void *) (*item)[si][i] )/sizeof(double) > jm ){ - -/* Return the required keyword value. */ - ret = (*item)[si][i][jm]; - } - } - } - } - -/* If required, report an error if the requested item of information has - not been stored. */ - if( ret == AST__BAD && name && astOK ){ - astError( AST__NOFTS, "%s(%s): No value can be found for %s.", status, - method, class, name ); - } - return ret; -} - -static int GetMaxJM( double ****item, char s, int *status ){ -/* -* Name: -* GetMaxJM - -* Purpose: -* Return the largest pixel axis or parameter index stored for an -* numerical axis keyword value in a FitStore structure. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetMaxJM( double ****item, char s, int *status) - -* Class Membership: -* FitsChan member function. - -* Description: -* The number of pixel axis numbers or projection parameters stored for -* a specified axis keyword is found and returned. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->crpix) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (i), and the pointer locates an -* array of axis keyword values. These arrays of keyword values have -* one element for every pixel axis (j) or projection parameter (m). -* s -* The co-ordinate version character (A to Z, or space), case -* insensitive -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The maximum pixel axis number or projection parameter index (zero -* based). -*/ - -/* Local Variables: */ - int jm; /* Number of parameters/pixel axes */ - int i; /* Intermediate axis index */ - int ret; /* Returned axis index */ - int si; /* Integer co-ordinate version index */ - -/* Initialise */ - ret = -1; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* If the array holding the required keyword is not null, proceed... */ - if( *item ){ - -/* Convert the character co-ordinate version into an integer index, and - check it is within range. The primary axis description (s=' ') is - given index zero. 'A' is 1, 'B' is 2, etc. */ - if( s == ' ' ) { - si = 0; - } else if( islower(s) ){ - si = (int) ( s - 'a' ) + 1; - } else { - si = (int) ( s - 'A' ) + 1; - } - if( si < 0 || si > 26 ) { - astError( AST__INTER, "GetMaxJM(fitschan): AST internal error; " - "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); - return ret; - } - -/* Find the number of coordinate versions in the supplied array. - Only proceed if it encompasses the requested co-ordinate - version. */ - if( astSizeOf( (void *) *item )/sizeof(double **) > si ){ - -/* Check that the pointer to the array of intermediate axis values is not null. */ - if( (*item)[si] ){ - -/* Loop round each used element in this array. */ - for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(double *); - i++ ){ - if( (*item)[si][i] ){ - -/* Get the size of the pixel axis/projection parameter array for the - current intermediate axis, and subtract 1 to get the largest index. */ - jm = astSizeOf( (void *) (*item)[si][i] )/sizeof(double) - 1; - -/* Ignore any trailing unused (AST__BAD) values. */ - while( jm >= 0 && (*item)[si][i][jm] == AST__BAD ) jm--; - -/* Update the returned value if the current value is larger. */ - if( jm > ret ) ret = jm; - } - } - } - } - } - return ret; -} - -static int GetMaxJMC( char *****item, char s, int *status ){ -/* -* Name: -* GetMaxJMC - -* Purpose: -* Return the largest pixel axis or parameter index stored for an -* character-valued axis keyword value in a FitStore structure. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetMaxJMC( char *****item, char s, int *status) - -* Class Membership: -* FitsChan member function. - -* Description: -* The number of pixel axis numbers or projection parameters stored for -* a specified axis keyword is found and returned. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->ctype) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (i), and the pointer locates an -* array of axis keyword string pointers. These arrays of keyword -* string pointers have one element for every pixel axis (j) or -* projection parameter (m). -* s -* The co-ordinate version character (A to Z, or space), case -* insensitive -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The maximum pixel axis number or projection parameter index (zero -* based). -*/ - -/* Local Variables: */ - int jm; /* Number of parameters/pixel axes */ - int i; /* Intermediate axis index */ - int ret; /* Returned axis index */ - int si; /* Integer co-ordinate version index */ - -/* Initialise */ - ret = -1; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* If the array holding the required keyword is not null, proceed... */ - if( *item ){ - -/* Convert the character co-ordinate version into an integer index, and - check it is within range. The primary axis description (s=' ') is - given index zero. 'A' is 1, 'B' is 2, etc. */ - if( s == ' ' ) { - si = 0; - } else if( islower(s) ){ - si = (int) ( s - 'a' ) + 1; - } else { - si = (int) ( s - 'A' ) + 1; - } - if( si < 0 || si > 26 ) { - astError( AST__INTER, "GetMaxJMC(fitschan): AST internal error; " - "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); - return ret; - } - -/* Find the number of coordinate versions in the supplied array. - Only proceed if it encompasses the requested co-ordinate - version. */ - if( astSizeOf( (void *) *item )/sizeof(char ***) > si ){ - -/* Check that the pointer to the array of intermediate axis values is not null. */ - if( (*item)[si] ){ - -/* Loop round each used element in this array. */ - for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(char **); - i++ ){ - if( (*item)[si][i] ){ - -/* Get the size of the pixel axis/projection parameter array for the - current intermediate axis, and subtract 1 to get the largest index. */ - jm = astSizeOf( (void *) (*item)[si][i] )/sizeof(char *) - 1; - -/* Ignore any trailing unused (NULL) values. */ - while( jm >= 0 && (*item)[si][i][jm] == NULL ) jm--; - -/* Update the returned value if the current value is larger. */ - if( jm > ret ) ret = jm; - } - } - } - } - } - return ret; -} - -static int GetMaxI( double ****item, char s, int *status ){ -/* -* Name: -* GetMaxI - -* Purpose: -* Return the largest WCS axis index stored for an axis keyword value in -* a FitStore structure. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetMaxJM( double ****item, char s) - -* Class Membership: -* FitsChan member function. - -* Description: -* The number of Wcs axis numbers stored for a specified axis keyword is -* found and returned. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->crval) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (i), and the pointer locates an -* array of axis keyword values. These arrays of keyword values have -* one element for every pixel axis (j) or projection parameter (m). -* s -* The co-ordinate version character (A to Z, or space), case -* insensitive - -* Returned Value: -* The maximum WCS axis index (zero based). -*/ - -/* Local Variables: */ - int ret; /* Returned axis index */ - int si; /* Integer co-ordinate version index */ - -/* Initialise */ - ret = -1; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* If the array holding the required keyword is not null, proceed... */ - if( *item ){ - -/* Convert the character co-ordinate version into an integer index, and - check it is within range. The primary axis description (s=' ') is - given index zero. 'A' is 1, 'B' is 2, etc. */ - if( s == ' ' ) { - si = 0; - } else if( islower(s) ){ - si = (int) ( s - 'a' ) + 1; - } else { - si = (int) ( s - 'A' ) + 1; - } - if( si < 0 || si > 26 ) { - astError( AST__INTER, "GetMaxI(fitschan): AST internal error; " - "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); - return ret; - } - -/* Find the number of coordinate versions in the supplied array. - Only proceed if it encompasses the requested co-ordinate - version. */ - if( astSizeOf( (void *) *item )/sizeof(double **) > si ){ - -/* Check that the pointer to the array of intermediate axis values is not null. */ - if( (*item)[si] ){ - -/* Get the size of the intermediate axis array and subtract 1 to get the largest - index. */ - ret = astSizeOf( (void *) (*item)[si] )/sizeof(double *) - 1; - -/* Ignore any trailing unused (NULL) values. */ - while( ret >= 0 && (*item)[si][ret] == NULL ) ret--; - } - } - } - return ret; -} - -static char GetMaxS( double ****item, int *status ){ -/* -* Name: -* GetMaxS - -* Purpose: -* Return the largest (i.e. closest to Z) coordinate version character -* stored for a axis keyword value in a FitStore structure. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* char GetMaxS( double ****item, int *status) - -* Class Membership: -* FitsChan member function. - -* Description: -* The largest (i.e. closest to Z) coordinate version character -* stored for a axis keyword value in a FitStore structure is found -* and returned. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->crval) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (i), and the pointer locates an -* array of axis keyword values. These arrays of keyword values have -* one element for every pixel axis (j) or projection parameter (m). -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The highest coordinate version character. -*/ - -/* Local Variables: */ - char ret; /* Returned axis index */ - int si; /* Integer index into alphabet */ - -/* Initialise */ - ret = ' '; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* If the array holding the required keyword is not null, proceed... */ - if( *item ){ - -/* Find the length of this array, and subtract 1 to get the largest index - in the array. */ - si = astSizeOf( (void *) *item )/sizeof(double **) - 1; - -/* Ignore any trailing null (i.e. unused) values. */ - while( si >= 0 && !(*item)[si] ) si--; - -/* Store the corresponding character */ - if( si == 0 ) { - ret = ' '; - } else { - ret = 'A' + si - 1; - } - } - return ret; -} - -static char *GetItemC( char *****item, int i, int jm, char s, char *name, - const char *method, const char *class, int *status ){ -/* -* Name: -* GetItemC - -* Purpose: -* Retrieve a string value for a axis keyword value from a FitStore -* structure. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* char *GetItemC( char *****item, int i, int jm, char s, char *name, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The requested keyword string value is retrieved from the specified -* array, at a position indicated by the axis and co-ordinate version. -* NULL is returned if the array does not contain the requested -* value. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->ctype) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (i), and the pointer locates an -* array of axis keyword string pointers. These arrays of keyword -* string pointers have one element for every pixel axis (j) or -* projection parameter (m). -* i -* The zero based intermediate axis index in the range 0 to 98. Set -* this to zero for keywords (e.g. CRPIX) which are not indexed by -* intermediate axis number. -* jm -* The zero based pixel axis index (in the range 0 to 98) or parameter -* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for -* keywords (e.g. CTYPE) which are not indexed by either pixel axis or -* parameter number. -* s -* The co-ordinate version character (A to Z, or space), case -* insensitive -* name -* A string holding a name for the item of information. A NULL -* pointer may be supplied, in which case it is ignored. If a -* non-NULL pointer is supplied, an error is reported if the item -* of information has not been stored, and the supplied name is -* used to identify the information within the error message. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the required keyword string value, or NULL if no value -* has previously been stored for the keyword (or if an error has -* occurred). -*/ - -/* Local Variables: */ - char *ret; /* Returned keyword value */ - int si; /* Integer co-ordinate version index */ - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Convert the character co-ordinate version into an integer index, and - check it is within range. The primary axis description (s=' ') is - given index zero. 'A' is 1, 'B' is 2, etc. */ - if( s == ' ' ) { - si = 0; - } else if( islower(s) ){ - si = (int) ( s - 'a' ) + 1; - } else { - si = (int) ( s - 'A' ) + 1; - } - if( si < 0 || si > 26 ) { - astError( AST__INTER, "GetItemC(fitschan): AST internal error; " - "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); - -/* Check the intermediate axis index is within range. */ - } else if( i < 0 || i > 98 ) { - astError( AST__INTER, "GetItemC(fitschan): AST internal error; " - "intermediate axis index %d is invalid.", status, i ); - -/* Check the pixel axis or parameter index is within range. */ - } else if( jm < 0 || jm > 99 ) { - astError( AST__INTER, "GetItem(fitschan): AST internal error; " - "pixel axis or parameter index %d is invalid.", status, jm ); - -/* Otherwise, if the array holding the required keyword is not null, - proceed... */ - } else if( *item ){ - -/* Find the number of coordinate versions in the supplied array. - Only proceed if it encompasses the requested co-ordinate - version. */ - if( astSizeOf( (void *) *item )/sizeof(char ***) > si ){ - -/* Find the number of intermediate axes in the supplied array. - Only proceed if it encompasses the requested intermediate axis. */ - if( astSizeOf( (void *) (*item)[si] )/sizeof(char **) > i ){ - -/* Find the number of pixel axes or parameters in the supplied array. - Only proceed if it encompasses the requested index. */ - if( astSizeOf( (void *) (*item)[si][i] )/sizeof(char *) > jm ){ - -/* Return the required keyword value. */ - ret = (*item)[si][i][jm]; - } - } - } - } - -/* If required, report an error if the requested item of information has - not been stored. */ - if( !ret && name && astOK ){ - astError( AST__NOFTS, "%s(%s): No value can be found for %s.", status, - method, class, name ); - } - return ret; -} - -static AstFitsTable *GetNamedTable( AstFitsChan *this, const char *extname, - int extver, int extlevel, int report, - const char *method, int *status ){ - -/* -* Name: -* GetNamedTable - -* Purpose: -* Return a FitsTable holding the contents of a named FITS binary table. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* AstFitsTable *GetNamedTable( AstFitsChan *this, const char *extname, -* int extver, int extlevel, int report, -* const char *method, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* If a table source function has been registered with FitsChan (using -* astTableSource), invoke it to read the required table from the external -* FITS file. If the extension is available in the FITS file, this will -* put a FitsTable into the "tables" KeyMap in the FitsChan structure, -* using the FITS extension name as the key - this will replace any -* FitsTable already present in the KeyMap with the same key. Finally, -* return a pointer to the FitsTable stored in the KeyMap - if any. -* -* This strategy allows the astPutTables or astPutTable method to be used -* as an alternative to registering a table source function with the -* FitsChan. Note, any table read using the source function is used -* in preference to any table stored in the FitsChan by an earlier call -* to astPutTables/astPutTable. - -* Parameters: -* this -* Pointer to the FitsChan. -* extname -* The key associated with the required table - should be the name -* of the FITS extension containing the binary table. -* extver -* The FITS "EXTVER" value for the required table. -* extlevel -* The FITS "EXTLEVEL" value for the required table. -* report -* If non-zero, report an error if the named table is not available. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Pointer to the FitsTable, or NULL if the table is not avalable. -*/ - -/* Local Variables: */ - AstFitsTable *ret; - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Fitrst attempt to read the required table from the external FITS file. - Only proceed if table source function and wrapper have been supplied - using astTableSource. */ - if( this->tabsource && this->tabsource_wrap ){ - -/* Invoke the table source function asking it to place the required FITS - table in the FitsChan. This is an externally supplied function which may - not be thread-safe, so lock a mutex first. Note, a cloned FitsChan pointer - is sent to the table source function since the table source function will - annul the supplied FitsChan pointer. Also store the channel data - pointer in a global variable so that it can be accessed in the source - function using macro astChannelData. */ - astStoreChannelData( this ); - LOCK_MUTEX2; - ( *this->tabsource_wrap )( this->tabsource, astClone( this ), extname, - extver, extlevel, status ); - UNLOCK_MUTEX2; - } - -/* Now get a pointer to the required FitsTable, stored as an entry in the - "tables" KeyMap. Report an error if required. */ - if( ! (this->tables) || !astMapGet0A( this->tables, extname, &ret ) ){ - if( report && astOK ) { - astError( AST__NOTAB, "%s(%s): Failed to read FITS binary table " - "from extension '%s' (extver=%d, extlevel=%d).", status, - method, astGetClass( this ), extname, extver, extlevel ); - } - } - -/* Return the result. */ - return ret; -} - -static AstKeyMap *GetTables( AstFitsChan *this, int *status ) { - -/* -*++ -* Name: -c astGetTables -f AST_GETTABLES - -* Purpose: -* Retrieve any FitsTables currently in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" -c AstKeyMap *astGetTables( AstFitsChan *this ) -f RESULT = AST_GETTABLES( THIS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -* If the supplied FitsChan currently contains any tables, then this -* function returns a pointer to a KeyMap. Each entry in the KeyMap -* is a pointer to a FitsTable holding the data for a FITS binary -* table. The key used to access each entry is the FITS extension -* name in which the table should be stored. -* -* Tables can be present in a FitsChan as a result either of using the -c astPutTable (or astPutTables) -f AST_PUTTABLE (or AST_PUTTABLES) -* method to store existing tables in the FitsChan, or of using the -c astWrite -f AST_WRITE -* method to write a FrameSet to the FitsChan. For the later case, if -* the FitsChan "TabOK" attribute is positive and the FrameSet requires -* a look-up table to describe one or more axes, then the "-TAB" -* algorithm code described in FITS-WCS paper III is used and the table -* values are stored in the FitsChan in the form of a FitsTable object -* (see the documentation for the "TabOK" attribute). - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Returned Value: -c astGetTables() -f AST_GETTABLES = INTEGER -* A pointer to a deep copy of the KeyMap holding the tables currently -* in the FitsChan, or -c NULL -f AST__NULL -* if the FitsChan does not contain any tables. The returned -* pointer should be annulled using -c astAnnul -f AST_ANNUL -* when no longer needed. - -* Notes: -* - 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: */ - AstKeyMap *result; /* Pointer value to return */ - -/* Initialise. */ - result = NULL; - -/* Check the global error status. */ - if ( !astOK ) return result; - -/* If the FitsChan contains any tables, return a pointer to a copy of - the KeyMap containing them. Otherwise, return a NULL pointer. */ - if( this->tables && astMapSize( this->tables ) > 0 ) { - result = astCopy( this->tables ); - } - -/* Return the result. */ - return result; -} - -static int GetUsedPolyTan( AstFitsChan *this, AstFitsChan *out, int latax, - int lonax, char s, const char *method, - const char *class, int *status ){ -/* -* Name: -* GetUsedPolyTan - -* Purpose: -* Get the value to use for the PolyTan attribute. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetUsedPolyTan( AstFitsChan *this, AstFitsChan *out, int latax, -* int lonax, char s, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* If the PolyTan attribute is zero or positive, then its value is -* returned. If it is negative, the supplied FitsChan is searched for -* keywords of the form PVi_m. If any are found on the latitude axis, -* or if any are found on the longitude axis with "m" > 4, +1 is -* returned (meaning "use the distorted TAN conventio"). Otherwise 0 -* is returned (meaning "use the standard TAN convention"). -* -* If all the PVi_m values for m > 0 on either axis are zero, a warning is -* issued and zero is returned. - -* Parameters: -* this -* Pointer to the FitsChan. -* out -* Pointer to a secondary FitsChan. If the PV values in "this" are -* found to be unusable, they will be marked as used in both "this" -* and "out". -* latax -* The one-based index of the latitude axis within the FITS header. -* lonax -* The one-based index of the longitude axis within the FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The attribute value to use. - -* Notes: -* - A value of zero is returned if an error has already occurred -* or if an error occurs for any reason within this function. -*/ - -/* Local Variables... */ - char template[ 20 ]; - double pval; - int lbnd_lat; - int lbnd_lon; - int m; - int nfound1; - int nfound2; - int ok; - int ret; - int ubnd_lat; - int ubnd_lon; - -/* Check the global status. */ - if( !astOK ) return 0; - -/* Get the value of the PolyTan attribute. */ - ret = astGetPolyTan( this ); - -/* If it is negative, we examine the FitsChan to see which convention to - use. */ - if( ret < 0 ) { - ret = 0; - -/* Search the FitsChan for latitude PV cards. */ - if( s != ' ' ) { - sprintf( template, "PV%d_%%d%c", latax, s ); - } else { - sprintf( template, "PV%d_%%d", latax ); - } - nfound1 = astKeyFields( this, template, 1, &ubnd_lat, &lbnd_lat ); - -/* Search the FitsChan for longitude PV cards. */ - if( s != ' ' ) { - sprintf( template, "PV%d_%%d%c", lonax, s ); - } else { - sprintf( template, "PV%d_%%d", lonax ); - } - nfound2 = astKeyFields( this, template, 1, &ubnd_lon, &lbnd_lon ); - -/* If any were found with "m" value greater than 4, assume the distorted - TAN convention is in use. Otherwise assume the stdanrd TAN convention is - in use. */ - if( nfound1 || ( nfound2 && ubnd_lon > 4 ) ) ret = 1; - -/* If the distorted TAN convention is to be used, check that at least one - of the PVi_m values is non-zero on each axis. We ignore the PVi_0 - (constant) terms in this check. */ - if( ret > 0 ) { - -/* Do the latitude axis first, skipping the first (constant) term. Assume - that all latitude pV values are zero until we find one that is not. */ - ok = 0; - for( m = 1; m <= ubnd_lat && !ok; m++ ) { - -/* Form the PVi_m keyword name. */ - if( s != ' ' ) { - sprintf( template, "PV%d_%d%c", latax, m, s ); - } else { - sprintf( template, "PV%d_%d", latax, m ); - } - -/* Get it's value. */ - if( ! GetValue( this, template, AST__FLOAT, &pval, 0, 0, - method, class, status ) ) { - -/* If the PVi_m header is not present in the FitsChan, use a default value. */ - pval = ( m == 1 ) ? 1.0 : 0.0; - } - -/* If the PVi_m header has a non-zero value, we can leave the loop. */ - if( pval != 0.0 ) ok = 1; - } - -/* If all the latitude PVi_m values are zero, issue a warning and return - zero, indicating that a simple undistorted TAN projection should be used. */ - if( !ok ) { - Warn( this, "badpv", "This FITS header describes a distorted TAN " - "projection, but all the distortion coefficients (the " - "PVi_m headers) on the latitude axis are zero.", method, - class, status ); - ret = 0; - - -/* Also, delete the PV keywords so that no attempt is made to use them. */ - for( m = 1; m <= ubnd_lat; m++ ) { - if( s != ' ' ) { - sprintf( template, "PV%d_%d%c", latax, m, s ); - } else { - sprintf( template, "PV%d_%d", latax, m ); - } - astClearCard( this ); - if( FindKeyCard( this, template, method, class, status ) ) { - DeleteCard( this, method, class, status ); - } - } - -/* Otherwise, do the same check for the longitude axis. */ - } else { - ok = 0; - for( m = 1; m <= ubnd_lon && !ok; m++ ) { - - if( s != ' ' ) { - sprintf( template, "PV%d_%d%c", lonax, m, s ); - } else { - sprintf( template, "PV%d_%d", lonax, m ); - } - - if( ! GetValue( this, template, AST__FLOAT, &pval, 0, 0, - method, class, status ) ) { - - pval = ( m == 1 ) ? 1.0 : 0.0; - } - - if( pval != 0.0 ) ok = 1; - } - - if( !ok ) { - Warn( this, "badpv", "This FITS header describes a distorted TAN " - "projection, but all the distortion coefficients (the " - "PVi_m headers) on the longitude axis are zero.", method, - class, status ); - ret = 0; - - for( m = 1; m <= ubnd_lon; m++ ) { - if( s != ' ' ) { - sprintf( template, "PV%d_%d%c", lonax, m, s ); - } else { - sprintf( template, "PV%d_%d", lonax, m ); - } - astClearCard( this ); - if( FindKeyCard( this, template, method, class, status ) ) { - DeleteCard( this, method, class, status ); - } - } - } - } - } - } - -/* Return the result. */ - return astOK ? ret : 0; -} - -static int GoodWarns( const char *value, int *status ){ -/* -* Name: -* GoodWarns - -* Purpose: -* Checks a string to ensure it is a legal list of warning conditions. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GoodWarns( const char *value, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function checks the supplied string to ensure it contains a space -* separated list of zero or more recognised warning conditions. An -* error is reported if it does not. - -* Parameters: -* value -* The string to check. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero is returned if the supplied string is not a legal list of -* conditions, or if an error has already occurred. One is returned -* otherwise. -*/ - -/* Local Variables: */ - char *b; /* Pointer to next buffer element */ - const char *c ; /* Pointer to next character */ - char buf[100]; /* Buffer for condition name */ - int inword; /* Are we in a word? */ - int n; /* Number of conditions supplied */ - int ret; /* Returned value */ - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Report an error and return if the pointer is null. */ - if( !value ){ - astError( AST__ATTIN, "astSetWarnings(fitschan): Null pointer " - "supplied for the Warnings attribute." , status); - return ret; - } - -/* Initialise things */ - inword = 0; - buf[ 0 ] = ' '; - b = buf + 1; - n = 0; - ret = 1; - -/* Loop round each character in the supplied string. */ - for( c = value ; c < value + strlen( value ) + 1; c++ ){ - -/* Have we found the first space or null following a word? */ - if( ( !(*c) || isspace( *c ) ) && inword ){ - -/* Add a space to the end of the buffer and terminate it. */ - *(b++) = ' '; - *b = 0; - -/* Check the word is legal by searching for it in the string of all - conditions, which should be lower case and have spaces at start and end. - The word in the buffer is delimited by spaces and so it will not match - a substring within a condition. If it is legal increment the number of - conditions found. */ - if( strstr( ALLWARNINGS, buf ) ){ - n++; - -/* Otherwise, report an error and break. */ - } else { - ret = 0; - *(--b) = 0; - astError( AST__ATTIN, "astSetWarnings(fitschan): Unknown " - "condition '%s' specified when setting the Warnings " - "attribute.", status, buf + 1 ); - break; - } - -/* Reset the pointer to the next character in the buffer, retaining the - initial space in the buffer. */ - b = buf + 1; - -/* Indicate we are no longer in a word. */ - inword = 0; - -/* Have we found the first non-space, non-null character following a space? */ - } else if( *c && !isspace( *c ) && !inword ){ - -/* Note we are now in a word. */ - inword = 1; - } - -/* If we are in a word, copy the lowercase character to the buffer. */ - if( inword ) *(b++) = tolower( *c ); - } - return ret; -} - -static AstMapping *GrismSpecWcs( char *algcode, FitsStore *store, int i, - char s, AstSpecFrame *specfrm, - const char *method, const char *class, int *status ) { -/* -* Name: -* GrismSpecWcs - -* Purpose: -* Create a Mapping describing a FITS-WCS grism-dispersion algorithm - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *GrismSpecWcs( char *algcode, FitsStore *store, int i, char s, -* AstSpecFrame *specfrm, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function uses the contents of the supplied FitsStore to create -* a Mapping which goes from Intermediate World Coordinate (known as "w" -* in the context of FITS-WCS paper III) to the spectral system -* described by the supplied SpecFrame. -* -* The returned Mapping implements the grism "GRA" and "GRI" algorithms -* described in FITS-WCS paper III. - -* Parameters: -* algcode -* Pointer to a string holding the code for the required algorithm -* ("-GRA" or "-GRI"). -* store -* Pointer to the FitsStore structure holding the values to use for -* the WCS keywords. -* i -* The zero-based index of the spectral axis within the FITS header -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* specfrm -* Pointer to the SpecFrame. This specifies the "S" system - the -* system in which the CRVAL kewyords (etc) are specified. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a Mapping, or NULL if an error occurs. -*/ - -/* Local Variables: */ - AstFrameSet *fs; - AstMapping *gmap; - AstMapping *map1; - AstMapping *map2; - AstMapping *map2a; - AstMapping *map2b; - AstMapping *ret; - AstMapping *smap; - AstSpecFrame *wfrm; - double crv; - double dg; - double gcrv; - double pv; - double wcrv; - -/* Check the global status. */ - ret = NULL; - if( !astOK ) return ret; - -/* The returned Mapping will be a CmpMap including a GrismMap. This - GrismMap will produced wavelength as output. We also need the Mapping - from wavelength to the system represented by the supplied SpecFrame. - To get this, we first create a copy of the supplied SpecFrame (in order - to inherit the standard of rest, epoch, etc), and set its System to - wavlength in vacuum (for "-GRI") or air (for "-GRA"), and then use - astConvert to get the Mapping from the SpecFrame system to relevant - form of wavelength. */ - wfrm = astCopy( specfrm ); - astSetSystem( wfrm, strcmp( algcode, "-GRI" )?AST__AIRWAVE:AST__WAVELEN ); - astSetUnit( wfrm, 0, "m" ); - fs = astConvert( specfrm, wfrm, "" ); - if( fs ) { - smap = astGetMapping( fs, AST__BASE, AST__CURRENT ); - fs = astAnnul( fs ); - -/* Get the CRVAL value for the spectral axis (this will be in the S system). */ - crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( crv == AST__BAD ) crv = 0.0; - -/* Convert it to the wavelength system (vacuum or air) in metres. */ - astTran1( smap, 1, &crv, 1, &wcrv ); - -/* Create a GrismMap, and then use the projection parameters stored in - the FitsStore to set its attributes (convert degrees values to radians - and supply the defaults specified in FITS-WCS paper III). The FITS - paper specifies units in which these parameters should be stored in a - FITS header - distances are in metres and angles in degrees. */ - gmap = (AstMapping *) astGrismMap( "", status ); - pv = GetItem( &(store->pv), i, 0, s, NULL, method, class, status ); - astSetGrismG( gmap, ( pv != AST__BAD )?pv:0.0 ); - pv = GetItem( &(store->pv), i, 1, s, NULL, method, class, status ); - astSetGrismM( gmap, ( pv != AST__BAD )?(int) ( pv + 0.5 ):0); - pv = GetItem( &(store->pv), i, 2, s, NULL, method, class, status ); - astSetGrismAlpha( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 ); - pv = GetItem( &(store->pv), i, 3, s, NULL, method, class, status ); - astSetGrismNR( gmap, ( pv != AST__BAD )?pv:1.0 ); - pv = GetItem( &(store->pv), i, 4, s, NULL, method, class, status ); - astSetGrismNRP( gmap, ( pv != AST__BAD )?pv:0.0 ); - pv = GetItem( &(store->pv), i, 5, s, NULL, method, class, status ); - astSetGrismEps( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 ); - pv = GetItem( &(store->pv), i, 6, s, NULL, method, class, status ); - astSetGrismTheta( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 ); - -/* Store the reference wavelength found above as an attribute of the - GrismMap. */ - astSetGrismWaveR( gmap, wcrv ); - -/* Invert the GrismMap to get the (Wavelength -> grism parameter) Mapping, and - then combine it with the (S -> Wavelength) Mapping to get the (S -> grism - parameter) Mapping. */ - astInvert( gmap ); - map1 = (AstMapping *) astCmpMap( smap, gmap, 1, "", status ); - -/* Convert the reference point value from wavelength to grism parameter. */ - astTran1( gmap, 1, &wcrv, 1, &gcrv ); - -/* Find the rate of change of grism parameter with respect to the S - system at the reference point, dg/dS. */ - dg = astRate( map1, &crv, 0, 0 ); - if( dg != AST__BAD && dg != 0.0 ) { - -/* FITS-WCS paper II requires headers to be constructed so that dS/dw = 1.0 - at the reference point. Therefore dg/dw = dg/dS. Create a WinMap which - scales and shifts the "w" value to get the grism parameter value. */ - map2a = (AstMapping *) astZoomMap( 1, dg, "", status ); - map2b = (AstMapping *) astShiftMap( 1, &gcrv, "", status ); - map2 = (AstMapping *) astCmpMap( map2a, map2b, 1, "", status ); - map2a = astAnnul( map2a ); - map2b = astAnnul( map2b ); - -/* The Mapping to be returned is the concatenation of the above Mapping - (from w to g) with the Mapping from g to S. */ - astInvert( map1 ); - ret = (AstMapping *) astCmpMap( map2, map1, 1, "", status ); - map2 = astAnnul( map2 ); - } - map1 = astAnnul( map1 ); - smap = astAnnul( smap ); - gmap = astAnnul( gmap ); - } - wfrm = astAnnul( wfrm ); - -/* Return the result */ - return ret; -} - -static int KeyFields( AstFitsChan *this, const char *filter, int maxfld, - int *ubnd, int *lbnd, int *status ){ - -/* -*+ -* Name: -* astKeyFields - -* Purpose: -* Find the ranges taken by integer fields within the keyword names -* in a FitsChan. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* int astKeyFields( AstFitsChan *this, const char *filter, int maxfld, -* int *ubnd, int *lbnd ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function returns the number of cards within a FitsChan which -* refer to keywords which match the supplied filter template. If the -* filter contains any integer field specifiers (e.g. "%d", "%3d", etc), -* it also returns the upper and lower bounds found for the integer -* fields. - -* Parameters: -* this -* Pointer to the FitsChan. -* filter -* The filter string. -* maxfld -* The size of the "ubnd" and "lbnd" arrays. -* ubnd -* A pointer to an integer array in which to return the -* upper bound found for each integer field in the filter. -* They are stored in the order in which they occur in the filter. -* If the filter contains too many fields to fit in the supplied -* array, the excess trailing fields are ignored. -* lbnd -* A pointer to an integer array in which to return the -* lower bound found for each integer field in the filter. - -* Returned Value: -* astKeyFields() -* The total number of cards matching the supplied filter in the -* FitsChan. - -* Filter Syntax: -* - The criteria for a keyword name to match a filter template are -* as follows: -* - All characters in the template other than "%" (and the field width -* and type specifiers which follow a "%") must be matched by an -* identical character in the test string. - - If a "%" occurs in the template, then the next character in the -* template should be a single digit specifying a field width. If it is -* zero, then the test string may contain zero or more matching characters. -* Otherwise, the test string must contain exactly the specified number -* of matching characters (i.e. 1 to 9). The field width digit may be -* omitted, in which case the test string must contain one or more matching -* characters. The next character in the template specifies the type of -* matching characters and must be one of "d", "c" or "f". Decimal digits -* are matched by "d", all upper (but not lower) case alphabetical -* characters are matched by "c", and all characters which may legally be -* found within a FITS keyword name are matched by "f". - -* Examples: -* - The filter "CRVAL1" accepts the single keyword CRVAL1. -* - The filter "CRVAL%1d" accepts the single keyword CRVAL0, CRVAL1, -* CRVAL2, up to CRVAL9. -* - The filter "CRVAL%d" accepts any keyword consisting of the string -* "CRVAL" followed by any integer value. -* - The filter "CR%0s1" accepts any keyword starting with the string "CR" -* and ending with the character "1" (including CR1). - -* Notes: -* - The entire FitsChan is searched, irrespective of the setting of -* the Card attribute. -* - If "maxfld" is supplied as zero, "ubnd" and "lbnd" are ignored, -* but the number of matching cards is still returned as the function value. -* - If no matching cards are found in the FitsChan, or if there are no -* integer fields in the filter, then the lower and upper bounds are -* returned as zero and -1 (i.e. reversed). -* - If an error has already occured, or if this function should fail -* for any reason, a value of zero is returned for the function value, -* and the lower and upper bounds are set to zero and -1. -*- -*/ - -/* Local Variables: */ - const char *class; /* Object class */ - const char *method; /* Method name */ - int *fields; /* Pointer to array of field values */ - int i; /* Field index */ - int icard; /* Index of current card on entry */ - int nmatch; /* No. of matching cards */ - int nf; /* No. of integer fields in the filter */ - int nfld; /* No. of integer fields in current keyword name */ - -/* Initialise the returned values. */ - nmatch = 0; - for( i = 0; i < maxfld; i++ ){ - lbnd[ i ] = 0; - ubnd[ i ] = -1; - } - nf = 0; - -/* Check the global error status. */ - if ( !astOK || !filter ) return nf; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Store the method name and object class for use in error messages. */ - method = "astKeyFields"; - class = astGetClass( this ); - -/* Count the number of integer fields in the filter string. */ - nf = CountFields( filter, 'd', method, class, status ); - -/* If this is larger than the supplied arrays, use the size of the arrays - instead. */ - if( nf > maxfld ) nf = maxfld; - -/* Allocate memory to hold the integer field values extracted from - each matching keyword. */ - fields = (int *) astMalloc( sizeof( int )*(size_t) nf ); - -/* Save the current card index, and rewind the FitsChan. */ - icard = astGetCard( this ); - astClearCard( this ); - -/* Check that the FitsChan is not empty and the pointer can be used. */ - if( !astFitsEof( this ) && astOK ){ - -/* Initialise the returned bounds. Any excess elements in the array are left - at the previously initialised values. */ - for( i = 0; i < nf; i++ ){ - lbnd[ i ] = INT_MAX; - ubnd[ i ] = -INT_MAX; - } - -/* Initialise the number of matching keywords. */ - nmatch = 0; - -/* Loop round all the cards in the FitsChan. */ - while( !astFitsEof( this ) && astOK ){ - -/* If the current keyword name matches the filter, update the returned - bounds and increment the number of matches. */ - if( Match( CardName( this, status ), filter, nf, fields, &nfld, - method, class, status ) ){ - for( i = 0; i < nf; i++ ){ - if( fields[ i ] > ubnd[ i ] ) ubnd[ i ] = fields[ i ]; - if( fields[ i ] < lbnd[ i ] ) lbnd[ i ] = fields[ i ]; - } - nmatch++; - } - -/* Move on to the next card. */ - MoveCard( this, 1, method, class, status ); - } - -/* If bounds were not found, returned 0 and -1. */ - for( i = 0; i < nf; i++ ){ - if( lbnd[ i ] == INT_MAX ){ - lbnd[ i ] = 0; - ubnd[ i ] = -1; - } - } - } - -/* Reinstate the original current card index. */ - astSetCard( this, icard ); - -/* Free the memory used to hold the integer field values extracted from - each matching keyword. */ - fields = (int *) astFree( (void *) fields ); - -/* If an error has occurred, returned no matches and reversed bounds. */ - if( !astOK ){ - nmatch = 0; - for( i = 0; i < maxfld; i++ ){ - lbnd[ i ] = 0; - ubnd[ i ] = -1; - } - } - -/* Returned the answer. */ - return nmatch; -} - -static int FindFits( AstFitsChan *this, const char *name, - char card[ AST__FITSCHAN_FITSCARDLEN + 1 ], int inc, int *status ){ - -/* -*++ -* Name: -c astFindFits -f AST_FINDFITS - -* Purpose: -* Find a FITS card in a FitsChan by keyword. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c int astFindFits( AstFitsChan *this, const char *name, char card[ 81 ], -c int inc ) -f RESULT = AST_FINDFITS( THIS, NAME, CARD, INC, STATUS ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function searches for a card in a FitsChan by keyword. The -* search commences at the current card (identified by the Card -* attribute) and ends when a card is found whose FITS keyword -* matches the template supplied, or when the last card in the -* FitsChan has been searched. -* -* If the search is successful (i.e. a card is found which matches -c the template), the contents of the card are (optionally) -f the template), the contents of the card are -* returned and the Card attribute is adjusted to identify the card -* found or, if required, the one following it. If the search is -c not successful, the function returns zero and the Card attribute -f not successful, the function returns .FALSE. and the Card attribute -* is set to the "end-of-file". - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c name -f NAME = CHARACTER * ( * ) (Given) -c Pointer to a null-terminated character string containing a -f A character string containing a -* template for the keyword to be found. In the simplest case, -* this should simply be the keyword name (the search is case -* insensitive and trailing spaces are ignored). However, this -* template may also contain "field specifiers" which are -* capable of matching a range of characters (see the "Keyword -* Templates" section for details). In this case, the first card -* with a keyword which matches the template will be found. To -* find the next FITS card regardless of its keyword, you should -* use the template "%f". -c card -f CARD = CHARACTER * ( 80 ) (Returned) -c An array of at least 81 characters (to allow room for a -c terminating null) -f A character variable with at least 80 characters -* in which the FITS card which is found will be returned. If -c the search is not successful (or a NULL pointer is given), a -f the search is not successful, a -* card will not be returned. -c inc -f INC = LOGICAL (Given) -c If this value is zero (and the search is successful), the -f If this value is .FALSE. (and the search is successful), the -* FitsChan's Card attribute will be set to the index of the card -c that was found. If it is non-zero, however, the Card -f that was found. If it is .TRUE., however, the Card -* attribute will be incremented to identify the card which -* follows the one found. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Returned Value: -c astFindFits() -f AST_FINDFITS = LOGICAL -c One if the search was successful, otherwise zero. -f .TRUE. if the search was successful, otherwise .FALSE.. - -* Notes: -* - The search always starts with the current card, as identified -* by the Card attribute. To ensure you search the entire contents -* of a FitsChan, you should first clear the Card attribute (using -c astClear). This effectively "rewinds" the FitsChan. -f AST_CLEAR). This effectively "rewinds" the FitsChan. -* - If a search is unsuccessful, the Card attribute is set to the -* "end-of-file" (i.e. to one more than the number of cards in the -* FitsChan). No error occurs. -c - A value of zero will be returned if this function is invoked -f - A value of .FALSE. will be returned if this function is invoked -* with the AST error status set, or if it should fail for any -* reason. - -* Examples: -c result = astFindFits( fitschan, "%f", card, 1 ); -f RESULT = AST_FINDFITS( FITSCHAN, '%f', CARD, .TRUE., STATUS ) -* Returns the current card in a FitsChan and advances the Card -* attribute to identify the card that follows (the "%f" -* template matches any keyword). -c result = astFindFits( fitschan, "BITPIX", card, 1 ); -f RESULT = AST_FINDFITS( FITSCHAN, 'BITPIX', CARD, .TRUE., STATUS ) -* Searches a FitsChan for a FITS card with the "BITPIX" keyword -* and returns that card. The Card attribute is then incremented -* to identify the card that follows it. -c result = astFindFits( fitschan, "COMMENT", NULL, 0 ); -f RESULT = AST_FINDFITS( FITSCHAN, 'COMMENT', CARD, .FALSE., STATUS ) -* Sets the Card attribute of a FitsChan to identify the next -c COMMENT card (if any). The card itself is not returned. -f COMMENT card (if any) and returns that card. -c result = astFindFits( fitschan, "CRVAL%1d", card, 1 ); -f RESULT = AST_FINDFITS( FITSCHAN, 'CRVAL%1d', CARD, .TRUE., STATUS ) -* Searches a FitsChan for the next card with a keyword of the -* form "CRVALi" (for example, any of the keywords "CRVAL1", -* "CRVAL2" or "CRVAL3" would be matched). The card found (if -* any) is returned, and the Card attribute is then incremented -* to identify the following card (ready to search for another -* keyword with the same form, perhaps). - -* Keyword Templates: -* The templates used to match FITS keywords are normally composed -* of literal characters, which must match the keyword exactly -* (apart from case). However, a template may also contain "field -* specifiers" which can match a range of possible characters. This -* allows you to search for keywords that contain (for example) -* numbers, where the digits comprising the number are not known in -* advance. -* -* A field specifier starts with a "%" character. This is followed -* by an optional single digit (0 to 9) specifying a field -* width. Finally, there is a single character which specifies the - -* type of character to be matched, as follows: -* -* - "c": matches all upper case letters, -* - "d": matches all decimal digits, -* - "f": matches all characters which are permitted within a FITS -* keyword (upper case letters, digits, underscores and hyphens). -* -* If the field width is omitted, the field specifier matches one -* or more characters. If the field width is zero, it matches zero -* or more characters. Otherwise, it matches exactly the number of - -* characters specified. In addition to this: -* -* - The template "%f" will match a blank FITS keyword consisting -* of 8 spaces (as well as matching all other keywords). -* - A template consisting of 8 spaces will match a blank keyword -* (only). -* - -* For example: -* -* - The template "BitPix" will match the keyword "BITPIX" only. -* - The template "crpix%1d" will match keywords consisting of -* "CRPIX" followed by one decimal digit. -* - The template "P%c" will match any keyword starting with "P" -* and followed by one or more letters. -* - The template "E%0f" will match any keyword beginning with "E". -* - The template "%f" will match any keyword at all (including a -* blank one). -*-- -*/ - -/* Local Variables: */ - char *c; /* Pointer to next character to check */ - char *lname; /* Pointer to copy of name without trailing spaces */ - const char *class; /* Object class */ - const char *method; /* Calling method */ - int ret; /* Was a card found? */ - -/* Check the global status, and supplied keyword name. */ - if( !astOK ) return 0; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Store the calling method and object class. */ - method = "astFindFits"; - class = astGetClass( this ); - -/* Get a local copy of the keyword template. */ - lname = (char *) astStore( NULL, (void *) name, strlen(name) + 1 ); - -/* Terminate it to exclude trailing spaces. */ - c = lname + strlen(lname) - 1; - while( *c == ' ' && c >= lname ) *(c--) = 0; - -/* Use the private FindKeyCard function to find the card and make it the - current card. Always use the supplied current card (if any) if the - template is "%f" or "%0f". */ - if ( !strcmp( lname, "%f" ) || !strcmp( lname, "%0f" ) ){ - ret = astFitsEof( this ) ? 0 : 1; - } else { - ret = FindKeyCard( this, lname, method, class, status ); - } - -/* Only proceed if the card was found. */ - if( ret && astOK ){ - -/* Format the current card if a destination string was supplied. */ - if( card ) FormatCard( this, card, method, status ); - -/* Increment the current card pointer if required. */ - if( inc ) MoveCard( this, 1, method, class, status ); - -/* Indicate that a card has been formatted. */ - ret = 1; - } - -/* Free the memory holding the local copy of the keyword template. */ - lname = (char *) astFree( (void *) lname ); - -/* If an errror has occurred, return zero. */ - if( !astOK ) ret = 0; - -/* Return the answer. */ - return ret; -} - -static int FindKeyCard( AstFitsChan *this, const char *name, - const char *method, const char *class, int *status ){ -/* -* Name: -* FindKeyCard - -* Purpose: -* Find the next card refering to given keyword. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int FindKeyCard( AstFitsChan *this, const char *name, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Finds the next card which refers to the supplied keyword and makes -* it the current card. The search starts with the current card and ends -* when it reaches the last card. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a string holding the keyword template (using the -* syntax expected by the Match function). -* method -* Pointer to string holding name of calling method. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if a card was found refering to the given -* keyword. Otherwise zero is returned. - -* Notes: -* - If a NULL pointer is supplied for "name" then the current card -* is left unchanged. -* - The current card is set to NULL (end-of-file) if no card can be -* found for the supplied keyword. -*/ - -/* Local Variables: */ - int nfld; /* Number of fields in keyword template */ - int ret; /* Was a card found? */ - -/* Check the global status, and supplied keyword name. */ - if( !astOK || !name ) return 0; - -/* Indicate that no card has been found yet. */ - ret = 0; - -/* Search forward through the list until all cards have been checked. */ - while( !astFitsEof( this ) && astOK ){ - -/* Break out of the loop if the keyword name from the current card matches - the supplied keyword name. */ - if( Match( CardName( this, status ), name, 0, NULL, &nfld, method, class, status ) ){ - ret = 1; - break; - -/* Otherwise, move the current card on to the next card. */ - } else { - MoveCard( this, 1, method, class, status ); - } - } - -/* Return. */ - return ret; -} - -static double *FitLine( AstMapping *map, double *g, double *g0, double *w0, - double dim, double *tol, int *status ){ -/* -* Name: -* FitLine - -* Purpose: -* Check a Mapping for linearity. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* double *FitLine( AstMapping *map, double *g, double *g0, double *w0, -* double dim, double *tol, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function applies the supplied Mapping to a set of points along -* a straight line in the input space. It checks to see if the transformed -* positions also lie on a straight line (in the output space). If so, -* it returns the vector along this line in the output space which -* corresponds to a unit vector along the line in the input space. If -* not, a NULL pointer is returned. -* -* The returned vector is found by doing a least squares fit. - -* Parameters: -* map -* A pointer to the Mapping to test. The number of outputs must be -* greater than or equal to the number of inputs. -* g -* A pointer to an array holding a unit vector within the input space -* defining the straight line to be checked. The number of elements -* within this array should equal the number of inputs for "map". -* g0 -* A pointer to an array holding a position within the input space -* giving the central position of the vector "g". The number of elements -* within this array should equal the number of inputs for "map". -* w0 -* A pointer to an array holding a vector within the output space -* which corresponds to "g0". The number of elements within this array -* should equal the number of outputs for "map". -* dim -* The length of the pixel axis, or AST__BAD if unknown. -* tol -* Pointer to an array holding the tolerance for equality on each -* output axis. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to dynamically allocated memory holding the required vector -* in the output space. The number of elements in this vector will equal -* the number of outputs for "map". The memory should be freed using -* astFree when no longer needed. If the Mapping is not linear, NULL -* is returned. - -* Notes: -* - NULL is returned if an error occurs. -*/ - -/* Local Constants: */ -#define NPO2 50 -#define NP (2*NPO2+1) - -/* Local Variables: */ - AstPointSet *pset1; - AstPointSet *pset2; - double **ptr1; - double **ptr2; - double *offset; - double *pax; - double *ret; - double *voffset; - double dax; - double denom; - double gap; - double sd2; - double sd; - double sdw; - double sw; - double wmax; - double wmin; - int i; - int j; - int n; - int nin; - int nout; - int ok; - -/* Initialise */ - ret = NULL; - -/* Check the inherited status and supplied axis size. */ - if( !astOK || dim == 0.0 ) return ret; - -/* Get the number of inputs and outputs for the Mapping. Return if the - number of outputs is smaller than the number of inputs. */ - nin = astGetNin( map ); - nout = astGetNout( map ); - if( nout < nin ) return ret; - -/* Check the supplied position is good on all axes. */ - for( j = 0; j < nout; j++ ) { - if( w0[ j ] == AST__BAD ) return ret; - } - -/* We use NP points in the fit. If a value for "dim" has been supplied, - we use points evenly distributed over the whole axis. If not, we use - a gap of 1.0 (corresponds to an axis length of 100 pixels). - Choose the gap. */ - gap = ( dim != AST__BAD ) ? dim/NP : 1.0; - -/* Create PointSets to hold the input and output positions. */ - pset1 = astPointSet( NP, nin, "", status ); - ptr1 = astGetPoints( pset1 ); - pset2 = astPointSet( NP, nout, "", status ); - ptr2 = astGetPoints( pset2 ); - -/* Allocate the returned array. */ - ret = astMalloc( sizeof( double )*(size_t) nout ); - -/* Allocate workspace to hold the constant offsets of the fit. */ - offset = astMalloc( sizeof( double )*(size_t) nout ); - voffset = astMalloc( sizeof( double )*(size_t) nout ); - -/* Indicate we have not yet got a usable returned vector. */ - ok = 0; - -/* Check we can use the pointers safely. */ - if( astOK ) { - -/* Set up the input positions: NP evenly spaced points along a line with - unit direction vector given by "g", centred at position given by "g0". */ - for( j = 0; j < nin; j++ ) { - pax = ptr1[ j ]; - dax = g[ j ]*gap; - for( i = -NPO2; i <= NPO2; i++ ) *(pax++) = g0[ j ] + dax*i; - } - -/* Transform these positions into the output space. */ - (void) astTransform( map, pset1, 1, pset2 ); - -/* Loop over all output axes, finding the component of the returned vector. */ - ok = 1; - for( j = 0; j < nout; j++ ) { - pax = ptr2[ j ]; - -/* Now loop over all the transformed points to form the other required - sums. We also form the sums needed to estimate the variance in the - calculated offset. */ - sdw = 0.0; - sw = 0.0; - sd = 0.0; - sd2 = 0.0; - n = 0; - wmax = -DBL_MAX; - wmin = DBL_MAX; - for( i = -NPO2; i <= NPO2; i++, pax++ ) { - if( *pax != AST__BAD ) { - -/* Increment the required sums. */ - sdw += i*(*pax); - sw += (*pax); - sd += i; - sd2 += i*i; - n++; - if( *pax > wmax ) wmax = *pax; - if( *pax < wmin ) wmin = *pax; - } - } - -/* If a reasonable number of good points were found, find the component of - the returned vector (excluding a scale factor of 1/gap). */ - denom = sd2*n - sd*sd; - if( n > NP/4 && denom != 0.0 ) { - -/* Find the constant scale factor to return for this axis. If the axis - value is constant, return zero. */ - if( wmax > wmin ) { - ret[ j ] = (sdw*n - sw*sd)/denom; - } else { - ret[ j ] = 0.0; - } - -/* Now find the constant offset for this axis. */ - offset[ j ] = (sw*sd2 - sdw*sd)/denom; - } else { - ok = 0; - break; - } - } - -/* Now check that the fit is good enough. Each axis is checked separately. - All axes must be good. */ - if( ok ) { - for( j = 0; j < nout; j++ ) { - -/* Store the axis values implied by the linear fit in the now un-needed ptr1[0] - array. */ - pax = ptr1[ 0 ]; - for( i = -NPO2; i <= NPO2; i++, pax++ ) { - *pax = i*ret[ j ] + offset[ j ]; - } - -/* Test the fit to see if we beleive that the mapping is linear. If - it is, scale the returned value from units of "per gap" to units of - "per pixel". Otherwise,indicate that he returned vector is unusable. */ - if( FitOK( NP, ptr2[ j ], ptr1[ 0 ], tol[ j ], status ) ) { - ret[ j ] /= gap; - } else { - ok = 0; - break; - } - } - } - } - -/* Annul the PointSets. */ - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - -/* Free memory. */ - offset = astFree( offset ); - voffset = astFree( voffset ); - -/* If an error has occurred, or if the returned vector is unusable, - free any returned memory */ - if( !astOK || !ok ) ret = astFree( ret ); - -/* Return the answer. */ - return ret; - -/* Undefine local constants: */ -#undef NP -#undef NPO2 -} - -static int FitsEof( AstFitsChan *this, int *status ){ - -/* -*+ -* Name: -* astFitsEof - -* Purpose: -* See if the FitsChan is at "end-of-file". - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* int astFitsEof( AstFitsChan *this ) - -* Class Membership: -* FitsChan method. - -* Description: -* A value of zero is returned if any more cards remain to be read from the -* FitsChan. Otherwise a value of 1 is returned. Thus, it is -* equivalent to testing the FitsChan for an "end-of-file" condition. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* One if no more cards remain to be read, otherwise zero. - -* Notes: -* - This function attempts to execute even if an error has already -* occurred. -*- -*/ - -/* Check the supplied object. */ - if( !this ) return 1; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* If no more cards remain to be read, the current card pointer in the - FitsChan will be NULL. Return an appropriate integer value. */ - return this->card ? 0 : 1; -} - -static int FitsSof( AstFitsChan *this, int *status ){ - -/* -*+ -* Name: -* FitsSof - -* Purpose: -* See if the FitsChan is at "start-of-file". - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int FitsSof( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A value of 1 is returned if the current card is the first card in -* the FitsChan. Otherwise a value of zero is returned. This function -* is much more efficient than "astGetCard(this) <= 1" . - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero if the current card is the first card. - -* Notes: -* - This function attempts to execute even if an error has already -* occurred. -* - A non-zero value is returned if the FitsChan is empty. -*- -*/ - -/* Return if no FitsChan was supplied, or if the FitsChan is empty. */ - if ( !this || !this->head ) return 1; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* If the current card is at the head of the linked list, it is the first - card. */ - return this->card == this->head; -} - -/* -*++ -* Name: -c astGetFits<X> -f AST_GETFITS<X> - -* Purpose: -* Get a named keyword value from a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c int astGetFits<X>( AstFitsChan *this, const char *name, <X>type *value ) -f RESULT = AST_GETFITS<X>( THIS, NAME, VALUE, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -* This is a family of functions which gets a value for a named keyword, -* or the value of the current card, from a FitsChan using one of several -* different data types. The data type of the returned value is selected -* by replacing <X> in the function name by one of the following strings -* representing the recognised FITS data types: -* -* - CF - Complex floating point values. -* - CI - Complex integer values. -* - F - Floating point values. -* - I - Integer values. -* - L - Logical (i.e. boolean) values. -* - S - String values. -* - CN - A "CONTINUE" value, these are treated like string values, but -* are encoded without an equals sign. -* -* The data type of the "value" -c parameter -f argument - -* depends on <X> as follows: -* -c - CF - "double *" (a pointer to a 2 element array to hold the real and -c imaginary parts of the complex value). -c - CI - "int *" (a pointer to a 2 element array to hold the real and -c imaginary parts of the complex value). -c - F - "double *". -c - I - "int *". -c - L - "int *". -c - S - "char **" (a pointer to a static "char" array is returned at the -c location given by the "value" parameter, Note, the stored string -c may change on subsequent invocations of astGetFitsS so a -c permanent copy should be taken of the string if necessary). -c - CN - Like"S". -f - CF - DOUBLE PRECISION(2) (a 2 element array to hold the real and -f imaginary parts of the complex value). -f - CI - INTEGER(2) (a 2 element array to hold the real and imaginary -f parts of the complex value). -f - F - DOUBLE PRECISION. -f - I - INTEGER -f - L - LOGICAL -f - S - CHARACTER -f - CN - CHARACTER - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c name -f NAME = CHARACTER * ( * ) (Given) -c Pointer to a null-terminated character string -f A character string -* containing the FITS keyword name. This may be a complete FITS -* header card, in which case the keyword to use is extracted from -* it. No more than 80 characters are read from this string. If -c NULL -f a single dot '.' -* is supplied, the value of the current card is returned. -c value -f VALUE = <X>type (Returned) -c A pointer to a -f A -* buffer to receive the keyword value. The data type depends on <X> -* as described above. The conents of the buffer on entry are left -* unchanged if the keyword is not found. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Returned Value: -c astGetFits<X><X>() -f AST_GETFITS<X> = LOGICAL -c A value of zero -f .FALSE. -* is returned if the keyword was not found in the FitsChan (no error -* is reported). Otherwise, a value of -c one -f .TRUE. -* is returned. - -* Notes: -* - If a name is supplied, the card following the current card is -* checked first. If this is not the required card, then the rest of the -* FitsChan is searched, starting with the first card added to the -* FitsChan. Therefore cards should be accessed in the order they are -* stored in the FitsChan (if possible) as this will minimise the time -* spent searching for cards. -* - If the requested card is found, it becomes the current card, -* otherwise the current card is left pointing at the "end-of-file". -* - If the stored keyword value is not of the requested type, it is -* converted into the requested type. -* - If the keyword is found in the FitsChan, but has no associated -* value, an error is reported. If necessary, the -c astTestFits -f AST_TESTFITS -* function can be used to determine if the keyword has a defined -* value in the FitsChan prior to calling this function. -* - An error will be reported if the keyword name does not conform -* to FITS requirements. -c - Zero -* - .FALSE. -* is returned as the function value if an error has already occurred, -* or if this function should fail for any reason. -* - The FITS standard says that string keyword values should be -* padded with trailing spaces if they are shorter than 8 characters. -* For this reason, trailing spaces are removed from the string -* returned by -c astGetFitsS -f AST_GETFITSS -* if the original string (including any trailing spaces) contains 8 -* or fewer characters. Trailing spaces are not removed from longer -* strings. -*-- -*/ - -/* Define a macro which expands to the implementation of the astGetFits<X> - routine for a given data type. */ -#define MAKE_FGET(code,ctype,ftype) \ -static int GetFits##code( AstFitsChan *this, const char *name, ctype value, int *status ){ \ -\ -/* Local Variables: */ \ - const char *class; /* Object class */ \ - const char *method; /* Calling method */ \ - char *lcom; /* Supplied keyword comment */ \ - char *lname; /* Supplied keyword name */ \ - char *lvalue; /* Supplied keyword value */ \ - char *string; /* Pointer to returned string value */ \ - char *c; /* Pointer to next character */ \ - int cl; /* Length of string value */ \ - int ret; /* The returned value */ \ -\ -/* Check the global error status. */ \ - if ( !astOK ) return 0; \ -\ -/* Ensure the source function has been called */ \ - ReadFromSource( this, status ); \ -\ -/* Store the calling method and object class. */ \ - method = "astGetFits"#code; \ - class = astGetClass( this ); \ -\ -/* Initialise the returned value. */ \ - ret = 0; \ -\ -/* Extract the keyword name from the supplied string. */ \ - if( name ) { \ - (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); \ - } else { \ - lname = NULL; \ - lvalue = NULL; \ - lcom = NULL; \ - } \ -\ -/* Attempt to find a card in the FitsChan refering to this keyword, \ - and make it the current card. Only proceed if a card was found. No \ - need to do the search if the value of the current card is required. */ \ - if( !lname || SearchCard( this, lname, method, class, status ) ){ \ -\ -/* Convert the stored data value to the requested type, and store it in \ - the supplied buffer. */ \ - if( !CnvValue( this, ftype, 0, value, method, status ) && astOK ) { \ - astError( AST__FTCNV, "%s(%s): Cannot convert FITS keyword " \ - "'%s' to %s.", status, method, class, \ - CardName( this, status ), type_names[ ftype ] ); \ - } \ -\ -/* If the returned value is a string containing 8 or fewer characters, \ - replace trailing spaces with null characters. */ \ - if( astOK ) { \ - if( ftype == AST__STRING ) { \ - string = *( (char **) value ); \ - if( string ) { \ - cl =strlen( string ); \ - if( cl <= 8 ) { \ - c = string + cl - 1; \ - while( *c == ' ' && c > string ) { \ - *c = 0; \ - c--; \ - } \ - } \ - } \ - } \ -\ -/* Indicate that a value is available. */ \ - ret = 1; \ - } \ -\ - } \ -\ -/* Context error message. */ \ - if( !astOK && lname && *lname ) { \ - astError( astStatus, "%s(%s): Cannot get value for FITS keyword " \ - "'%s'.", status, method, class, lname ); \ - } \ -\ -/* Release the memory used to hold keyword name, value and comment strings. */ \ - lname = (char *) astFree( (void *) lname ); \ - lvalue = (char *) astFree( (void *) lvalue ); \ - lcom = (char *) astFree( (void *) lcom ); \ -\ -/* Return the answer. */ \ - return ret; \ -\ -} - -/* Use the above macro to give defintions for the astGetFits<X> method - for each FITS data type. */ -MAKE_FGET(CF,double *,AST__COMPLEXF) -MAKE_FGET(CI,int *,AST__COMPLEXI) -MAKE_FGET(F,double *,AST__FLOAT) -MAKE_FGET(I,int *,AST__INT) -MAKE_FGET(L,int *,AST__LOGICAL) -MAKE_FGET(S,char **,AST__STRING) -MAKE_FGET(CN,char **,AST__CONTINUE) -#undef MAKE_FGET - -static int FitsGetCom( AstFitsChan *this, const char *name, - char **comment, int *status ){ - -/* -*+ -* Name: -* astFitsGetCom - -* Purpose: -* Get a keyword comment from a FitsChan. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" - -* int astFitsGetCom( AstFitsChan *this, const char *name, -* char **comment ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function gets the comment associated with the next occurrence of -* a named keyword in a FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* A pointer to a -* string holding the keyword name. This may be a complete FITS -* header card, in which case the keyword to use is extracted from -* it. No more than 80 characters are read from this string. -* comment -* A pointer to a location at which to return a pointer to a string -* holding the keyword comment. Note, the stored string will change on -* subsequent invocations of astFitsGetCom so a permanent copy -* should be taken of the string if necessary. - -* Returned Value: -* astFitsGetCom() -* A value of zero is returned if the keyword was not found before -* the end of the FitsChan was reached (no error is reported). -* Otherwise, a value of one is returned. - -* Notes: -* - If a NULL pointer is supplied for "name" then the comment from -* the current card is returned. -* - The returned value is obtained from the next card refering to -* the required keyword, starting the search with the current card. -* Any cards occuring before the current card are not seached. If -* the entire contents of the FitsChan must be searched, then ensure -* the current card is the first card in the FitsChan by clearing the Card -* attribute. This effectively "rewinds" the FitsChan. -* - The current card is updated to become the card following the one -* read by this function. If the card read by this function is the -* last one in the FitsChan, then the current card is left pointing at the -* "end-of-file". -* - An error will be reported if the keyword name does not conform -* to FITS requirements. -* - A NULL pointer is returned for the comment string if the keyword -* has no comment. -* - Zero is returned as the function value if an error has already -* occurred, or if this function should fail for any reason. -*- -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - const char *method; /* Calling method */ - const char *class; /* Object class */ - char *lcom; /* Supplied keyword comment */ - char *lname; /* Supplied keyword name */ - char *lvalue; /* Supplied keyword value */ - int ret; /* The returned value */ - -/* Check the global error status. */ - if ( !astOK ) return 0; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Initialise the returned value. */ - ret = 0; - -/* Store the method name and object class. */ - method = "astFitsGetCom"; - class = astGetClass( this ); - -/* Extract the keyword name from the supplied string (if supplied). */ - if( name ){ - (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); - } else { - lname = NULL; - lcom = NULL; - lvalue = NULL; - } - -/* Find the next card in the FitsChan refering to this keyword. This will - be the current card if no keyword name was supplied. The matching card - is made the current card. Only proceed if a card was found. */ - if( FindKeyCard( this, lname, method, class, status ) ){ - -/* Copy the comment into a static buffer, and return a pointer to it. */ - if( CardComm( this, status ) ){ - (void) strncpy( fitsgetcom_sval, CardComm( this, status ), AST__FITSCHAN_FITSCARDLEN ); - fitsgetcom_sval[ AST__FITSCHAN_FITSCARDLEN ] = 0; - if( comment ) *comment = fitsgetcom_sval; - } else { - if( comment ) *comment = NULL; - } - -/* Move on to the next card. */ - MoveCard( this, 1, method, class, status ); - -/* Indicate that a value is available. */ - if( astOK ) ret = 1; - } - -/* Release the memory used to hold keyword name, value and comment strings. */ - lname = (char *) astFree( (void *) lname ); - lvalue = (char *) astFree( (void *) lvalue ); - lcom = (char *) astFree( (void *) lcom ); - -/* Return the answer. */ - return ret; -} - -static int SetFits( AstFitsChan *this, const char *keyname, void *value, - int type, const char *comment, int overwrite, int *status ){ - -/* -* Name: -* SetFits - -* Purpose: -* Store a keyword value of any type in a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int SetFits( AstFitsChan *this, const char *keyname, void *value, -* int type, const char *comment, int overwrite, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function stores the supplied value for the supplied keyword -* in the supplied FitsChan, assuming it is of the supplied data type. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* A pointer to a string holding the keyword name. -* value -* A pointer to a buffer holding the keyword value. For strings, -* the buffer should hold the address of a pointer to the character -* string. -* type -* The keyword type. -* comment -* A pointer to a string holding a comment to associated with the -* keyword. If a NULL pointer or a blank string is supplied, then -* any comment included in the string supplied for the "name" parameter -* is used instead. If "name" contains no comment, then any existing -* comment in the card being over-written is retained, or a NULL -* pointer is stored if a new card is being inserted. If the data -* value being stored for the card is the same as the card being -* over-written, then any existing comment is retained. -* overwrite -* If non-zero, the new card formed from the supplied keyword name, -* value and comment string over-writes the current card, and the -* current card is incremented to refer to the next card. If zero, the -* new card is inserted in front of the current card and the current -* card is left unchanged. In either case, if the current card on -* entry points to the "end-of-file", the new card is appended to the -* end of the list. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 0 is returned if the value could not be stored for any -* reason. A value of 1 is returned otherwise. - -* Notes: -* - Nothing is stored in the FitsChan and a value of zero is returned -* (but no error is reported) if an AST__FLOAT value is supplied equal -* to AST__BAD. -*/ - -/* Local Variables: */ - const char *cval; - const char *ecval; - double dval; - double ecdval[ 2 ]; - double edval; - int ecival[ 2 ]; - int eival; - int ival; - int ret; - -/* Check the global status, and the supplied pointer. */ - if( !astOK || !value ) return 0; - -/* Initialise the returned value to indicate that the supplied name was - stored. */ - ret = 1; - -/* Check each data type in turn. */ - if( type == AST__FLOAT ){ - dval = *( (double *) value ); - if( dval != AST__BAD ) { - -/* If the data value has not changed, and the card has a coment, - set the comment pointer NULL so that the existing comment will be - retained. */ - if( overwrite && CnvValue( this, type, 0, &edval, "SetFits", - status ) && - CardComm( this, status ) ) { - if( astEQUAL( edval, dval ) ) comment = NULL; - } - astSetFitsF( this, keyname, dval, comment, overwrite ); - } else { - ret = 0; - } - } else if( type == AST__STRING ){ - cval = *( (char **) value); - if( cval ){ - -/* If the data value has not changed, retain the original comment. */ - if( overwrite && CnvValue( this, type, 0, &ecval, "SetFits", - status ) && - CardComm( this, status ) ) { - if( Similar( ecval, cval, status ) ) comment = NULL; - } - -/* Ignore comments if they are identical to the keyword value. */ - if( comment && !strcmp( cval, comment ) ) comment = NULL; - astSetFitsS( this, keyname, cval, comment, overwrite ); - } else { - ret = 0; - } - } else if( type == AST__CONTINUE ){ - cval = *( (char **) value); - if( cval ){ - astSetFitsCN( this, keyname, cval, comment, overwrite ); - } else { - ret = 0; - } - } else if( type == AST__COMMENT ){ - astSetFitsCom( this, keyname, comment, overwrite ); - } else if( type == AST__INT ){ - ival = *( (int *) value ); - -/* If the data value has not changed, retain the original comment. */ - if( overwrite && CnvValue( this, type, 0, &eival, "SetFits", - status ) && - CardComm( this, status ) ) { - if( eival == ival ) comment = NULL; - } - astSetFitsI( this, keyname, ival, comment, overwrite ); - } else if( type == AST__COMPLEXF ){ - if( ( (double *) value )[0] != AST__BAD && - ( (double *) value )[1] != AST__BAD ) { - -/* If the data value has not changed, retain the original comment. */ - if( overwrite && CnvValue( this, type, 0, ecdval, "SetFits", - status ) && - CardComm( this, status ) ) { - if( astEQUAL( ecdval[ 0 ], ( (double *) value )[ 0 ] ) && - astEQUAL( ecdval[ 1 ], ( (double *) value )[ 1 ] ) ) comment = NULL; - } - astSetFitsCF( this, keyname, (double *) value, comment, overwrite ); - } else { - ret = 0; - } - } else if( type == AST__COMPLEXI ){ - -/* If the data value has not changed, retain the original comment. */ - if( overwrite && CnvValue( this, type, 0, ecival, "SetFits", - status ) && - CardComm( this, status ) ) { - if( ecival[ 0 ] == ( (int *) value )[ 0 ] && - ecival[ 1 ] == ( (int *) value )[ 1 ] ) comment = NULL; - } - astSetFitsCI( this, keyname, (int *) value, comment, overwrite ); - } else if( type == AST__LOGICAL ){ - ival = ( *( (int *) value ) != 0 ); - -/* If the data value has not changed, retain the original comment. */ - if( overwrite && CnvValue( this, type, 0, &eival, "SetFits", - status ) && - CardComm( this, status ) ) { - if( eival == ival ) comment = NULL; - } - astSetFitsL( this, keyname, ival, comment, overwrite ); - } else if( type == AST__UNDEF ){ - if( overwrite && CardType( this, status ) == AST__UNDEF && CardComm( this, status ) ) { - comment = NULL; - } - astSetFitsU( this, keyname, comment, overwrite ); - } - return ret; -} - -/* -*++ -* Name: -c astSetFits<X> -f AST_SETFITS<X> - -* Purpose: -* Store a keyword value in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c void astSetFits<X>( AstFitsChan *this, const char *name, <X>type value, -c const char *comment, int overwrite ) -f CALL AST_SETFITS<X>( THIS, NAME, VALUE, COMMENT, OVERWRITE, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This is a family of functions which store values for named keywords -f This is a family of routines which store values for named keywords -* within a FitsChan at the current card position. The supplied keyword -* value can either over-write an existing keyword value, or can be -* inserted as a new header card into the FitsChan. -* -c The keyword data type is selected by replacing <X> in the function name -f The keyword data type is selected by replacing <X> in the routine name -* by one of the following strings representing the recognised FITS data - -* types: -* -* - CF - Complex floating point values. -* - CI - Complex integer values. -* - F - Floating point values. -* - I - Integer values. -* - L - Logical (i.e. boolean) values. -* - S - String values. -* - CN - A "CONTINUE" value, these are treated like string values, but -* are encoded without an equals sign. -* - -* The data type of the "value" parameter depends on <X> as follows: -* -c - CF - "double *" (a pointer to a 2 element array holding the real and -c imaginary parts of the complex value). -c - CI - "int *" (a pointer to a 2 element array holding the real and -c imaginary parts of the complex value). -c - F - "double". -c - I - "int". -c - L - "int". -c - S - "const char *". -c - CN - "const char *". -* -f - CF - DOUBLE PRECISION(2) (a 2 element array holding the real and -f imaginary parts of the complex value). -f - CI - INTEGER(2) (a 2 element array holding the real and imaginary -f parts of the complex value). -f - F - DOUBLE PRECISION. -f - I - INTEGER -f - L - LOGICAL -f - S - CHARACTER -f - CN - CHARACTER - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c name -f NAME = CHARACTER * ( * ) (Given) -c Pointer to a null-terminated character string -f A character string -* containing the FITS keyword name. This may be a complete FITS -* header card, in which case the keyword to use is extracted from -* it. No more than 80 characters are read from this string. -c value -f VALUE = <X>type (Given) -* The keyword value to store with the named keyword. The data type -* of this parameter depends on <X> as described above. -c comment -f COMMENT = CHARACTER * ( * ) (Given) -c A pointer to a null terminated string -f A string -* holding a comment to associated with the keyword. -c If a NULL pointer or -f If -* a blank string is supplied, then any comment included in the string -* supplied for the -c "name" parameter is used instead. If "name" -f NAME parameter is used instead. If NAME -* contains no comment, then any existing comment in the card being -* over-written is retained. Otherwise, no comment is stored with -* the card. -c overwrite -f OVERWRITE = LOGICAL (Given) -c If non-zero, -f If .TRUE., -* the new card formed from the supplied keyword name, value and comment -* string over-writes the current card, and the current card is -* incremented to refer to the next card (see the "Card" attribute). If -c zero, -f .FALSE., -* the new card is inserted in front of the current card and the current -* card is left unchanged. In either case, if the current card on entry -* points to the "end-of-file", the new card is appended to the end of -* the list. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - The -c function astSetFitsU -f routine AST_SETFITSU -* can be used to indicate that no value is associated with a keyword. -* - The -c function astSetFitsCM -f routine AST_SETFITSCM -* can be used to store a pure comment card (i.e. a card with a blank -* keyword). -* - To assign a new value for an existing keyword within a FitsChan, -c first find the card describing the keyword using astFindFits, and -c then use one of the astSetFits<X> family to over-write the old value. -f first find the card describing the keyword using AST_FINDFITS, and -f then use one of the AST_SETFITS<X> family to over-write the old value. -* - If, on exit, there are no cards following the card written by -c this function, then the current card is left pointing at the -f this routine, then the current card is left pointing at the -* "end-of-file". -* - An error will be reported if the keyword name does not conform -* to FITS requirements. -*-- -*/ - -/* Define a macro which expands to the implementation of the astSetFits<X> - routine for a given data type. */ -#define MAKE_FSET(code,ctype,ftype,valexp) \ -static void SetFits##code( AstFitsChan *this, const char *name, ctype value, const char *comment, int overwrite, int *status ) { \ -\ -/* Local variables: */ \ - const char *class; /* Object class */ \ - const char *method; /* Calling method */ \ - const char *com; /* Comment to use */ \ - char *lcom; /* Supplied keyword comment */ \ - char *lname; /* Supplied keyword name */ \ - char *lvalue; /* Supplied keyword value */ \ - int free_com; /* Should com be freed before returned? */ \ -\ -/* Check the global error status. */ \ - if ( !astOK ) return; \ -\ -/* Ensure the source function has been called */ \ - ReadFromSource( this, status ); \ -\ -/* Store the object clas and calling method. */ \ - class = astGetClass( this ); \ - method = "astSetFits"#code; \ -\ -/* Extract the keyword name from the supplied string. */ \ - (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); \ -\ -/* Initialise a pointer to the comment to be stored. If the supplied \ - comment is blank, use the comment given with "name". */ \ - com = ChrLen( comment, status ) ? comment : lcom; \ -\ -/* If the comment is still blank, use the existing comment if we are \ - over-writing, or a NULL pointer otherwise. */ \ - free_com = 0; \ - if( !ChrLen( com, status ) ) { \ - com = NULL; \ - if( overwrite ) { \ - if( CardComm( this, status ) ){ \ - com = (const char *) astStore( NULL, (void *) CardComm( this, status ), \ - strlen( CardComm( this, status ) ) + 1 ); \ - free_com = 1; \ - } \ - } \ - } \ -\ -/* Insert the new card. */ \ - InsCard( this, overwrite, lname, ftype, valexp, com, method, class, status ); \ -\ -/* Release the memory used to hold keyword name, value and comment strings. */ \ - lname = (char *) astFree( (void *) lname ); \ - lvalue = (char *) astFree( (void *) lvalue ); \ - lcom = (char *) astFree( (void *) lcom ); \ -\ -/* Release the memory holding the stored comment string, so long as it was \ - allocated within this function. */ \ - if( free_com ) com = (const char *) astFree( (void *) com ); \ -\ -} - -/* Use the above macro to give defintions for the astSetFits<X> method - for each FITS data type. */ -MAKE_FSET(I,int,AST__INT,(void *)&value) -MAKE_FSET(F,double,AST__FLOAT,(void *)&value) -MAKE_FSET(S,const char *,AST__STRING,(void *)value) -MAKE_FSET(CN,const char *,AST__CONTINUE,(void *)value) -MAKE_FSET(CF,double *,AST__COMPLEXF,(void *)value) -MAKE_FSET(CI,int *,AST__COMPLEXI,(void *)value) -MAKE_FSET(L,int,AST__LOGICAL,(void *)&value) -#undef MAKE_FSET - -static void SetFitsU( AstFitsChan *this, const char *name, const char *comment, - int overwrite, int *status ) { - -/* -*++ -* Name: -c astSetFitsU -f AST_SETFITSU - -* Purpose: -* Store an undefined keyword value in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c void astSetFitsU( AstFitsChan *this, const char *name, -c const char *comment, int overwrite ) -f CALL AST_SETFITSU( THIS, NAME, COMMENT, OVERWRITE, STATUS ) - -* Description: -* This -c function -f routine -* stores an undefined value for a named keyword within -* a FitsChan at the current card position. The new undefined value -* can either over-write an existing keyword value, or can be inserted -* as a new header card into the FitsChan. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c name -f NAME = CHARACTER * ( * ) (Given) -c Pointer to a null-terminated character string -f A character string -* containing the FITS keyword name. This may be a complete FITS -* header card, in which case the keyword to use is extracted from -* it. No more than 80 characters are read from this string. -c comment -f COMMENT = CHARACTER * ( * ) (Given) -c A pointer to a null terminated string -f A string -* holding a comment to associated with the keyword. -c If a NULL pointer or -f If -* a blank string is supplied, then any comment included in the string -* supplied for the -c "name" parameter is used instead. If "name" -f NAME parameter is used instead. If NAME -* contains no comment, then any existing comment in the card being -* over-written is retained. Otherwise, no comment is stored with -* the card. -c overwrite -f OVERWRITE = LOGICAL (Given) -c If non-zero, -f If .TRUE., -* the new card formed from the supplied keyword name and comment -* string over-writes the current card, and the current card is -* incremented to refer to the next card (see the "Card" attribute). If -c zero, -f .FALSE., -* the new card is inserted in front of the current card and the current -* card is left unchanged. In either case, if the current card on entry -* points to the "end-of-file", the new card is appended to the end of -* the list. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - If, on exit, there are no cards following the card written by -* this function, then the current card is left pointing at the -* "end-of-file". -* - An error will be reported if the keyword name does not conform -* to FITS requirements. -*-- -*/ - -/* Local variables: */ - const char *class; /* Object class */ - const char *method; /* Calling method */ - const char *com; /* Comment to use */ - char *lcom; /* Supplied keyword comment */ - char *lname; /* Supplied keyword name */ - char *lvalue; /* Supplied keyword value */ - int free_com; /* Should com be freed before returned? */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Store the object clas and calling method. */ - class = astGetClass( this ); - method = "astSetFitsU"; - -/* Extract the keyword name from the supplied string. */ - (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); - -/* Initialise a pointer to the comment to be stored. If the supplied - comment is blank, use the comment given with "name". */ - com = ChrLen( comment, status ) ? comment : lcom; - -/* If the comment is still blank, use the existing comment if we are - over-writing, or a NULL pointer otherwise. */ - free_com = 0; - if( !ChrLen( com, status ) ) { - com = NULL; - if( overwrite ) { - if( CardComm( this, status ) ){ - com = (const char *) astStore( NULL, (void *) CardComm( this, status ), - strlen( CardComm( this, status ) ) + 1 ); - free_com = 1; - } - } - } - -/* Insert the new card. */ - InsCard( this, overwrite, lname, AST__UNDEF, NULL, com, method, class, - status ); - -/* Release the memory used to hold keyword name, value and comment strings. */ - lname = (char *) astFree( (void *) lname ); - lvalue = (char *) astFree( (void *) lvalue ); - lcom = (char *) astFree( (void *) lcom ); - -/* Release the memory holding the stored comment string, so long as it was - allocated within this function. */ - if( free_com ) com = (const char *) astFree( (void *) com ); -} - -static void SetFitsCM( AstFitsChan *this, const char *comment, - int overwrite, int *status ) { - -/* -*++ -* Name: -c astSetFitsCM -f AST_SETFITSCM - -* Purpose: -* Store a comment card in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c void astSetFitsCM( AstFitsChan *this, const char *comment, -c int overwrite ) -f CALL AST_SETFITSCM( THIS, COMMENT, OVERWRITE, STATUS ) - -* Description: -* This -c function -f routine -* stores a comment card ( i.e. a card with no keyword name or equals -* sign) within a FitsChan at the current card position. The new card -* can either over-write an existing card, or can be inserted as a new -* card into the FitsChan. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c comment -f COMMENT = CHARACTER * ( * ) (Given) -c A pointer to a null terminated string -f A string -* holding the text of the comment card. -c If a NULL pointer or -f If -* a blank string is supplied, then a totally blank card is produced. -c overwrite -f OVERWRITE = LOGICAL (Given) -c If non-zero, -f If .TRUE., -* the new card over-writes the current card, and the current card is -* incremented to refer to the next card (see the "Card" attribute). If -c zero, -f .FALSE., -* the new card is inserted in front of the current card and the current -* card is left unchanged. In either case, if the current card on entry -* points to the "end-of-file", the new card is appended to the end of -* the list. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - If, on exit, there are no cards following the card written by -* this function, then the current card is left pointing at the -* "end-of-file". -*-- -*/ - -/* Just call astSetFitsCom with a blank keyword name. */ - astSetFitsCom( this, "", comment, overwrite ); -} - -static void SetFitsCom( AstFitsChan *this, const char *name, - const char *comment, int overwrite, int *status ){ - -/* -*+ -* Name: -* astSetFitsCom - -* Purpose: -* Store a comment for a keyword in a FitsChan. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" - -* void astSetFitsCom( AstFitsChan *this, const char *name, -* const char *comment, int overwrite ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function replaces the comment within an existing card, or -* stores a new comment card within a FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* A pointer to a -* string holding the keyword name. This may be a complete FITS -* header card, in which case the keyword to use is extracted from -* it. No more than 80 characters are read from this string. -* comment -* A pointer to a -* string holding a comment to associated with the keyword. -* If a NULL or -* blank string is supplied, any existing comment associated with -* the keyword is removed. -* overwrite -* If non-zero, the new comment replaces the comment in the current -* card, and the current card is then incremented to refer to the next -* card. If zero, a new comment card is inserted in front of the current -* card and the current card is left unchanged. In either case, if the -* current card on entry points to the "end-of-file", the new card is -* appended to the end of the list. - -* Notes: -* - When replacing an existing comment, any existing keyword value is -* retained only if the supplied keyword name is the same as the keyword -* name in the current card. If the keyword names are different, then -* the new name replaces the old name, and any existing keyword data value -* is deleted. The card thus becomes a comment card with the supplied -* keyword name and comment, but no data value. -* - If, on exit, there are no cards following the card written by -* this function, then the current card is left pointing at the -* "end-of-file". -* - The current card can be set explicitly before calling this function -* either by assigning a value to the Card attribute (if the index of the -* required card is already known), or using astFindFits (if only the -* keyword name is known). -* - An error will be reported if the keyword name does not conform -* to FITS requirements. -*- -*/ - -/* Local variables: */ - const char *class; /* Pointer to object class string */ - const char *method; /* Pointer to calling method string */ - const char *cname; /* The existing keyword name */ - const char *com; /* The comment to use */ - char *lcom; /* Supplied keyword comment */ - char *lname; /* Supplied keyword name */ - char *lvalue; /* Supplied keyword value */ - void *old_data; /* Pointer to the old data value */ - void *data; /* Pointer to data value to be stored */ - size_t size; /* The size of the data value */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Initialisation */ - size = 0; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Store the calling method and object class. */ - method = "astSetFitsCom"; - class = astGetClass( this ); - -/* Extract the keyword name, etc, from the supplied string. */ - (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); - -/* If a blank comment has been supplied, use NULL instead. */ - com = ChrLen( comment, status )? comment : NULL; - -/* If we are inserting a new card, or over-writing an old card with a - different name, create and store a comment card with the given keyword - name and comment, but no data value. */ - cname = CardName( this, status ); - if( !overwrite || !cname || strcmp( lname, cname ) ){ - InsCard( this, overwrite, lname, AST__COMMENT, NULL, com, method, class, status ); - -/* If we are overwriting an existing keyword comment, use the data type - and value from the existing current card. Note, we have to take a copy - of the old data value because InsCard over-writes by deleting the old - card and then inserting a new one. */ - } else { - old_data = CardData( this, &size, status ); - data = astStore( NULL, old_data, size ); - InsCard( this, 1, lname, CardType( this, status ), data, com, method, class, status ); - data = astFree( data ); - } - -/* Release the memory used to hold keyword name, value and comment strings. */ - lname = (char *) astFree( (void *) lname ); - lvalue = (char *) astFree( (void *) lvalue ); - lcom = (char *) astFree( (void *) lcom ); -} - -static void FixNew( AstFitsChan *this, int flag, int remove, - const char *method, const char *class, int *status ){ - -/* -* -* Name: -* FixNew - -* Purpose: -* Remove "new" flags from the whole FitsChan, and optionally remove -* "new" cards. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void FixNew( AstFitsChan *this, int flag, int remove, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function searches the entire FitsChan for cards which are -* marked as new using the supplied flag (NEW1 or NEW2). If "remove" -* is non-zero, these cards are completely removed from the FitsChan -* (not just marked as used). If "remove" is zero, they are retained -* and the specified flag is cleared. - -* Parameters: -* this -* Pointer to the FitsChan. -* flag -* The flag to use; NEW1 or NEW2. -* remove -* Remove flagged cards from the FitsChan? -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Notes: -* - This function attempts to execute even if an error has occurred. -* - If any cards are removed, the current Card is left at "end-of-file" -* on exit. If no cards are removed, the original current card is -* retained. -*- -*/ - -/* Local Variables: */ - int *flags; /* Pointer to flags mask for the current card */ - int icard; /* Index of current card on entry */ - int ndeleted; /* Number of cards deleted by this call */ - -/* Return if no FitsChan was supplied, or if the FitsChan is empty. */ - if ( !this || !this->head ) return; - -/* Save the current card index, and rewind the FitsChan. */ - icard = astGetCard( this ); - astClearCard( this ); - -/* Indicate no cards have yet been deleted. */ - ndeleted = 0; - -/* Loop through the list of FitsCards in the FitsChan until the final - card is reached. */ - while( astOK && this->card ){ - -/* Get a pointer to the flags mask for this card. */ - flags = CardFlags( this, status ); - -/* See if the Card has been marked with the requeste new flag. */ - if( flags && ( (*flags) & flag ) ) { - -/* If requested, remove the card. This will automatically move the - current card on to the next card. */ - if( remove ){ - DeleteCard( this, method, class, status ); - ndeleted++; - -/* Otherwise, clear the flag. */ - } else { - *flags = (*flags) & ~flag; - -/* Increment the card count and move on to the next card. */ - MoveCard( this, 1, method, class, status ); - } - -/* Move on to the next card if this card is not marked with the requested - new flag. */ - } else { - MoveCard( this, 1, method, class, status ); - } - } - -/* If no cards were removed, we can safely re-instate the original - current card. Otherwise, the current card is left at "end-of-file". */ - if( ndeleted == 0 ) astSetCard( this, icard ); - -/* Return */ - return; -} - -static void FixUsed( AstFitsChan *this, int reset, int used, int remove, - const char *method, const char *class, int *status ){ - -/* -* -* Name: -* FixUsed - -* Purpose: -* Remove "provisionally used" flags from the whole FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void FixUsed( AstFitsChan *this, int reset, int used, int remove, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function searches the entire FitsChan for cards which are -* marked as "provisionally used". The "provisionally used" flag is -* cleared for each such card. In addition, if "used" is non-zero then -* each such card is flagged as having been "definitely used". If -* "remove" is non-zero, then all "provisionally used" cards are deleted -* from the FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. -* reset -* Set all cards so that they are neither provisionally used or -* definitely used. In this case neither the "used" nor the -* "remove" parameter are accssed. -* used -* Have the provisionally used cards definitely been used? -* remove -* Should provisionally used cards be deleted? -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Notes: -* - This function attempts to execute even if an error has occurred. -*- -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - FitsCard *card0; /* Pointer to current FitsCard */ - int *flags; /* Pointer to flags mask for the current card */ - int old_ignore_used; /* Original value of variable ignore_used */ - int old_status; /* Original inherited status value */ - int rep; /* Original error reporting flag */ - -/* Return if no FitsChan was supplied, or if the FitsChan is empty. */ - if ( !this || !this->head ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Temporarily clear any bad status value and supress error reporting in - this function. */ - old_status = astStatus; - astClearStatus; - rep = astReporting( 0 ); - -/* Indicate that we should not skip over cards marked as having been - read. */ - old_ignore_used = ignore_used; - ignore_used = 0; - -/* Save a pointer to the current card, and the reset the current card to - be the first card. */ - card0 = this->card; - astClearCard( this ); - -/* Loop through the list of FitsCards in the FitsChan until the final - card is reached. */ - while( this->card ){ - -/* Get a pointer to the flags mask for this card. */ - flags = CardFlags( this, status ); - -/* Reset both used flags if required. */ - if( reset ) { - *flags = (*flags) & ~PROVISIONALLY_USED; - *flags = (*flags) & ~USED; - MoveCard( this, 1, method, class, status ); - -/* Otherwise perform the actions indicated by parameters "used" and - "remove". */ - } else { - -/* See if the Card has been provisionally used. */ - if( flags && ( (*flags) & PROVISIONALLY_USED ) ) { - -/* Clear the provisionally used flag. */ - *flags = (*flags) & ~PROVISIONALLY_USED; - -/* If required, set the definitely used flag. */ - if( used ) *flags = (*flags) | USED; - -/* If required, delete the card. The next card is made current. If we are - about to delete the original current card, we need to update the - pointer to the card to be made current at the end of this function. - If we end up back at the head of the chain, indicate that we have - reached the end of file by setting card0 NULL. */ - if( remove ) { - if( card0 == this->card && card0 ) { - card0 = ( (FitsCard *) this->card )->next; - if( (void *) card0 == this->head ) card0 = NULL; - } - DeleteCard( this, method, class, status ); - -/* Otherwise, just move on to the next card. */ - } else { - MoveCard( this, 1, method, class, status ); - } - -/* If this card has not bee provisionally used, move on to the next card. */ - } else { - MoveCard( this, 1, method, class, status ); - } - } - } - -/* Re-instate the original current card. */ - this->card = card0; - -/* If this card is now flagged as definitely used, move forward to the - next un-used card. */ - flags = CardFlags( this, status ); - if( flags && (*flags & USED ) ) { - ignore_used = 1; - MoveCard( this, 1, method, class, status ); - } - -/* Re-instate the original flag indicating if cards marked as having been - read should be skipped over. */ - ignore_used = old_ignore_used; - -/* Re-instate the original status value and error reporting condition. */ - astReporting( rep ); - astSetStatus( old_status ); -} - -static void FormatCard( AstFitsChan *this, char *buf, const char *method, int *status ){ - -/* -* -* Name: -* FormatCard - -* Purpose: -* Formats the current card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void FormatCard( AstFitsChan *this, char *buf, const char *method, int *status ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function write the current card into the supplied character -* buffer as a complete FITS header card. - -* Parameters: -* this -* Pointer to the FitsChan. -* buf -* A character string into which the header card is written. This -* should be at least 81 characters long. The returned string is -* padded with spaces upto column 80. A terminating null character -* is added. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Notes: -* - An error is reported if the requested header card does not conform to -* FITS standards. -* -*/ - -/* Local Variables: */ - const char *com; /* Pointer to comment string to use */ - int comlen; /* Length of comment string */ - int comstart; /* Column in which to start comment */ - int i; /* Loop counter for characters */ - int len; /* Output string length */ - int digits; /* No. of digits to use when formatting floating point values */ - int type; /* Card data type */ - -/* Check the global error status, and check the current card is defined. */ - if ( !astOK || astFitsEof( this ) ) return; - -/* Get a pointer to the comment to use and determine its length. */ - com = CardComm( this, status ); - comlen = ChrLen( com, status ); - -/* Copy the keyword name to the start of the output buffer, and store - its length. */ - len = (int) strlen( strcpy( buf, CardName( this, status ) ) ); - -/* Pad the name with spaces up to column 8. */ - while ( len < FITSNAMLEN ) buf[ len++ ] = ' '; - -/* If the card contains a keyword value... */ - type = CardType( this, status ); - if( type != AST__COMMENT ){ - -/* Get the number of digits to use when formatting floating point values. */ - digits = astGetFitsDigits( this ); - -/* Put an equals sign in column 9 (or a space if the keyword is a CONTINUE - card), followed by a space in column 10. */ - buf[ len++ ] = ( type == AST__CONTINUE ) ? ' ' : '='; - buf[ len++ ] = ' '; - -/* Format and store the keyword value, starting at column 11 and update the - output string length. */ - len += EncodeValue( this, buf + len, FITSNAMLEN + 3, digits, - method, status ); - -/* If there is a comment, determine which column it should start in so that - it ends in column 80. */ - if( com ){ - comstart = AST__FITSCHAN_FITSCARDLEN - ( comlen - 2 ) + 1; - -/* Adjust the starting column to 32 if possible, avoiding over-writing - the value, or running off the end of the card unless this is - unavoidable. */ - if ( comstart > FITSCOMCOL ) comstart = FITSCOMCOL; - if ( comstart < len + 2 ) comstart = len + 2; - -/* Pad the output buffer with spaces up to the start of the comment. */ - while ( len < comstart - 1 ) buf[ len++ ] = ' '; - -/* Then append "/ " to introduce the comment, truncating if the card - length forces this. */ - for ( i = 0; ( i < 2 ) && ( len < AST__FITSCHAN_FITSCARDLEN ); i++ ) { - buf[ len++ ] = "/ "[ i ]; - } - } - } - -/* Append any comment, truncating it if the card length forces - this. */ - if ( com ) { - for ( i = 0; com[ i ] && ( len < AST__FITSCHAN_FITSCARDLEN ); i++ ) { - buf[ len++ ] = com[ i ]; - } - } - -/* Pad with spaces up to the end of the card. */ - while ( len < AST__FITSCHAN_FITSCARDLEN ) buf[ len++ ] = ' '; - -/* Terminate it. */ - buf[ AST__FITSCHAN_FITSCARDLEN ] = 0; -} - -static int FullForm( const char *list, const char *test, int abbrev, int *status ){ -/* -* Name: -* FullForm - -* Purpose: -* Identify the full form of an option string. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int FullForm( const char *list, const char *test, int abbrev, int *status ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function identifies a supplied test option within a supplied -* list of valid options, and returns the index of the option within -* the list. The test option may be abbreviated, and case is -* insignificant. - -* Parameters: -* list -* A list of space separated option strings. -* test -* A candidate option string. -* abbrev -* 1 if abbreviations are to be accepted. Zero otherwise. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The index of the identified option within the supplied list, starting -* at zero. -1 is returned if the option is not recognised, and (if -* abbrev is 1 ) -2 if the option is ambiguous (no errors are reported -* in these cases). If abbrev is zero, the returned index will be the -* index of the first matching string. -* - -* Notes: -* - A value of -1 is returned if an error has already occurred, or -* if this function should fail for any reason. -*/ - -/* Local Variables: */ - char *context; /* Context used by strtok_r */ - char *llist; /* Pointer to a local copy of the options list */ - char *option; /* Pointer to the start of the next option */ - int i; /* Current option index */ - int len; /* Length of supplied option */ - int nmatch; /* Number of matching options */ - int ret; /* The returned index */ - -/* Initialise the answer to indicate that the option has not been - identified. */ - ret = -1; - -/* Avoid compiler warnings. */ - context = NULL; - -/* Check global status. */ - if( !astOK ) return ret; - -/* Take a local copy of the supplied options list. This is necessary since - "strtok" modified the string by inserting null characters. */ - llist = (char *) astStore( NULL, (void *) list, strlen(list) + 1 ); - if( astOK ){ - -/* Save the number of characters in the supplied test option (excluding - trailing spaces). */ - len = ChrLen( test, status ); - -/* Compare the supplied test option against each of the known options in - turn. Count the number of matches. */ - nmatch = 0; -#if HAVE_STRTOK_R - option = strtok_r( llist, " ", &context ); -#else - option = strtok( llist, " " ); -#endif - i = 0; - while( option ){ - -/* If every character in the supplied label matches the corresponding - character in the current test label we have a match. Increment the - number of matches and save the current item index. If abbreviation is - not allowed ensure that the lengths of the strings are equal. */ - if( !Ustrncmp( test, option, len, status ) && ( abbrev || - len == ChrLen( option, status ) ) ) { - nmatch++; - ret = i; - if( !abbrev ) break; - } - -/* Get a pointer to the next option. */ -#if HAVE_STRTOK_R - option = strtok_r( NULL, " ", &context ); -#else - option = strtok( NULL, " " ); -#endif - i++; - } - -/* Return -1 if no match was found. */ - if( !nmatch ){ - ret = -1; - -/* Return -2 if the option was ambiguous. */ - } else if( abbrev && nmatch > 1 ){ - ret = -2; - } - -/* Free the local copy of the options list. */ - llist = (char *) astFree( (void *) llist ); - } - -/* Return the answer. */ - return ret; -} - -static const char *GetAllWarnings( AstFitsChan *this, int *status ){ - -/* -*+ -* Name: -* astGetAllWarnings - -* Purpose: -* Return a list of all condition names. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* const char *GetAllWarnings( AstFitsChan *this ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function returns a space separated lits of the condition names -* currently recognized by the Warnings attribute. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* A pointer to a static string holding the condition names. - -* Notes: -* - This routine does not check the inherited status. -*- -*/ - -/* Return the result. */ - return ALLWARNINGS; -} -const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { - -/* -* Name: -* GetAttrib - -* Purpose: -* Get the value of a specified attribute for a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the protected astGetAttrib -* method inherited from the Channel class). - -* Description: -* This function returns a pointer to the value of a specified -* attribute for a FitsChan, formatted as a character string. - -* Parameters: -* this -* Pointer to the FitsChan. -* attrib -* Pointer to a null-terminated string containing the name of -* the attribute whose value is required. This name should be in -* lower case, with all white space removed. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* - Pointer to a null-terminated string containing the attribute -* value. - -* Notes: -* - The returned string pointer may point at memory allocated -* within the FitsChan, or at static memory. The contents of the -* string may be over-written or the pointer may become invalid -* following a further invocation of the same function or any -* modification of the FitsChan. A copy of the string should -* therefore be made if necessary. -* - 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: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - const char *result; /* Pointer value to return */ - double dval; /* Double attribute value */ - int ival; /* Integer attribute value */ - -/* Initialise. */ - result = NULL; - -/* Check the global error status. */ - if ( !astOK ) return result; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_object); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_object; - -/* Card. */ -/* ----- */ - if ( !strcmp( attrib, "card" ) ) { - ival = astGetCard( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* CardComm. */ -/* --------- */ - } else if ( !strcmp( attrib, "cardcomm" ) ) { - result = astGetCardComm( this ); - -/* CardName. */ -/* --------- */ - } else if ( !strcmp( attrib, "cardname" ) ) { - result = astGetCardName( this ); - -/* CardType. */ -/* --------- */ - } else if ( !strcmp( attrib, "cardtype" ) ) { - ival = astGetCardType( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* Encoding. */ -/* --------- */ - } else if ( !strcmp( attrib, "encoding" ) ) { - ival = astGetEncoding( this ); - if ( astOK ) { - if( ival == NATIVE_ENCODING ){ - result = NATIVE_STRING; - } else if( ival == FITSPC_ENCODING ){ - result = FITSPC_STRING; - } else if( ival == FITSIRAF_ENCODING ){ - result = FITSIRAF_STRING; - } else if( ival == FITSAIPS_ENCODING ){ - result = FITSAIPS_STRING; - } else if( ival == FITSAIPSPP_ENCODING ){ - result = FITSAIPSPP_STRING; - } else if( ival == FITSCLASS_ENCODING ){ - result = FITSCLASS_STRING; - } else if( ival == FITSWCS_ENCODING ){ - result = FITSWCS_STRING; - } else if( ival == DSS_ENCODING ){ - result = DSS_STRING; - } else { - result = UNKNOWN_STRING; - } - } - -/* CDMatrix */ -/* -------- */ - } else if ( !strcmp( attrib, "cdmatrix" ) ) { - ival = astGetCDMatrix( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* DefB1950 */ -/* -------- */ - } else if ( !strcmp( attrib, "defb1950" ) ) { - ival = astGetDefB1950( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* TabOK */ -/* ----- */ - } else if ( !strcmp( attrib, "tabok" ) ) { - ival = astGetTabOK( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* CarLin */ -/* ------ */ - } else if ( !strcmp( attrib, "carlin" ) ) { - ival = astGetCarLin( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* SipReplace */ -/* ---------- */ - } else if ( !strcmp( attrib, "sipreplace" ) ) { - ival = astGetSipReplace( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* FitsTol. */ -/* -------- */ - } else if ( !strcmp( attrib, "fitstol" ) ) { - dval = astGetFitsTol( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); - result = getattrib_buff; - } - -/* PolyTan */ -/* ------- */ - } else if ( !strcmp( attrib, "polytan" ) ) { - ival = astGetPolyTan( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* SipOK */ -/* ------- */ - } else if ( !strcmp( attrib, "sipok" ) ) { - ival = astGetSipOK( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* Iwc */ -/* --- */ - } else if ( !strcmp( attrib, "iwc" ) ) { - ival = astGetIwc( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* Clean */ -/* ----- */ - } else if ( !strcmp( attrib, "clean" ) ) { - ival = astGetClean( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* FitsAxisOrder. */ -/* -------------- */ - } else if ( !strcmp( attrib, "fitsaxisorder" ) ) { - result = astGetFitsAxisOrder( this ); - -/* FitsDigits. */ -/* ----------- */ - } else if ( !strcmp( attrib, "fitsdigits" ) ) { - ival = astGetFitsDigits( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* Ncard. */ -/* ------ */ - } else if ( !strcmp( attrib, "ncard" ) ) { - ival = astGetNcard( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* Nkey. */ -/* ----- */ - } else if ( !strcmp( attrib, "nkey" ) ) { - ival = astGetNkey( this ); - if ( astOK ) { - (void) sprintf( getattrib_buff, "%d", ival ); - result = getattrib_buff; - } - -/* AllWarnings */ -/* ----------- */ - } else if ( !strcmp( attrib, "allwarnings" ) ) { - result = astGetAllWarnings( this ); - -/* Warnings. */ -/* -------- */ - } else if ( !strcmp( attrib, "warnings" ) ) { - result = astGetWarnings( this ); - -/* If the attribute name was not recognised, pass it on to the parent - method for further interpretation. */ - } else { - result = (*parent_getattrib)( this_object, attrib, status ); - } - -/* Return the result. */ - return result; -} - -static int GetCard( AstFitsChan *this, int *status ){ - -/* -*+ -* Name: -* astGetCard - -* Purpose: -* Get the value of the Card attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* int astGetCard( AstFitsChan *this ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function returns the value of the Card attribute for the supplied -* FitsChan. This is the index of the next card to be read from the -* FitsChan. The index of the first card is 1. If there are no more -* cards to be read, a value one greater than the number of cards in the -* FitsChan is returned. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* The index of the next card to be read. - -* Notes: -* - A value of zero will be returned if the current card is not defined. -* - This function attempts to execute even if an error has occurred. -*- -*/ - -/* Local Variables: */ - const char *class; /* Pointer to class string */ - const char *method; /* Pointer to method string */ - FitsCard *card0; /* Pointer to current FitsCard */ - int index; /* Index of next FitsCard */ - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Return if no FitsChan was supplied, or if the FitsChan is empty. */ - if ( !this || !this->head ) return 0; - -/* Store the method and object class. */ - method = "astGetCard"; - class = astGetClass( this ); - -/* Save a pointer to the current card, and the reset the current card to - be the first card. */ - card0 = this->card; - astClearCard( this ); - -/* Count through the list of FitsCards in the FitsChan until the original - current card is reached. If the current card is not found (for instance - if it has been marked as deleted and we are currently skipping such cards), - this->card will be left null (end-of-file). */ - index = 1; - while( this->card != card0 && astOK && this->card ){ - -/* Increment the card count and move on to the next card. */ - index++; - MoveCard( this, 1, method, class, status ); - } - -/* Return the card index. */ - return index; -} - -static const char *GetCardComm( AstFitsChan *this, int *status ){ -/* -*+ -* Name: -* GetCardComm - -* Purpose: -* Get the value of the CardComm attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* const char *astGetCardComm( AstFitsChan *this) - -* Class Membership: -* FitsChan method. - -* Description: -* This function returns the value of the CardComm attribute for the -* supplied FitsChan. This is the comment for the current card. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* A pointer to a static string holding the comment. A zero-length -* string is returned if the card has no comment. - -* Notes: -* - A value of NULL will be returned if an error has already -* occurred, or if this function should fail for any reason. -*- -*/ - -/* Local Variables */ - const char *result = NULL; - -/* Check inherited status */ - if( !astOK ) return result; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Get the comment for the current card. */ - result = CardComm( this, status ); - -/* Return a zero-length string if the card has no comment. */ - if( astOK && !result ) result = ""; - -/* Return the comment. */ - return result; -} - -static const char *GetCardName( AstFitsChan *this, int *status ){ -/* -*+ -* Name: -* GetCardName - -* Purpose: -* Get the value of the CardName attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* const char *astGetCardName( AstFitsChan *this) - -* Class Membership: -* FitsChan method. - -* Description: -* This function returns the value of the CardName attribute for the -* supplied FitsChan. This is the keyword name for the current card. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* A pointer to a static string holding the keyword name. - -* Notes: -* - A value of NULL will be returned if an error has already -* occurred, or if this function should fail for any reason. -*- -*/ - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Return the keyword name of the current card. */ - return CardName( this, status ); -} - -static int GetCardType( AstFitsChan *this, int *status ){ -/* -*+ -* Name: -* GetCardType - -* Purpose: -* Get the value of the CardType attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* int astGetCardType( AstFitsChan *this ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function returns the value of teh CardType attribute for the supplied -* FitsChan. This is the data type of the keyword value for the current card. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* An integer representing the data type of the current card. - -* Notes: -* - A value of AST__NOTYPE will be returned if an error has already -* occurred, or if this function should fail for any reason. -*- -*/ - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Return the data type of the current card. */ - return CardType( this, status ); -} - -static int GetFull( AstChannel *this_channel, int *status ) { -/* -* Name: -* GetFull - -* Purpose: -* Obtain the value of the Full attribute for a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetFull( AstChannel *this, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the protected astGetFull -* method inherited from the Channel class). - -* Description: -* This function return the integer value of the Full attribute for -* a FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The Full attribute value. - -* Notes: -* - This function modifies the default Full value from 0 to -1 for -* the benefit of the FitsChan class. This prevents non-essential -* information being written by the astWrite method unless it is -* requested by explicitlt setting a Full value. -* - A value of zero will be returned if this function is invoked -* with the global error status set, or if it should fail for any -* reason. -*/ - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - int result; /* Result value to return */ - -/* Check the global error status. */ - if ( !astOK ) return 0; - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* If the Full attribute us set, obtain its value using the parent class - method. */ - if ( astTestFull( this ) ) { - result = (* parent_getfull)( this_channel, status ); - -/* Otherwise, supply a default value of -1. */ - } else { - result = -1; - } - -/* Return the result. */ - return result; -} - -static FitsCard *GetLink( FitsCard *card, int next, const char *method, - const char *class, int *status ){ -/* -* Name: -* GetLink - -* Purpose: -* Get a pointer to the next or previous card in the list. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* FitsCard *GetLink( FitsCard *card, int next, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns the a pointer to either the next or previous FitsCard -* structure in the circular linked list of such structures stored in a -* FitsChan. A check is performed to ensure that the forward and -* backward links from the supplied card are consistent and an error -* is reported if they are not (so long as no previous error has been -* reported). Memory corruption can result in inconsistent links -* which can result in infinite loops if an attempt is made to scan the -* list. - -* Parameters: -* card -* The current card. -* next -* If non-zero, a pointer to the "next" card is returned. Otherwise -* a pointer to the "previous" card is returned. -* method -* Pointer to string holding the name of the calling method. -* class -* Pointer to string holding the object class. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the required card, or NULL if an error occurs. - -* Notes: -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - FitsCard *ret; /* Pointer to the returned card */ - -/* Check that the "next" link from the previous card points back to - the current card, and that the "prev" link from the next card points - back to the current card. */ - if( card && ( card->prev->next != card || - card->next->prev != card ) ){ - -/* Report an error so long as no previous error has been reported, and - return a NULL pointer. */ - if( astOK ){ - astError( AST__FCRPT, "%s(%s): A corrupted %s object has been " - "supplied.", status, method, class, class ); - } - ret = NULL; - -/* If the links are good, return a pointer to the required card. */ - } else { - ret = next ? card->next : card->prev; - } - -/* Return the result. */ - return ret; -} - -static int GetNcard( AstFitsChan *this, int *status ){ - -/* -*+ -* Name: -* astGetNcard - -* Purpose: -* Get the value of the Ncard attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* int astGetNcard( AstFitsChan *this ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function returns the value of the Ncard attribute for the supplied -* FitsChan. This is the number of cards currently in the FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* The number of cards currently in the FitsChan. - -* Notes: -* - A value of zero will be returned if an error has already -* occurred, or if this function should fail for any reason. -*- -*/ - -/* Local Variables: */ - const char *class; /* Pointer to class string */ - const char *method; /* Pointer to method string */ - FitsCard *card0; /* Pointer to current card on entry */ - int ncard; /* Number of cards so far */ - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Return zero if an error has already occurred, or no FitsChan was supplied, - or the FitsChan is empty. */ - if ( !astOK || !this || !this->head ) return 0; - -/* Store the method and object class. */ - method = "astGetNcard"; - class = astGetClass( this ); - -/* Save a pointer to the current card, and then reset the current card to - be the first card. */ - card0 = this->card; - astClearCard( this ); - -/* Count through the cards in the FitsChan until the end of file is reached. */ - ncard = 0; - while( astOK && this->card ){ - -/* Increment the card count and move on to the next card. */ - ncard++; - MoveCard( this, 1, method, class, status ); - } - -/* Reset the current card to be the original current card. */ - this->card = card0; - -/* Return the result. */ - return astOK ? ncard : 0; -} - -static int GetNkey( AstFitsChan *this, int *status ){ - -/* -*+ -* Name: -* astGetNkey - -* Purpose: -* Get the value of the Nkey attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* int astGetNkey( AstFitsChan *this ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function returns the value of the Nkey attribute for the supplied -* FitsChan. This is the number of unique keywords currently in the -* FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* The number of unique keywords currently in the FitsChan. - -* Notes: -* - A value of zero will be returned if an error has already -* occurred, or if this function should fail for any reason. -*- -*/ - -/* Local Variables: */ - AstKeyMap *km; /* KeyMap holding unique keyword names */ - FitsCard *card0; /* Pointer to current card on entry */ - const char *class; /* Pointer to class string */ - const char *method; /* Pointer to method string */ - int nkey; /* Returned Nkey value */ - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Return zero if an error has already occurred, or no FitsChan was supplied, - or the FitsChan is empty. */ - if ( !astOK || !this || !this->head ) return 0; - -/* Store the method and object class. */ - method = "astGetNkey"; - class = astGetClass( this ); - -/* Create an empty KeyMap to hold the unused keyword names */ - km = astKeyMap( " ", status ); - -/* Save a pointer to the current card, and then reset the current card to - be the first card. */ - card0 = this->card; - astClearCard( this ); - -/* Loop through the cards in the FitsChan until the end of file is reached. */ - while( astOK && this->card ){ - -/* Get the keyword name for the current card and add it to the keymap. */ - astMapPut0I( km, CardName( this, status ), 0, NULL ); - -/* Move on to the next unused card. */ - MoveCard( this, 1, method, class, status ); - } - -/* Reset the current card to be the original current card. */ - this->card = card0; - -/* Get the number of keywords. */ - nkey = astMapSize( km ); - -/* Annull the KeyMap . */ - km = astAnnul( km ); - -/* Return the result. */ - return astOK ? nkey : 0; -} - -static void GetNextData( AstChannel *this_channel, int skip, char **name, - char **val, int *status ) { -/* -* Name: -* GetNextData - -* Purpose: -* Read the next item of data from a data source. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void GetNextData( AstChannel *this, int skip, char **name, char **val ) - -* Class Membership: -* FitsChan member function (over-rides the protected -* astGetNextData method inherited from the Channel class). - -* Description: -* This function reads the next item of input data from a data -* source associated with a FitsChan and returns the result. It -* decodes the data item and returns name/value pairs ready for -* use. - -* Parameters: -* this -* Pointer to the FitsChan. -* skip -* A non-zero value indicates that a new Object is to be read, -* and that all input data up to the next "Begin" item are to be -* skipped in order to locate it. This is useful if the data -* source contains AST objects interspersed with other data (but -* note that these other data cannot appear inside AST Objects, -* only between them). -* -* A zero value indicates that all input data are significant -* and the next item will therefore be read and an attempt made -* to interpret it whatever it contains. Any other data -* inter-mixed with AST Objects will then result in an error. -* name -* An address at which to store a pointer to a null-terminated -* dynamically allocated string containing the name of the next -* item in the input data stream. This name will be in lower -* case with no surrounding white space. It is the callers -* responsibilty to free the memory holding this string (using -* astFree) when it is no longer required. -* -* A NULL pointer value will be returned (without error) to -* indicate when there are no further input data items to be -* read. -* val -* An address at which to store a pointer to a null-terminated -* dynamically allocated string containing the value associated -* with the next item in the input data stream. No case -* conversion is performed on this string and all white space is -* potentially significant. It is the callers responsibilty to -* free the memory holding this string (using astFree) when it -* is no longer required. -* -* The returned pointer will be NULL if an Object data item is -* read (see the "Data Representation" section). - -* Data Representation: - -* The returned data items fall into the following categories: -* -* - Begin: Identified by the name string "begin", this indicates -* the start of an Object definition. The associated value string -* gives the class name of the Object being defined. -* -* - IsA: Identified by the name string "isa", this indicates the -* end of the data associated with a particular class structure -* within the definiton of a larger Object. The associated value -* string gives the name of the class whose data have just been -* read. -* -* - End: Identified by the name string "end", this indicates the -* end of the data associated with a complete Object -* definition. The associated value string gives the class name of -* the Object whose definition is being ended. -* -* - Non-Object: Identified by any other name string plus a -* non-NULL "val" pointer, this gives the value of a non-Object -* structure component (instance variable). The name identifies -* which instance variable it is (within the context of the class -* whose data are being read) and the value is encoded as a string. -* -* - Object: Identified by any other name string plus a NULL "val" -* pointer, this identifies the value of an Object structure -* component (instance variable). The name identifies which -* instance variable it is (within the context of the class whose -* data are being read) and the value is given by subsequent data -* items (so the next item should be a "Begin" item). - -* Notes: -* - NULL pointer values will be returned if this function is -* invoked with the global error status set, or if it should fail -* for any reason. -*/ - -/* Local Constants: */ -#define BUFF_LEN 100 /* Length of formatting buffer */ - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - char *keyword; /* Pointer to current keyword string */ - char *newdata; /* Pointer to stripped string value */ - char *upq; /* Pointer to unprequoted string */ - char buff[ BUFF_LEN + 1 ]; /* Buffer for formatting values */ - const char *class; /* Pointer to object class */ - const char *method; /* Pointer to method name */ - int cont; /* String ends with an ampersand? */ - int done; /* Data item found? */ - int freedata; /* Should the data pointer be freed? */ - int i; /* Loop counter for keyword characters */ - int len; /* Length of current keyword */ - int nc; /* Number of characters read by "astSscanf" */ - int nn; /* No. of characters after UnPreQuoting */ - int type; /* Data type code */ - void *data; /* Pointer to current data value */ - -/* Initialise the returned pointer values. */ - *name = NULL; - *val = NULL; - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Store the method name and object class. */ - method = "astRead"; - class = astGetClass( this ); - -/* Loop to consider successive cards stored in the FitsChan (starting - at the "current" card) until a valid data item is read or "end of - file" is reached. Also quit the loop if an error occurs. */ - done = 0; - newdata = NULL; - while ( !done && !astFitsEof( this ) && astOK ){ - -/* Obtain the keyword string, data type code and data value pointer - from the current card. */ - keyword = CardName( this, status ); - type = CardType( this, status ); - data = CardData( this, NULL, status ); - -/* Mark all cards as having been used unless we are skipping over cards which - may not be related to AST. */ - if( !skip ) MarkCard( this, status ); - -/* Ignore comment cards. */ - if ( type != AST__COMMENT ) { - -/* Native encoding requires trailing white space to be removed from - string values (so that null strings can be distinguished from blank - strings). Do this now. */ - freedata = 0; - if ( ( type == AST__STRING || type == AST__CONTINUE ) && data ){ - newdata = (char *) astStore( NULL, data, strlen( (char *) data ) + 1 ); - if( newdata ){ - newdata[ ChrLen( data, status ) ] = 0; - data = (void *) newdata; - freedata = 1; - } - } - -/* Obtain the keyword length and test the card to identify the type of - AST data item (if any) that it represents. */ - len = (int) strlen( keyword ); - -/* "Begin" item. */ -/* ------------- */ - -/* This is identified by a string value and a keyword of the form - "BEGASTxx", where "xx" are characters encoding a sequence - number. */ - if ( ( type == AST__STRING ) && - ( nc = 0, - ( 0 == astSscanf( keyword, "BEGAST" - "%*1[" SEQ_CHARS "]" - "%*1[" SEQ_CHARS "]%n", &nc ) ) - && ( nc >= len ) ) ) { - -/* Note we have found a data item. */ - done = 1; - -/* Set the returned name to "begin" and extract the associated class - name from the string value. Store both of these in dynamically - allocated strings. */ - *name = astString( "begin", 5 ); - *val = UnPreQuote( (const char *) data, status ); - -/* Indicate that the current card has been used. */ - MarkCard( this, status ); - -/* The "begin" item will be preceded by a header of COMMENT cards. Mark - them as having been used. */ - ComBlock( this, -1, method, class, status ); - -/* "IsA" item. */ -/* ----------- */ - -/* This is identified by a string value and a keyword of the form - "ISAxx", where "xx" are characters encoding a sequence - number. Don't accept the item if we are skipping over cards looking - for a "Begin" item. */ - } else if ( !skip && - ( type == AST__STRING ) && - ( nc = 0, - ( 0 == astSscanf( keyword, - "ISA" - "%*1[" SEQ_CHARS "]" - "%*1[" SEQ_CHARS "]%n", &nc ) ) - && ( nc >= len ) ) ) { - -/* Note we have found a data item. */ - done = 1; - -/* Set the returned name to "isa" and extract the associated class - name from the string value. Store both of these in dynamically - allocated strings. */ - *name = astString( "isa", 3 ); - *val = UnPreQuote( (const char *) data, status ); - -/* "End" item. */ -/* ----------- */ - -/* This is identified by a string value and a keyword of the form - "ENDASTxx", where "xx" are characters encoding a sequence - number. Don't accept the item if we are skipping over cards looking - for a "Begin" item. */ - } else if ( !skip && - ( type == AST__STRING ) && - ( nc = 0, - ( 0 == astSscanf( keyword, - "ENDAST" - "%*1[" SEQ_CHARS "]" - "%*1[" SEQ_CHARS "]%n", &nc ) ) - && ( nc >= len ) ) ) { - -/* Note we have found a data item. */ - done = 1; - -/* Set the returned name to "end" and extract the associated class - name from the string value. Store both of these in dynamically - allocated strings. */ - *name = astString( "end", 3 ); - *val = UnPreQuote( (const char *) data, status ); - -/* The "end" item eill be followed by a footer of COMMENT cards. Mark - these cards as having been used. */ - ComBlock( this, 1, method, class, status ); - -/* Object or data item. */ -/* -------------------- */ - -/* These are identified by a string, int, or double value, and a - keyword ending in two characters encoding a sequence number. Don't - accept the item if we are skipping over cards looking for a "Begin" - item. */ - } else if ( !skip && - ( ( type == AST__STRING ) || - ( type == AST__INT ) || - ( type == AST__FLOAT ) ) && - ( len > 2 ) && - strchr( SEQ_CHARS, keyword[ len - 1 ] ) && - strchr( SEQ_CHARS, keyword[ len - 2 ] ) ) { - -/* Note we have found a data item. */ - done = 1; - -/* Set the returned name by removing the last two characters from the - keyword and converting to lower case. Store this in a dynamically - allocated string. */ - *name = astString( keyword, len - 2 ); - for ( i = 0; ( *name )[ i ]; i++ ) { - ( *name )[ i ] = tolower( ( *name )[ i ] ); - } - -/* Classify the data type. */ - switch ( type ) { - -/* If the value is a string, test if it is zero-length. If so, this - "null" value indicates an Object data item (whose definition - follows), so leave the returned value pointer as NULL. Otherwise, - we have a string data item, so extract its value and store it in a - dynamically allocated string. */ - case AST__STRING: - if ( *( (char *) data ) ) { - -/* A long string value may be continued on subsequent CONTINUE cards. See - if the current string may be continued. This is the case if the final - non-blank character (before UnPreQuoting) is an ampersand. */ - cont = ( ((char *) data)[ ChrLen( data, status ) - 1 ] == '&' ); - -/* If the string does not end with an ampersand, just UnPreQUote it and - return a copy. */ - if( !cont ) { - *val = UnPreQuote( (const char *) data, status ); - -/* Otherwise, initialise the returned string to hold a copy of the keyword - value. */ - } else { - nc = strlen( (const char *) data ); - *val = astStore( NULL, (const char *) data, nc + 1 ); - -/* Loop round reading any subsequent CONTINUE cards. Leave the loop when - the end-of-file is hit, or an error occurs. */ - while( cont && MoveCard( this, 1, method, class, status ) && - astOK ){ - -/* See if this is a CONTINUE card. If so, get its data pointer. */ - if( CardType( this, status ) == AST__CONTINUE ){ - data = CardData( this, NULL, status ); - -/* See if the CONTINUE card ends with an ampersand (i.e. if there is - a possibility of there being any remaining CONTINUE cards). */ - cont = ( ( (char *) data)[ ChrLen( data, status ) - 1 ] == '&' ); - -/* UnPreQUote it. */ - upq = UnPreQuote( (const char *) data, status ); - if( !astOK ) break; - -/* Expand the memory for the returned string to hold the new string. */ - nn = strlen( upq ); - *val = astRealloc( *val, nc + nn ); - if( !astOK ) break; - -/* Copy the new string into the expanded memory, so that the first - character of the new string over-writes the trailing ampersand - currently in the buffer. */ - strcpy( *val + nc - 1, upq ); - -/* Release the memory holding the UnPreQUoted string . */ - upq = astFree( upq ); - -/* Update the current length of the returned string. */ - nc += nn - 1; - -/* Mark the current card as having been read. */ - MarkCard( this, status ); - -/* Report an error if this is not a CONTINUE card. */ - } else { - astError( AST__BADIN, "%s(%s): One or more " - "FITS \"CONTINUE\" cards are missing " - "after the card for keyword \"%s\".", status, - method, class, keyword ); - } - } - } - } - break; - -/* If the value is an int, format it and store the result in a - dynamically allocated string. */ - case AST__INT: - (void) sprintf( buff, "%d", *( (int *) data ) ); - *val = astString( buff, (int) strlen( buff ) ); - break; - -/* If the value is a double, format it and store the result in a - dynamically allocated string. */ - case AST__FLOAT: - (void) sprintf( buff, "%.*g", AST__DBL_DIG, *( (double *) data ) ); - CheckZero( buff, *( (double *) data ), 0, status ); - *val = astString( buff, (int) strlen( buff ) ); - break; - } - -/* Anything else. */ -/* -------------- */ - -/* If the input line didn't match any of the above and the "skip" flag - is not set, then report an error.. */ - } else if ( !skip ) { - astError( AST__BADIN, - "%s(%s): Cannot interpret the input data given by " - "FITS keyword \"%s\".", status, method, class, keyword ); - } - -/* Free any memory used to hold stripped string data. */ - if( freedata ) newdata = (char *) astFree( (void *) newdata ); - } - -/* Increment the current card. */ - MoveCard( this, 1, method, class, status ); - } - -/* If an error occurred, ensure that any allocated memory is freed and - that NULL pointer values are returned. */ - if ( !astOK ) { - *name = astFree( *name ); - *val = astFree( *val ); - } - -/* Undefine macros local to this function. */ -#undef BUFF_LEN -} - -static int GetSkip( AstChannel *this_channel, int *status ) { -/* -* Name: -* GetSkip - -* Purpose: -* Obtain the value of the Skip attribute for a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int GetSkip( AstChannel *this, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the protected astGetSkip -* method inherited from the Channel class). - -* Description: -* This function return the (boolean) integer value of the Skip -* attribute for a FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The Skip attribute value. - -* Notes: -* - This function modifies the default Skip value from 0 to 1 for -* the benefit of the FitsChan class. This default value allows the -* astRead method to skip over unrelated FITS keywords when -* searching for the next Object to read. -* - A value of zero will be returned if this function is invoked -* with the global error status set, or if it should fail for any -* reason. -*/ - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - int result; /* Result value to return */ - -/* Check the global error status. */ - if ( !astOK ) return 0; - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* If the Skip attribute us set, obtain its value using the parent class - method. */ - if ( astTestSkip( this ) ) { - result = (* parent_getskip)( this_channel, status ); - -/* Otherwise, supply a default value of 1. */ - } else { - result = 1; - } - -/* Return the result. */ - return result; -} - -static int GetValue( AstFitsChan *this, const char *keyname, int type, - void *value, int report, int mark, const char *method, - const char *class, int *status ){ -/* -* Name: -* GetValue - -* Purpose: -* Obtain a FITS keyword value. - -* Type: -* Private function. - -* Synopsis: -* int GetValue( AstFitsChan *this, const char *keyname, int type, void *value, -* int report, int mark, const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function gets a value for the specified keyword from the -* supplied FitsChan, and stores it in the supplied buffer. Optionally, -* the keyword is marked as having been read into an AST object so that -* it is not written out when the FitsChan is deleted. - -* Parameters: -* this -* A pointer to the FitsChan containing the keyword values to be -* read. -* keyname -* A pointer to a string holding the keyword name. -* type -* The FITS data type in which to return the keyword value. If the -* stored value is not of the requested type, it is converted if -* possible. -* value -* A pointer to a buffer of suitable size to receive the keyword -* value. The supplied value is left unchanged if the keyword is -* not found. -* report -* Should an error be reported if the keyword cannot be found, or -* cannot be converted to the requested type? -* mark -* Should the card be marked as having been used? -* method -* A string holding the name of the calling method. -* class -* A string holding the object class. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero if the keyword does not exist in "this", or cannot be -* converted to the requested type. One is returned otherwise. - -* Notes: -* - An error is reported if the keyword value is undefined. -* - A value of zero is returned if an error has already occurred, -* or if an error occurs within this function. -*/ - -/* Local Variables: */ - int icard; /* Current card index */ - int ret; /* Returned value */ - -/* Check the status */ - if( !astOK ) return 0; - -/* Save the current card index. */ - icard = astGetCard( this ); - -/* Attempt to find the supplied keyword. */ - ret = SearchCard( this, keyname, method, class, status ); - -/* If the keyword was found, convert the current card's data value and copy - it to the supplied buffer. */ - if( ret ){ - if( CnvValue( this, type, 0, value, method, status ) ) { - -/* If required, mark it as having been read into an AST object. */ - if( mark ) MarkCard( this, status ); - -/* If the value is undefined, report an error if "report" is non-zero. */ - if( type == AST__UNDEF && report && astOK ) { - ret = 0; - astError( AST__FUNDEF, "%s(%s): FITS keyword \"%s\" has no value.", - status, method, class, keyname ); - } - -/* If the value could not be converted to the requested data, type report - an error if reporting is enabled. */ - } else { - ret = 0; - if( report && astOK ){ - astError( AST__FTCNV, "%s(%s): Cannot convert FITS keyword '%s' to %s.", - status, method, class, keyname, type_names[ type ] ); - } - } - -/* If the keyword was not found, report an error if "report" is non-zero. */ - } else if( report && astOK ){ - astError( AST__BDFTS, "%s(%s): Unable to find a value for FITS " - "keyword \"%s\".", status, method, class, keyname ); - } - -/* Reinstate the original current card index. */ - astSetCard( this, icard ); - -/* If an error has occurred, return 0. */ - if( !astOK ) ret = 0; - -/* Return the result. */ - return ret; -} - -static int GetValue2( AstFitsChan *this1, AstFitsChan *this2, const char *keyname, - int type, void *value, int report, const char *method, - const char *class, int *status ){ -/* -* Name: -* GetValue2 - -* Purpose: -* Obtain a FITS keyword value from one of two FitsChans. - -* Type: -* Private function. - -* Synopsis: -* int GetValue2( AstFitsChan *this1, AstFitsChan *this2, const char *keyname, -* int type, void *value, int report, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function attempts to get a value for the specified keyword from -* the first supplied FitsChan. If this fails (due to the FitsChan not -* containing a value for the ketword) then an attempt is made to get -* a value for the keyword from the second supplied FitsChan. - -* Parameters: -* this1 -* A pointer to the first FitsChan to be used. -* this2 -* A pointer to the second FitsChan to be used. -* keyname -* A pointer to a string holding the keyword name. -* type -* The FITS data type in which to return the keyword value. If the -* stored value is not of the requested type, it is converted if -* possible. -* value -* A pointer to a buffer of suitable size to receive the keyword -* value. The supplied value is left unchanged if the keyword is -* not found. -* report -* Should an error be reported if the keyword cannot be found, or -* cannot be converted to the requested type? -* method -* A string holding the name of the calling method. -* class -* A string holding the object class. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero if the keyword does not exist in either FitsChan, or cannot be -* converted to the requested type. One is returned otherwise. - -* Notes: -* - A value of zero is returned if an error has already occurred, -* or if an error occurs within this function. -* - If the card is found in the first FitsChan, it is not marked as -* having been used. If the card is found in the second FitsChan, it is -* marked as having been used. -*/ - -/* Local Variables: */ - int ret; /* Returned value */ - -/* Check the status */ - if( !astOK ) return 0; - -/* Try the first FitsChan. If this fails try the second. Do not report - an error if the keyword is not found in the first FitsChan (this will - be done, if required, once the second FitsChan has been searched). */ - ret = GetValue( this1, keyname, type, value, 0, 0, method, class, status ); - if( ! ret ) { - ret = GetValue( this2, keyname, type, value, report, 1, method, class, status ); - } - -/* If an error has occurred, return 0. */ - if( !astOK ) ret = 0; - -/* Return the result. */ - return ret; -} - -static int HasAIPSSpecAxis( AstFitsChan *this, const char *method, - const char *class, int *status ){ - -/* -* Name: -* HasAIPSSpecAxis - -* Purpose: -* Does the FitsChan contain an AIPS spectral CTYPE keyword? - -* Type: -* Private function. - -* Synopsis: - -* int HasAIPSSpecAxis( AstFitsChan *this, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function returns a non-zero value if the FitsCHan contains a -* CTYPE value which conforms to the non-standard system used by AIPS. - -* Parameters: -* this -* A pointer to the FitsChan to be used. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Non-zero if an AIPS spectral CTYPE keyword was found. -*/ - -/* Local Variables: */ - char *assys; /* AIPS standard of rest type */ - char *astype; /* AIPS spectral type */ - char *cval; /* Pointer to character string */ - int j; /* Current axis index */ - int jhi; /* Highest axis index with a CTYPE */ - int jlo; /* Lowest axis index with a CTYPE */ - int ret; /* Returned value */ - -/* Initialise */ - ret = 0; - -/* Check the status */ - if( !astOK ) return ret; - -/* If the FitsChan contains any CTYPE values, convert the bounds from - one-based to zero-based, and loop round them all. */ - if( astKeyFields( this, "CTYPE%1d", 1, &jhi, &jlo ) ) { - jlo--; - jhi--; - for( j = jlo; j <= jhi; j++ ) { - -/* Get the next CTYPE value. If found, see if it is an AIPS spectral - CTYPE value. */ - if( GetValue( this, FormatKey( "CTYPE", j + 1, -1, ' ', status ), - AST__STRING, (void *) &cval, 0, 0, method, - class, status ) ){ - if( IsAIPSSpectral( cval, &astype, &assys, status ) ) { - ret = 1; - break; - } - } - } - } - -/* If an error has occurred, return 0. */ - if( !astOK ) ret = 0; - -/* Return the result. */ - return ret; -} - -static int HasCard( AstFitsChan *this, const char *name, - const char *method, const char *class, int *status ){ - -/* -* Name: -* HasCard - -* Purpose: -* Check if the FitsChan contains a specified keyword. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int HasCard( AstFitsChan *this, const char *name, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns a non-zero value if the FitsChan contains the given keyword, -* and zero otherwise. The current card is unchanged. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a string holding the keyword name. -* method -* Pointer to string holding name of calling method. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if a card was found refering to the given -* keyword. Otherwise zero is returned. -*/ - -/* Check the supplied pointers (we can rely on astMapHasKey to check the - inherited status). */ - if( !name || !this || !this->keywords ) return 0; - -/* Search the KeyMap holding the keywords currently in the FitsChan, - returning non-zero if the keyword was found. A KeyMap is used because - it uses a hashing algorithm to find the entries and is therefore a lot - quicker than searching through the list of linked FitsCards. */ - return astMapHasKey( this->keywords, name ); -} -void astInitFitsChanVtab_( AstFitsChanVtab *vtab, const char *name, int *status ) { - -/* -*+ -* Name: -* astInitFitsChanVtab - -* Purpose: -* Initialise a virtual function table for a FitsChan. - -* Type: -* Protected function. - -* Synopsis: -* #include "fitschan.h" -* void astInitFitsChanVtab( AstFitsChanVtab *vtab, const char *name ) - -* Class Membership: -* FitsChan vtab initialiser. - -* Description: -* This function initialises the component of a virtual function -* table which is used by the FitsChan 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 */ - AstChannelVtab *channel; /* Pointer to Channel component of Vtab */ - char buf[ 100 ]; /* Buffer large enough to store formatted INT_MAX */ - -/* 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. */ - astInitChannelVtab( (AstChannelVtab *) vtab, name ); - -/* Store a unique "magic" value in the virtual function table. This - will be used (by astIsAFitsChan) 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 = &(((AstChannelVtab *) vtab)->id); - -/* Initialise member function pointers. */ -/* ------------------------------------ */ - -/* Store pointers to the member functions (implemented here) that provide - virtual methods for this class. */ - vtab->PutCards = PutCards; - vtab->PutFits = PutFits; - vtab->DelFits = DelFits; - vtab->GetTables = GetTables; - vtab->PutTables = PutTables; - vtab->PutTable = PutTable; - vtab->TableSource = TableSource; - vtab->SetTableSource = SetTableSource; - vtab->RemoveTables = RemoveTables; - vtab->PurgeWCS = PurgeWCS; - vtab->RetainFits = RetainFits; - vtab->FindFits = FindFits; - vtab->KeyFields = KeyFields; - vtab->ReadFits = ReadFits; - vtab->ShowFits = ShowFits; - vtab->WriteFits = WriteFits; - vtab->EmptyFits = EmptyFits; - vtab->FitsEof = FitsEof; - vtab->GetFitsCF = GetFitsCF; - vtab->GetFitsCI = GetFitsCI; - vtab->GetFitsF = GetFitsF; - vtab->GetFitsI = GetFitsI; - vtab->GetFitsL = GetFitsL; - vtab->TestFits = TestFits; - vtab->GetFitsS = GetFitsS; - vtab->GetFitsCN = GetFitsCN; - vtab->FitsGetCom = FitsGetCom; - vtab->SetFitsCom = SetFitsCom; - vtab->SetFitsCF = SetFitsCF; - vtab->SetFitsCI = SetFitsCI; - vtab->SetFitsF = SetFitsF; - vtab->SetFitsI = SetFitsI; - vtab->SetFitsL = SetFitsL; - vtab->SetFitsU = SetFitsU; - vtab->SetFitsS = SetFitsS; - vtab->SetFitsCN = SetFitsCN; - vtab->SetFitsCM = SetFitsCM; - vtab->ClearCard = ClearCard; - vtab->TestCard = TestCard; - vtab->SetCard = SetCard; - vtab->GetCard = GetCard; - vtab->ClearFitsDigits = ClearFitsDigits; - vtab->TestFitsDigits = TestFitsDigits; - vtab->SetFitsDigits = SetFitsDigits; - vtab->GetFitsDigits = GetFitsDigits; - vtab->ClearFitsAxisOrder = ClearFitsAxisOrder; - vtab->TestFitsAxisOrder = TestFitsAxisOrder; - vtab->SetFitsAxisOrder = SetFitsAxisOrder; - vtab->GetFitsAxisOrder = GetFitsAxisOrder; - vtab->ClearDefB1950 = ClearDefB1950; - vtab->TestDefB1950 = TestDefB1950; - vtab->SetDefB1950 = SetDefB1950; - vtab->GetDefB1950 = GetDefB1950; - vtab->ClearTabOK = ClearTabOK; - vtab->TestTabOK = TestTabOK; - vtab->SetTabOK = SetTabOK; - vtab->GetTabOK = GetTabOK; - vtab->ClearCarLin = ClearCarLin; - vtab->TestCarLin = TestCarLin; - vtab->SetCarLin = SetCarLin; - vtab->GetCarLin = GetCarLin; - vtab->ClearSipReplace = ClearSipReplace; - vtab->TestSipReplace = TestSipReplace; - vtab->SetSipReplace = SetSipReplace; - vtab->GetSipReplace = GetSipReplace; - vtab->ClearFitsTol = ClearFitsTol; - vtab->TestFitsTol = TestFitsTol; - vtab->SetFitsTol = SetFitsTol; - vtab->GetFitsTol = GetFitsTol; - vtab->ClearPolyTan = ClearPolyTan; - vtab->TestPolyTan = TestPolyTan; - vtab->SetPolyTan = SetPolyTan; - vtab->GetPolyTan = GetPolyTan; - vtab->ClearSipOK = ClearSipOK; - vtab->TestSipOK = TestSipOK; - vtab->SetSipOK = SetSipOK; - vtab->GetSipOK = GetSipOK; - vtab->ClearIwc = ClearIwc; - vtab->TestIwc = TestIwc; - vtab->SetIwc = SetIwc; - vtab->GetIwc = GetIwc; - vtab->ClearWarnings = ClearWarnings; - vtab->TestWarnings = TestWarnings; - vtab->SetWarnings = SetWarnings; - vtab->GetWarnings = GetWarnings; - vtab->GetCardType = GetCardType; - vtab->GetCardName = GetCardName; - vtab->GetCardComm = GetCardComm; - vtab->GetNcard = GetNcard; - vtab->GetNkey = GetNkey; - vtab->GetAllWarnings = GetAllWarnings; - vtab->ClearEncoding = ClearEncoding; - vtab->TestEncoding = TestEncoding; - vtab->SetEncoding = SetEncoding; - vtab->GetEncoding = GetEncoding; - vtab->ClearClean = ClearClean; - vtab->TestClean = TestClean; - vtab->SetClean = SetClean; - vtab->GetClean = GetClean; - vtab->ClearCDMatrix = ClearCDMatrix; - vtab->TestCDMatrix = TestCDMatrix; - vtab->SetCDMatrix = SetCDMatrix; - vtab->GetCDMatrix = GetCDMatrix; - -/* Save the inherited pointers to methods that will be extended, and - replace them with pointers to the new member functions. */ - object = (AstObjectVtab *) vtab; - channel = (AstChannelVtab *) vtab; - parent_getobjsize = object->GetObjSize; - object->GetObjSize = GetObjSize; -#if defined(THREAD_SAFE) - parent_managelock = object->ManageLock; - object->ManageLock = ManageLock; -#endif - parent_clearattrib = object->ClearAttrib; - object->ClearAttrib = ClearAttrib; - parent_getattrib = object->GetAttrib; - object->GetAttrib = GetAttrib; - parent_setattrib = object->SetAttrib; - object->SetAttrib = SetAttrib; - parent_testattrib = object->TestAttrib; - object->TestAttrib = TestAttrib; - parent_write = channel->Write; - channel->Write = Write; - parent_read = channel->Read; - channel->Read = Read; - parent_getskip = channel->GetSkip; - channel->GetSkip = GetSkip; - parent_getfull = channel->GetFull; - channel->GetFull = GetFull; - channel->WriteBegin = WriteBegin; - channel->WriteIsA = WriteIsA; - channel->WriteEnd = WriteEnd; - channel->WriteInt = WriteInt; - channel->WriteDouble = WriteDouble; - channel->WriteString = WriteString; - channel->WriteObject = WriteObject; - channel->GetNextData = GetNextData; - parent_setsourcefile = channel->SetSourceFile; - channel->SetSourceFile = SetSourceFile; - -/* Declare the class dump, copy and delete functions.*/ - astSetDump( vtab, Dump, "FitsChan", "I/O channels to FITS files" ); - astSetCopy( (AstObjectVtab *) vtab, Copy ); - astSetDelete( (AstObjectVtab *) vtab, Delete ); - -/* Max number of characters needed to format an int. */ - LOCK_MUTEX4 - sprintf( buf, "%d", INT_MAX ); - int_dig = strlen( buf ); - -/* Create a pair of MJD TimeFrames which will be used for converting to and - from TDB. */ - astBeginPM; - if( !tdbframe ) tdbframe = astTimeFrame( "system=MJD,timescale=TDB", status ); - if( !timeframe ) timeframe = astTimeFrame( "system=MJD", status ); - astEndPM; - UNLOCK_MUTEX4 - -/* 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 void InsCard( AstFitsChan *this, int overwrite, const char *name, - int type, void *data, const char *comment, - const char *method, const char *class, int *status ){ - -/* -* Name: -* InsCard - -* Purpose: -* Inserts a card into a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void InsCard( AstFitsChan *this, int overwrite, const char *name, -* int type, void *data, const char *comment, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Either appends a new card to a FitsChan, or over-writes an existing -* card, holding the supplied keyword name, value and comment. - -* Parameters: -* this -* Pointer to the FitsChan containing the filters to apply to the -* keyword name. If a NULL pointer is supplied, no filtering is applied. -* overwrite -* If non-zero, the new card over-writes the current card given by -* the "Card" attribute, and the current card is incremented so -* that it refers to the next card. Otherwise, the new card is -* inserted in front of the current card and the current card is -* left unchanged. -* name -* Pointer to a string holding the keyword name of the new card. -* type -* An integer value representing the data type of the keyword. -* data -* Pointer to the data associated with the keyword. -* comment -* Pointer to a null-terminated string holding a comment. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Notes: -* - An error is reported if an attempt is made to change the data type -* of an existing card. -* - If a type of AST__COMMENT is supplied, then any data value (of any -* type) associated with an existing card is left unchanged. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - int flags; /* Flags to assign to new card */ - -/* Check the global status. */ - if( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* If the current card is to be over-written, delete the current card (the - next card in the list, if any, will become the new current card). */ - if( overwrite ) DeleteCard( this, method, class, status ); - -/* If requested, set both NEW flags for the new card. */ - flags = ( mark_new ) ? ( NEW1 | NEW2 ): 0; - -/* Insert the new card into the list, just before the current card. */ - NewCard( this, name, type, data, comment, flags, status ); -} - -static int IRAFFromStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ){ - -/* -* Name: -* IRAFFromStore - -* Purpose: -* Store WCS keywords in a FitsChan using FITS-IRAF encoding. - -* Type: -* Private function. - -* Synopsis: - -* int IRAFFromStore( AstFitsChan *this, FitsStore *store, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function copies the WCS information stored in the supplied -* FitsStore into the supplied FitsChan, using FITS-IRAF encoding. -* -* IRAF encoding is like FITS-WCS encoding but with the following -* restrictions: -* -* 1) The celestial projection must not have any projection parameters -* which are not set to their default values. The one exception to this -* is that SIN projections are acceptable if the associated projection -* parameter PV<axlat>_1 is zero and PV<axlat>_2 = cot( reference point -* latitude). This is encoded using the string "-NCP". The SFL projection -* is encoded using the string "-GLS". Note, the original IRAF WCS -* system only recognised a small subset of the currently available -* projections, but some more recent IRAF-like software recognizes some -* of the new projections included in the FITS-WCS encoding. -* -* 2) The celestial axes must be RA/DEC, galactic or ecliptic. -* -* 3) LONPOLE and LATPOLE cannot be used. -* -* 4) Only primary axis descriptions are written out. -* -* 5) RADECSYS is used in place of RADESYS. -* -* 6) PC/CDELT keywords are not allowed (CD must be used) - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if succesfull, and zero is returned -* otherwise. -*/ - -/* Local Variables: */ - char *comm; /* Pointer to comment string */ - char *cval; /* Pointer to string keyword value */ - char combuf[80]; /* Buffer for FITS card comment */ - char lattype[MXCTYPELEN];/* Latitude axis CTYPE */ - char lontype[MXCTYPELEN];/* Longitude axis CTYPE */ - char s; /* Co-ordinate version character */ - char sign[2]; /* Fraction's sign character */ - double cdelt; /* A CDELT value */ - double fd; /* Fraction of a day */ - double mjd99; /* MJD at start of 1999 */ - double p1, p2; /* Projection parameters */ - double val; /* General purpose value */ - int axlat; /* Index of latitude FITS WCS axis */ - int axlon; /* Index of longitude FITS WCS axis */ - int axspec; /* Index of spectral FITS WCS axis */ - int i; /* Axis index */ - int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ - int iymdf[ 4 ]; /* Year, month, date, fractional day */ - int j; /* Axis index */ - int jj; /* SlaLib status */ - int naxis; /* No. of axes */ - int ok; /* Is FitsSTore OK for IRAF encoding? */ - int prj; /* Projection type */ - int ret; /* Returned value. */ - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* First check that the values in the FitsStore conform to the - requirements of the IRAF encoding. Assume they do to begin with. */ - ok = 1; - -/* Just do primary axes. */ - s = ' '; - -/* Look for the primary celestial and spectral axes. */ - FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); - -/* If both longitude and latitude axes are present and thereis no - spectral axis...*/ - if( axlon >= 0 && axlat >= 0 ) { - -/* Get the CTYPE values for both axes. */ - cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); - if( !cval ) return ret; - strcpy( lontype, cval ); - cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); - if( !cval ) return ret; - strcpy( lattype, cval ); - -/* Extract the projection type as specified by the last 4 characters - in the CTYPE keyword value. */ - prj = astWcsPrjType( lattype + 4 ); - -/* Check the projection type is OK. Assume not initially. */ - ok = 0; - -/* FITS-IRAF cannot handle the AST-specific TPN projection. */ - if( prj == AST__TPN || prj == AST__WCSBAD ) { - ok = 0; - -/* SIN projections are handled later. */ - } else if( prj != AST__SIN ){ - -/* There must be no projection parameters. */ - if( GetMaxJM( &(store->pv), ' ', status ) == -1 ) ok = 1; - -/* Change the new SFL projection code to to the older equivalent GLS */ - if( prj == AST__SFL ){ - (void) strcpy( lontype + 4, "-GLS" ); - (void) strcpy( lattype + 4, "-GLS" ); - } - -/* SIN projections are only acceptable if the associated projection - parameters are both zero, or if the first is zero and the second - = cot( reference point latitude ) (the latter case is equivalent to - the old NCP projection). */ - } else { - p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status ); - p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status ); - if( p1 == AST__BAD ) p1 = 0.0; - if( p2 == AST__BAD ) p2 = 0.0; - val = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - if( p1 == 0.0 ) { - if( p2 == 0.0 ) { - ok = 1; - } else if( fabs( p2 ) >= 1.0E14 && val == 0.0 ){ - ok = 1; - (void) strcpy( lontype + 4, "-NCP" ); - (void) strcpy( lattype + 4, "-NCP" ); - } else if( fabs( p2*tan( AST__DD2R*val ) - 1.0 ) - < 0.01 ){ - ok = 1; - (void) strcpy( lontype + 4, "-NCP" ); - (void) strcpy( lattype + 4, "-NCP" ); - } - } - } - } - -/* Identify the celestial coordinate system from the first 4 characters of the - longitude CTYPE value. Only RA, galactic longitude, and ecliptic - longitude can be stored using FITS-IRAF. */ - if( strncmp( lontype, "RA--", 4 ) && - strncmp( lontype, "GLON", 4 ) && - strncmp( lontype, "ELON", 4 ) ) ok = 0; - -/* If the physical Frame requires a LONPOLE or LATPOLE keyword, it cannot - be encoded using FITS-IRAF. */ - if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ) - != AST__BAD || - GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ) - != AST__BAD ) ok = 0; - -/* If there are no celestial axes, the physical Frame can be written out - using FITS-IRAF. */ - } else { - ok = 1; - } - -/* Save the number of axes */ - naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1; - -/* If this is different to the value of NAXIS abort since this encoding - does not support WCSAXES keyword. */ - if( naxis != store->naxis ) ok = 0; - -/* Return if the FitsStore does not conform to IRAF encoding. */ - if( !ok ) return ret; - -/* Get and save CRPIX for all pixel axes. These are required, so return - if they are not available. */ - for( i = 0; i < naxis; i++ ){ - val = GetItem( &(store->crpix), 0, i, s, NULL, method, class, status ); - if( val == AST__BAD ) return ret; - sprintf( combuf, "Reference pixel on axis %d", i + 1 ); - SetValue( this, FormatKey( "CRPIX", i + 1, -1, s, status ), &val, AST__FLOAT, - combuf, status ); - } - -/* Get and save CRVAL for all intermediate axes. These are required, so return - if they are not available. */ - for( j = 0; j < naxis; j++ ){ - val = GetItem( &(store->crval), j, 0, s, NULL, method, class, status ); - if( val == AST__BAD ) return ret; - sprintf( combuf, "Value at ref. pixel on axis %d", j + 1 ); - SetValue( this, FormatKey( "CRVAL", j + 1, -1, s, status ), &val, AST__FLOAT, - combuf, status ); - } - -/* Get and save CTYPE for all intermediate axes. These are required, so return - if they are not available. Use the potentially modified versions saved - above for the celestial axes. */ - for( i = 0; i < naxis; i++ ){ - if( i == axlat ) { - cval = lattype; - } else if( i == axlon ) { - cval = lontype; - } else { - cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - if( !cval ) return ret; - } - if( strlen(cval) > 4 && !strcmp( cval + 4, "-TAB" ) ) return ret; - comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); - if( !comm ) { - sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); - comm = combuf; - } - SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, AST__STRING, - comm, status ); - } - -/* CD matrix (the product of the CDELT and PC matrices). */ - for( i = 0; i < naxis; i++ ){ - cdelt = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - if( cdelt == AST__BAD ) cdelt = 1.0; - for( j = 0; j < naxis; j++ ){ - val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; - val *= cdelt; - if( val != 0.0 ) { - SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val, - AST__FLOAT, "Transformation matrix element", status ); - } - } - } - -/* Get and save CUNIT for all intermediate axes. These are NOT required, so - do not return if they are not available. */ - for( i = 0; i < naxis; i++ ){ - cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); - if( cval ) { - sprintf( combuf, "Units for axis %d", i + 1 ); - SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING, - combuf, status ); - } - } - -/* Get and save RADECSYS. This is NOT required, so do not return if it is - not available. */ - cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); - if( cval ) SetValue( this, "RADECSYS", &cval, AST__STRING, - "Reference frame for RA/DEC values", status ); - -/* Reference equinox */ - val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, "EQUINOX", &val, AST__FLOAT, - "Epoch of reference equinox", status ); - -/* Date of observation */ - val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status ); - if( val != AST__BAD ) { - -/* The format used for the DATE-OBS keyword depends on the value of the - keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. - Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ - palCaldj( 99, 1, 1, &mjd99, &jj ); - if( val < mjd99 ) { - palDjcal( 0, val, iymdf, &jj ); - sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], - iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); - } else { - palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); - palDd2tf( 3, fd, sign, ihmsf ); - sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", - iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], - ihmsf[2], ihmsf[3] ); - } - -/* Now store the formatted string in the FitsChan. */ - cval = combuf; - SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, - "Date of observation", status ); - } - -/* If we get here we have succeeded. */ - ret = 1; - -/* Return zero or ret depending on whether an error has occurred. */ - return astOK ? ret : 0; -} - -static int IsMapLinear( AstMapping *smap, const double lbnd_in[], - const double ubnd_in[], int coord_out, int *status ) { -/* -* Name: -* IsMapLinear - -* Purpose: -* See if a specified Mapping output is linearly related to the -* Mapping inputs. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int IsMapLinear( AstMapping *smap, const double lbnd_in[], -* const double ubnd_in[], int coord_out, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns a flag indicating if the specified output of the supplied -* Mapping is a linear function of the Mapping inputs. A set of output -* positions are created which are evenly spaced along the specified -* output coordinate. The spacing is chosen so that the entire range -* of the output coordinate is covered in 20 steps. The other output -* coordinates are held fixed at arbitrary values (actually, values -* at which the specified output coordinate achieves its minimum value). -* This set of output positions is transformed into the corresponding -* set of input coordinates using the inverse of the supplied Mapping. -* A least squares linear fit is then made which models each input -* coordinate as a linear function of the specified output coordinate. -* The residual at every point in this fit must be less than some -* small fraction of the total range of the corresponding input -* coordinate for the Mapping to be considered linear. - -* Parameters: -* smap -* Pointer to the Mapping. -* lbnd_in -* Pointer to an array of double, with one element for each -* Mapping input coordinate. This should contain the lower bound -* of the input box in each input dimension. -* ubnd_in -* Pointer to an array of double, with one element for each -* Mapping input coordinate. This should contain the upper bound -* of the input box in each input dimension. -* coord_out -* The zero-based index of the Mapping output which is to be checked. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Non-zero if the specified Mapping output is linear. Zero otherwise. -*/ - -/* Local Constants: */ -#define NP 20 - -/* Local Variables: */ - AstMapping *map; - AstPointSet *pset1; - AstPointSet *pset2; - double **ptr1; - double **ptr2; - double *p; - double *s; - double *xl; - double c; - double d; - double delta; - double in_lbnd; - double in_ubnd; - double lbnd_out; - double m; - double p0; - double pv; - double sn; - double sp; - double sps; - double ss2; - double ss; - double sv; - double tol; - double ubnd_out; - int *ins; - int boxok; - int i; - int j; - int nin; - int nout; - int oldrep; - int ret; - -/* Initialise */ - ret = 0; - -/* Check inherited status */ - if( !astOK ) return ret; - -/* Attempt to split off the required output (in case any of the other - outputs are associated with Mappings that do not have an inverse). */ - astInvert( smap ); - ins = astMapSplit( smap, 1, &coord_out, &map ); - astInvert( smap ); - -/* If successful, check that the output is fed by only one input. */ - if( ins ) { - if( astGetNin( map ) == 1 ) { - -/* If so, invert the map so that it goes from pixel to wcs, and then - modify the supplied arguments so that they refer to the single required - axis. */ - astInvert( map ); - lbnd_in += coord_out; - ubnd_in += coord_out; - coord_out = 0; - -/* If the output was fed by more than one input, annul the split mapping - and use the supplied nmapping. */ - } else { - (void) astAnnul( map ); - map = astClone( smap ); - } - ins = astFree( ins ); - -/* If the supplied Mapping could not be split, use the supplied nmapping. */ - } else { - map = astClone( smap ); - } - -/* Check the Mapping is defined in both directions. */ - if( astGetTranForward( map ) && astGetTranInverse( map ) ) { - -/* Allocate resources. */ - nin = astGetNin( map ); - nout = astGetNout( map ); - xl = astMalloc( sizeof( double )*(size_t) nin ); - pset1 = astPointSet( NP, nin, "", status ); - ptr1 = astGetPoints( pset1 ); - pset2 = astPointSet( NP, nout, "", status ); - ptr2 = astGetPoints( pset2 ); - -/* Call astMapBox in a new error reporting context. */ - boxok = 0; - if( astOK ) { - -/* Temporarily switch off error reporting so that no report is made if - astMapBox cannot find a bounding box (which can legitimately happen with - some non-linear Mappings). */ - oldrep = astReporting( 0 ); - -/* Find the upper and lower bounds on the specified Mapping output. This also - returns the input coords of a point at which the required output has its - lowest value. */ - astMapBox( map, lbnd_in, ubnd_in, 1, coord_out, &lbnd_out, &ubnd_out, - xl, NULL ); - -/* If the box could not be found, clear the error status and pass on. */ - if( !astOK ) { - astClearStatus; - -/* If the box was found OK, flag this and check if the bounds are equal. - If so we cannot use them. In this case create new bounds. */ - } else { - boxok = 1; - if( astEQUAL( lbnd_out, ubnd_out ) ) { - m = 0.5*( lbnd_out + ubnd_out ); - if( fabs( m ) > 1.0E-15 ) { - lbnd_out = 0.9*m; - ubnd_out = 1.1*m; - } else { - lbnd_out = -1.0; - ubnd_out = 1.0; - } - } - } - -/* Re-instate error reporting. */ - astReporting( oldrep ); - } - -/* Check pointers can be used safely and a box was obtained. */ - if( astOK && boxok ) { - -/* Transform the input position returned by astMapBox using the supplied - Mapping to get the corresponding output position. Fill all unused - elements of the PointSet with AST__BAD. */ - for( i = 0; i < nin; i++ ){ - p = ptr1[ i ]; - *(p++) = xl[ i ]; - for( j = 1; j < NP; j++ ) *(p++) = AST__BAD; - } - (void) astTransform( map, pset1, 1, pset2 ); - -/* Now create a set of NP points evenly spaced in output coordinates. The - first point is at the output position found above. Each subsequent - point is incremented by a fixed amount along the specified output - coordinate (the values on all other output coordinates is held fixed). */ - delta = ( ubnd_out - lbnd_out )/ ( NP - 1 ); - for( i = 0; i < nout; i++ ){ - p = ptr2[ i ]; - if( i == coord_out ) { - for( j = 0; j < NP; j++ ) *(p++) = lbnd_out + j*delta; - } else { - p0 = p[ 0 ]; - for( j = 0; j < NP; j++ ) *(p++) = p0; - } - } - -/* Transform these output positions into input positions using the - inverse Mapping. */ - (void) astTransform( map, pset2, 0, pset1 ); - -/* Do a least squares fit to each input coordinate. Each fit gives the - corresponding input coordinate value as a linear function of the - specified output coordinate value. Note, linear function should never - produce bad values so abort if a bad value is found. */ - ret = 1; - s = ptr2[ coord_out ]; - for( i = 0; i < nin; i++ ) { - p = ptr1[ i ]; - -/* Form the required sums. Also find the largest and smallest input - coordinate value achieved. */ - sp = 0.0; - ss = 0.0; - sps = 0.0; - sn = 0.0; - ss2 = 0.0; - in_lbnd = DBL_MAX; - in_ubnd = DBL_MIN; - for( j = 0; j < NP; j++ ) { - sv = s[ j ]; - pv = p[ j ]; - if( pv != AST__BAD && sv != AST__BAD ) { - sp += pv; - ss += sv; - sps += pv*sv; - sn += 1.0; - ss2 += sv*sv; - if( pv < in_lbnd ) in_lbnd = pv; - if( pv > in_ubnd ) in_ubnd = pv; - } else { - sn = 0.0; - break; - } - } - -/* Ignore input axes which are independant of the output axis. */ - if( !astEQUAL( in_lbnd, in_ubnd ) ) { - -/* Calculate the constants "input coord = m*output coord + c" */ - d = ss*ss - sn*ss2; - if( sn > 0.0 && d != 0.0 ) { - m = ( sp*ss - sps*sn )/d; - c = ( sps*ss - sp*ss2 )/d; - -/* Subtract off the fit value form the "p" values to get the residuals of - the fit. */ - for( j = 0; j < NP; j++ ) p[ j ] -= m*s[ j ] + c; - -/* We now do a least squares fit to the residuals. This second fit is done - because the first least squares fit sometimes leaves the residuals with a - distinct non-zero gradient. We do not need to worry about bad values - here since we have checked above that there are no bad values. Also we - do not need to recalculate sums which only depend on the "s" values since - they have not changed. */ - sp = 0.0; - sps = 0.0; - for( j = 0; j < NP; j++ ) { - pv = p[ j ]; - sp += pv; - sps += pv*s[ j ]; - } - -/* Find the constants in "input residual = m*output coord + c" equation. */ - m = ( sp*ss - sps*sn )/d; - c = ( sps*ss - sp*ss2 )/d; - -/* Subtract off the fit value form the "p residuals" values to get the - residual redisuals of the fit. */ - for( j = 0; j < NP; j++ ) p[ j ] -= m*s[ j ] + c; - -/* The requirement for a linear relationship is that the absolute residual - between the input coord produced by the above linear fit and the input - coord produced by the actual Mapping should be less than some small - fraction of the total range of input coord value, at every point. Test - this. */ - tol = 1.0E-7*( in_ubnd - in_lbnd ); - for( j = 0; j < NP; j++ ) { - if( fabs( p[ j ] ) > tol ) { - ret = 0; - break; - } - } - } else { - ret = 0; - } - } - if( !ret ) break; - } - } - -/* Free resources. */ - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - xl = astFree( xl ); - } - map = astAnnul( map ); - -/* Return the answer. */ - return ret; -} - -static AstMapping *IsMapTab1D( AstMapping *map, double scale, const char *unit, - AstFrame *wcsfrm, double *dim, int iax, - int iwcs, AstFitsTable **table, int *icolmain, - int *icolindex, int *interp, int *status ){ -/* -* Name: -* IsMapTab1D - -* Purpose: -* See if a specified Mapping output is related to a single Mapping input -* via a FITS -TAB algorithm. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *IsMapTab1D( AstMapping *map, double scale, const char *unit, -* AstFrame *wcsfrm, double *dim, int iax, -* int iwcs, AstFitsTable **table, int *icolmain, -* int *icolindex, int *interp, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A specified axis of the supplied Mapping is tested to see if it -* can be represented by the -TAB alogirithm described in FITS-WCS -* paper III. If the test is passed, a Mapping is returned from the -* specified WCS axis to the corresponding psi axis. A FitsTable is -* also created holding the information to be stored in the -* corresponding FITS binary table. -* -* Note, when creating a -TAB header, AST uses grid coords for the psi -* axis. See FITS-WCS paper III section 6.1.2 for a definition of the -* psi axes. - -* Parameters: -* map -* Pointer to the Mapping from pixel coords to WCS coords. -* scale -* A scale factor by which to multiply the axis values stored in the -* returned FitsTable. Note, the returned Mapping is unaffected by -* this scaling factor. -* unit -* Pointer to the unit string to store with the coords column. If -* NULL, the unit string is extracted form the supplied WCS Frame. -* wcsfrm -* Pointer to a Frame describing WCS coords. -* dim -* An array holding the array dimensions in pixels. AST__BAD should -* be supplied for any unknown dimensions. -* iax -* The zero-based index of the Mapping output which is to be checked. -* iwcs -* The zero-based index of the corresponding FITS WCS axis. -* table -* Pointer to a location holding a pointer to the FitsTable describing -* the -TAB look-up table. If "*table" is NULL on entry, a new -* FitsTable will be created and returned, otherwise the supplied -* FitsTable is used. -* icolmain -* The one-based index of the column within "*table" that holds the -* main data array. -* icolindex -* The one-based index of the column within "*table" that holds the -* index vector. Returned equal to -1 if no index is added to the -* table (i.e. if the index is a unt index). -* interp -* The interpolation method (0=linear, other=nearest neighbour). -* status -* Pointer to the inherited status variable. - -* Returned Value: -* If the specified "map" output can be described using the -TAB -* algorithm of FITS-WCS paper III, then a 1-input/1-output Mapping -* from the specified WCS axis to the corresponding psi axis (which is -* assumed to be equal to grid coords) is returned. NULL is returned -* otherwise, of if an error occurs. -*/ - -/* Local Variables: */ - AstCmpMap *cm; /* CmpMap pointer */ - AstMapping **map_list; /* Mapping array pointer */ - AstMapping *postmap; /* Total Mapping after LutMap */ - AstMapping *premap; /* Total Mapping before LutMap */ - AstMapping *ret; /* Returned WCS axis Mapping */ - AstMapping *tmap; /* Temporary Mapping */ - AstPermMap *pm; /* PermMap pointer */ - char cellname[ 20 ]; /* Buffer for cell name */ - char colname[ 20 ]; /* Buffer for column name */ - double *lut; /* Pointer to table of Y values */ - double *work1; /* Pointer to work array */ - double *work2; /* Pointer to work array */ - double inc; /* X increment between table entries */ - double start; /* X value at first table entry */ - double v[ 2 ]; /* Y values at start and end of interval */ - double x[ 2 ]; /* X values at start and end of interval */ - int *ins; /* Array of "map" input indices */ - int *invert_list; /* Invert array pointer */ - int *outs; /* Array of "map" output indices */ - int dims[ 2 ]; /* Dimensions of the tab coords array */ - int iin; /* Index of Mapping input */ - int ilut; /* Index of the LutMap within the mappings list */ - int imap; /* Index of current Mapping in list */ - int iout; /* Index of Mapping output */ - int jout; /* Index of Mapping output */ - int nin; /* Number of Mapping inputs */ - int nlut; /* Number of elements in "lut" array */ - int nmap; /* Number of Mappings in the list */ - int nout; /* Number of Mapping outputs */ - int ok; /* Were columns added to the table? */ - int old_invert; /* Original value for Mapping's Invert flag */ - int outperm; /* Index of input that feeds the single output */ - -/* Initialise */ - ret = NULL; - *icolmain = -1; - *icolindex = -1; - *interp = 0; - -/* Check inherited status */ - if( !astOK ) return ret; - -/* Ensure we have aunit string. */ - if( !unit ) unit = astGetUnit( wcsfrm, iax ); - -/* Check that the requested mapping output is fed by only one mapping - input, identify that input, and extract the input->output mapping from - the total mapping. Since astMapSplit splits off a specified input, we - need to invert the Mapping first so we can split off a specified output. */ - astInvert( map ); - ins = astMapSplit( map, 1, &iax, &ret ); - astInvert( map ); - -/* If the Mapping could not be split, try a different approach in which - each input is checked in turn to see if it feeds the specified output. */ - if( !ins ) { - -/* Loop round each input of "map". */ - nin = astGetNin( map ); - for( iin = 0; iin < nin && !ins; iin++ ) { - -/* Attempt to find a group of outputs (of "map") that are fed by just - this one input. */ - outs = astMapSplit( map, 1, &iin, &ret ); - -/* If successful, "ret" will be a Mapping with one input corresponding to - input "iin" of "map, and one or more outputs. We loop round these - outputs to see if any of them correspond to output "iax" of "map". */ - if( outs ) { - nout = astGetNout( ret ); - for( iout = 0; iout < nout; iout++ ) { - if( outs[ iout ] == iax ) break; - } - -/* Did input "iin" feed the output "iax" (and possibly other outputs)? */ - if( iout < nout ) { - -/* The "ret" Mapping is now a 1-input (pixel) N-output (WCS) Mapping in which - output "iout" corresponds to output "iax" of Mapping. To be compatible - with the previous approach, we want "ret" to be a 1-input (WCS) to - 1-output (pixel) Mapping in which the input corresponds to output - "iax" of Mapping. To get "ret" into this form, we first append a PermMap - to "ret" that selects a single output ("iout"), and then invert the - whole CmpMap. */ - for( jout = 0; jout < nout; jout++ ) { - outs[ jout ] = -1; - } - outs[ iout ] = 0; - outperm = iout; - - pm = astPermMap( nout, outs, 1, &outperm, NULL, "", status ); - cm = astCmpMap( ret, pm, 1, " ", status ); - (void) astAnnul( ret ); - pm = astAnnul( pm ); - ret = (AstMapping *) cm; - astInvert( ret ); - -/* The earlier approach leves ins[ 0 ] holding the index of the input to - "map" that feeds output iax. Ensure we have this too. */ - ins = outs; - ins[ 0 ] = iin; - -/* Free resources if the current input did not feed the required output. */ - } else { - outs = astFree( outs ); - ret = astAnnul( ret ); - } - } - } - } - -/* If the Mapping still could not be split, try again on a copy of the - Mapping in which all PermMaps provide an alternative implementation of - the astMapSplit method. */ - if( !ins ) { - astInvert( map ); - tmap = astCopy( map ); - ChangePermSplit( tmap, status ); - ins = astMapSplit( tmap, 1, &iax, &ret ); - tmap = astAnnul( tmap ); - astInvert( map ); - } - -/* Assume the Mapping cannot be represented by -TAB */ - ok = 0; - -/* Check a Mapping was returned by astMapSplit. If so, it will be the - mapping from the requested output of "map" (the WCS axis) to the - corresponding input(s) (grid axes). Check only one "map" input feeds the - requested output. */ - if( ins && ret && astGetNout( ret ) == 1 ) { - -/* Invert the Mapping so that the input is grid coord and the output is - WCS coord. */ - astInvert( ret ); - -/* We now search the "ret" mapping for a non-inverted LutMap, splitting ret - up into three serial components: 1) the mappings before the LutMap, 2) the - LutMap itself, and 3) the mappings following the LutMap. First, decompose - the mapping into a list of series mappings. */ - map_list = NULL; - invert_list = NULL; - nmap = 0; - astMapList( ret, 1, astGetInvert( ret ), &nmap, &map_list, - &invert_list ); - -/* Search the list for a non-inverted LutMap. */ - ilut = -1; - for( imap = 0; imap < nmap; imap++ ) { - if( astIsALutMap( map_list[ imap ] ) && !(invert_list[ imap ]) ) { - ilut = imap; - break; - } - } - -/* If a LutMap was found, combine all Mappings before the LutMap into a - single Mapping. Remember to set the Mapping Invert flags temporarily to - the values used within the CmpMap. */ - if( ilut >= 0 ) { - premap = (AstMapping *) astUnitMap( 1, " ", status ); - for( imap = 0; imap < ilut; imap++ ) { - old_invert = astGetInvert( map_list[ imap ] ); - astSetInvert( map_list[ imap ], invert_list[ imap ] ); - tmap = (AstMapping *) astCmpMap( premap, map_list[ imap ], 1, - " ", status ); - astSetInvert( map_list[ imap ], old_invert ); - (void) astAnnul( premap ); - premap = tmap; - } - -/* Also combine all Mappings after the LutMap into a single Mapping, setting - the Mapping Invert flags temporarily to the values used within the - CmpMap. */ - postmap = (AstMapping *) astUnitMap( 1, " ", status ); - for( imap = ilut + 1; imap < nmap; imap++ ) { - old_invert = astGetInvert( map_list[ imap ] ); - astSetInvert( map_list[ imap ], invert_list[ imap ] ); - tmap = (AstMapping *) astCmpMap( postmap, map_list[ imap ], 1, - " ", status ); - astSetInvert( map_list[ imap ], old_invert ); - (void) astAnnul( postmap ); - postmap = tmap; - } - -/* Get the table of values, and other attributes, from the LutMap. */ - lut = astGetLutMapInfo( map_list[ ilut ], &start, &inc, &nlut ); - *interp = astGetLutInterp( map_list[ ilut ] ); - -/* If required, create a FitsTable to hold the returned table info. */ - if( ! *table ) *table = astFitsTable( NULL, "", status ); - ok = 1; - -/* Define the properties of the column in the FitsTable that holds the main - coordinate array. Points on a WCS axis are described by a single value - (wavelength, frequency, or whatever), but the coords array has to be - 2-dimensional, with an initial degenerate axis, as required by FITS-WCS - paper III. */ - dims[ 0 ] = 1; - dims[ 1 ] = nlut; - sprintf( colname, "COORDS%d", iwcs + 1 ); - astAddColumn( *table, colname, AST__DOUBLETYPE, 2, dims, unit ); - -/* Get the one-based index of the column just added to the table. */ - *icolmain = astGetNcolumn( *table ); - -/* Get workspace. */ - work1 = astMalloc( nlut*sizeof( double ) ); - if( astOK ) { - -/* Transform the LutMap table values using the post-lutmap mapping to - get the list of WCS values in AST units. */ - astTran1( postmap, nlut, lut, 1, work1 ); - -/* Convert them to FITS units (e.g. celestial axis values should be - converted from radians to degrees). */ - for( ilut = 0; ilut < nlut; ilut++ ) work1[ ilut ] *= scale; - -/* Store them in row 1, column COORDS, in the FitsTable. */ - sprintf( cellname, "COORDS%d(1)", iwcs + 1 ); - astMapPut1D( *table, cellname, nlut, work1, NULL ); - -/* Create an array holding the LutMap input value at the centre of each - table entry. Re-use the "lut" array since we no longer need it. */ - for( ilut = 0; ilut < nlut; ilut++ ) { - lut[ ilut ] = start + ilut*inc; - } - -/* Transform this array using the inverted pre-lutmap mapping to get the - list of grid coord. */ - astTran1( premap, nlut, lut, 0, work1 ); - -/* Test this list to see if they form a unit index (i.e. index(i) == i+1 ). - (not the "+1" is due to the fact that "i" is zero based). */ - for( ilut = 0; ilut < nlut; ilut++ ) { - if( fabs( work1[ ilut ] - ilut - 1.0 ) > 1.0E-6 ) break; - } - -/* if it is not a unit index, we add the index to the table. */ - if( ilut < nlut ) { - -/* Define the properties of the column in the FitsTable that holds the - indexing vector. */ - sprintf( colname, "INDEX%d", iwcs + 1 ); - astAddColumn( *table, colname, AST__DOUBLETYPE, 1, &nlut, " " ); - -/* Get the one-based index of the column just added to the table. */ - *icolindex = astGetNcolumn( *table ); - -/* Store the values in the column. */ - sprintf( cellname, "INDEX%d(1)", iwcs + 1 ); - astMapPut1D( *table, cellname, nlut, work1, NULL ); - } - } - -/* Free resources. */ - work1 = astFree( work1 ); - lut = astFree( lut ); - premap = astAnnul( premap ); - postmap = astAnnul( postmap ); - -/* If no LutMap was found in the Mapping, then we can create a FitsTable - by sampling the full WCS Mapping at selected input (i.e. grid) - positions. But we can only do this if we know the number of pixels - along the WCS axis. */ - } else if( dim[ ins[ 0 ] ] != AST__BAD ) { - -/* Create two works array each holding a single value. The first holds - the grid coords at which the samples are taken. The second holds the - WCS coords at the sampled positions. These arrays are expanded as - required within function AdaptLut. */ - work1 = astMalloc( sizeof( double ) ); - work2 = astMalloc( sizeof( double ) ); - if( astOK ) { - -/* Get the WCS values at the centres of the first and last pixel on - the WCS axis. */ - x[ 0 ] = 1.0; - x[ 1 ] = dim[ ins[ 0 ] ]; - astTran1( ret, 2, x, 1, v ); - -/* Put the lower limit into the work arrays. */ - work1[ 0 ] = x[ 0 ]; - work2[ 0 ] = v[ 0 ]; - nlut = 1; - -/* Expand the arrays by sampling the WCS axis adaptively so that - more samples occur where the WCS value is changing most rapidly. - We require the maximum error introduced by the table to be 0.25 pixels. */ - AdaptLut( ret, 3, 0.25, x[ 0 ], x[ 1 ], v[ 0 ], v[ 1 ], - &work1, &work2, &nlut, status ); - -/* Create a FitsTable to hold the returned table info. */ - if( ! *table ) *table = astFitsTable( NULL, "", status ); - ok = 1; - -/* Define the properties of the column in the FitsTable that holds the main - coordinate array. */ - sprintf( colname, "COORDS%d", iwcs + 1 ); - dims[ 0 ] = 1; - dims[ 1 ] = nlut; - astAddColumn( *table, colname, AST__DOUBLETYPE, 2, dims, unit ); - *icolmain = astGetNcolumn( *table ); - -/* Convert the axis values to FITS units (e.g. celestial axis values should be - converted from radians to degrees). */ - for( ilut = 0; ilut < nlut; ilut++ ) work2[ ilut ] *= scale; - -/* Store the scaled axis values in row 1 of the column. */ - sprintf( cellname, "COORDS%d(1)", iwcs + 1 ); - astMapPut1D( *table, cellname, nlut, work2, NULL ); - -/* Test the index vector to see if they form a unit index (i.e. index(i) == - i+1 ). If not the "+1" is due to the fact that "i" is zero based). If not, store - them as the index vector in the FitsTable. */ - for( ilut = 0; ilut < nlut; ilut++ ) { - if( fabs( work1[ ilut ] - ilut - 1.0 ) > 1.0E-6 ) break; - } - -/* If the index vector is not a unit index, define the properties of the - column in the FitsTable that holds the indexing vector. Then store values - in row 1 of the column. */ - if( ilut < nlut ) { - sprintf( colname, "INDEX%d", iwcs + 1 ); - astAddColumn( *table, colname, AST__DOUBLETYPE, 1, &nlut, " " ); - *icolindex = astGetNcolumn( *table ); - sprintf( cellname, "INDEX%d(1)", iwcs + 1 ); - astMapPut1D( *table, cellname, nlut, work1, NULL ); - } - } - -/* Free resources */ - work1 = astFree( work1 ); - work2 = astFree( work2 ); - } - -/* If columns were added to the table, invert the returned Mapping again - so that the input is wcs coord and the output is grid coord. Otherwise, - annul the returned Mapping. */ - if( ok ) { - astInvert( ret ); - } else { - ret = astAnnul( ret ); - } - -/* Loop to annul all the Mapping pointers in the list. */ - for ( imap = 0; imap < nmap; imap++ ) map_list[ imap ] = astAnnul( map_list[ imap ] ); - -/* Free the dynamic arrays. */ - map_list = astFree( map_list ); - invert_list = astFree( invert_list ); - } - -/* Free resources. */ - ins = astFree( ins ); - -/* If an error occurred, free the returned Mapping. */ - if( !astOK ) ret = astAnnul( ret ); - -/* Return the result. */ - return ret; -} - -static AstMapping *IsMapTab2D( AstMapping *map, double scale, const char *unit, - AstFrame *wcsfrm, double *dim, int iax1, - int iax2, int iwcs1, int iwcs2, - AstFitsTable **table, int *icolmain1, - int *icolmain2, int *icolindex1, - int *icolindex2, int *max1, int *max2, - int *interp1, int *interp2, int *status ){ -/* -* Name: -* IsMapTab2D - -* Purpose: -* See if a specified pair of Mapping outputs are related to a pair of -* Mapping inputs via a FITS -TAB algorithm. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *IsMapTab2D( AstMapping *map, double scale, const char *unit, -* AstFrame *wcsfrm, double *dim, int iax1, -* int iax2, int iwcs1, int iwcs2, -* AstFitsTable **table, int *icolmain1, -* int *icolmain2, int *icolindex1, -* int *icolindex2, int *max1, int *max2, -* int *interp1, int *interp2, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A specified pair of outputs axes of the supplied Mapping are tested -* to see if they can be represented by the -TAB alogirithm described in -* FITS-WCS paper III. If the test is passed, a Mapping is returned from -* the specified WCS axes to the corresponding psi axes. A FitsTable is -* also created holding the information to be stored in the corresponding -* FITS binary table. Note, when creating a header, AST assumes a unit -* transformaton between psi axes and grid axes (psi axes are defined -* in FITS-WCS paper III section 6.1.2). - -* Parameters: -* map -* Pointer to the Mapping from pixel coords to WCS coords. -* scale -* A scale factor by which to multiply the axis values stored in the -* returned FitsTable. Note, the returned Mapping is unaffected by -* this scaling factor. -* unit -* A unit string for the axis values. If supplied, the same -* string is stored for both axes. If NULL, the unit strings are -* extracted from the relavent axes of the supplied WCS Frame. -* wcsfrm -* Pointer to a Frame describing WCS coords. -* dim -* An array holding the array dimensions in pixels. AST__BAD should -* be supplied for any unknown dimensions. -* iax1 -* The zero-based index of the first Mapping output which is to be -* checked. -* iax2 -* The zero-based index of the second Mapping output which is to be -* checked. -* iwcs1 -* The zero-based index of the FITS WCS axis corresponding to "iax1". -* iwcs2 -* The zero-based index of the FITS WCS axis corresponding to "iax2". -* table -* Pointer to a location holding a pointer to the FitsTable describing -* the -TAB look-up table. If "*table" is NULL on entry, a new -* FitsTable will be created and returned, otherwise the supplied -* FitsTable is used. -* icolmain1 -* The one-based index of the column within "*table" that holds the -* main coord array for the first Mapping output. -* icolmain2 -* The one-based index of the column within "*table" that holds the -* main coord array for the second Mapping output. -* icolindex1 -* The one-based index of the column within "*table" that holds the -* index vector for the first Mapping output. Returned equal to -1 -* if no index is added to the table (e.g. because the index is a -* unit index). -* icolindex2 -* The one-based index of the column within "*table" that holds the -* index vector for the second Mapping output. Returned equal to -1 -* if no index is added to the table (e.g. because the index is a -* unit index). -* max1 -* The one-based index of the dimension describing the first Mapping -* output within the main coord array specified by "icolmain1". -* max2 -* The one-based index of the dimension describing the second Mapping -* output within the main coord array specified by "icolmain1". -* interp1 -* The interpolation method (0=linear, other=nearest neighbour) for -* the first mapping output. -* interp2 -* The interpolation method (0=linear, other=nearest neighbour) for -* the second mapping output. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* If the specified "map" outputs can be described using the -TAB -* algorithm of FITS-WCS paper III, then a 2-input/2-output Mapping -* from the specified WCS axes to the corresponding psi axes (i.e. -* grid axes) is returned. NULL is returned otherwise, of if an error -* occurs. -*/ - -/* Local Variables: */ - AstMapping *ret1; /* WCS->IWC Mapping for first output */ - AstMapping *ret2; /* WCS->IWC Mapping for second output */ - AstMapping *ret; /* Returned WCS axis Mapping */ - AstMapping *tmap; - AstPermMap *pm; - int *pix_axes; /* Zero-based indices of corresponding pixel axes */ - int wcs_axes[ 2 ]; /* Zero-based indices of selected WCS axes */ - int inperm[ 1 ]; - int outperm[ 2 ]; - -/* Initialise */ - ret = NULL; - -/* Check inherited status */ - if( !astOK ) return ret; - -/* First see if the two required Mapping outputs are separable, in which case - they can be described by two 1D tables. */ - ret1 = IsMapTab1D( map, scale, unit, wcsfrm, dim, iax1, iwcs1, table, icolmain1, - icolindex1, interp1, status ); - ret2 = IsMapTab1D( map, scale, unit, wcsfrm, dim, iax2, iwcs2, table, icolmain2, - icolindex2, interp2, status ); - -/* If both outputs are seperable... */ - if( ret1 && ret2 ) { - -/* Both axes are stored as the first dimension in the corresponding main - coords array. */ - *max1 = 1; - *max2 = 1; - -/* Get a Mapping from the required pair of WCS axes to the corresponding - pair of grid axes. First try to split the supplied grid->wcs mapping. */ - wcs_axes[ 0 ] = iax1; - wcs_axes[ 1 ] = iax2; - - astInvert( map ); - pix_axes = astMapSplit( map, 2, wcs_axes, &ret ); - astInvert( map ); - - if( pix_axes ) { - pix_axes = astFree( pix_axes ); - if( astGetNout( ret ) > 2 ) { - ret = astAnnul( ret ); - -/* If the two output WCS axes are fed by the same grid axis, we need to - add another pixel axis to form the pair. */ - } else if( astGetNout( ret ) == 1 ) { - inperm[ 0 ] = 0; - outperm[ 0 ] = 0; - outperm[ 1 ] = 0; - pm = astPermMap( 1, inperm, 2, outperm, NULL, " ", status ); - tmap = (AstMapping *) astCmpMap( ret, pm, 1, " ", status ); - ret = astAnnul( ret ); - pm = astAnnul( pm ); - ret = tmap; - } - } - -/* If this was unsuccessful, combine the Mappings returned by IsMapTab1D. - We only do this if the above astMapSplit call failed, since the IsMapTab1D - mappings may well not be independent of each other, and we may end up - sticking together in parallel two mappings that are basically the same - except for ending with PermMapa that select different axes. Is is hard - then to simplify such a parallel CmpMap back into the simpler form - that uses only one of the two identical mappings, without a PermMap. */ - if( !ret ) { - ret = (AstMapping *) astCmpMap( ret1, ret2, 0, " ", status ); - } - -/* Free resources. */ - ret1 = astAnnul( ret1 ); - ret2 = astAnnul( ret2 ); - -/* If only one output is separable, remove the corresponding columns from - the returned table. */ - } else if( ret1 ) { - ret1 = astAnnul( ret1 ); - astRemoveColumn( *table, astColumnName( *table, *icolmain1 ) ); - if( icolindex1 >= 0 ) astRemoveColumn( *table, astColumnName( *table, *icolindex1 ) ); - } else if( ret2 ) { - ret2 = astAnnul( ret2 ); - astRemoveColumn( *table, astColumnName( *table, *icolmain2 ) ); - if( icolindex1 >= 0 ) astRemoveColumn( *table, astColumnName( *table, *icolindex2 ) ); - } - -/* If the required Mapping outputs were not separable, create a single - 2D coords array describing both outputs. */ - if( !ret ) { - -/* TO BE DONE... Until then non-separable Mappings will result in a - failure to create a -TAB header. No point in doing this until AST has - an N-dimensional LutMap class (otherwise AST could never read the - resulting FITS header). */ - } - -/* If an error occurred, free the returned Mapping. */ - if( !astOK ) ret = astAnnul( ret ); - -/* Return the result. */ - return ret; -} - -static int IsAIPSSpectral( const char *ctype, char **wctype, char **wspecsys, int *status ){ -/* -* Name: -* IsAIPSSpectral - -* Purpose: -* See if a given CTYPE value describes a FITS-AIPS spectral axis. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int IsAIPSSpectral( const char *ctype, char **wctype, char **wspecsys, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The given CTYPE value is checked to see if it conforms to the -* requirements of a spectral axis CTYPE value as specified by -* FITS-AIPS encoding. If so, the equivalent FITS-WCS CTYPE and -* SPECSYS values are returned. - -* Parameters: -* ctype -* Pointer to a null terminated string holding the CTYPE value to -* check. -* wctype -* The address of a location at which to return a pointer to a -* static string holding the corresponding FITS-WCS CTYPE value. A -* NULL pointer is returned if the supplied CTYPE string is not an -* AIPS spectral CTYPE value. -* wspecsys -* The address of a location at which to return a pointer to a -* static string holding the corresponding FITS-WCS SPECSYS value. A -* NULL pointer is returned if the supplied CTYPE string is not an -* AIPS spectral CTYPE value. -* status -* Pointer to the inherited status variable. - -* Retuned Value: -* Non-zero fi the supplied CTYPE was an AIPS spectral CTYPE value. - -* Note: -* - These translations are also used by the FITS-CLASS encoding. -*/ - -/* Local Variables: */ - int ret; - -/* Initialise */ - ret = 0; - *wctype = NULL; - *wspecsys = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* If the used length of the string is not 8, then it is not an AIPS spectral axis. */ - if( astChrLen( ctype ) == 8 ) { - -/* Translate AIPS spectral CTYPE values to FITS-WCS paper III equivalents. - These are of the form AAAA-BBB, where "AAAA" can be "FREQ", "VELO" (=VRAD!) - or "FELO" (=VOPT-F2W), and BBB can be "LSR", "LSD", "HEL" (=*Bary*centric!) - or "GEO". */ - if( !strncmp( ctype, "FREQ", 4 ) ){ - *wctype = "FREQ "; - } else if( !strncmp( ctype, "VELO", 4 ) ){ - *wctype = "VRAD "; - } else if( !strncmp( ctype, "FELO", 4 ) ){ - *wctype = "VOPT-F2W"; - } else if( !strncmp( ctype, "WAVELENG", 8 ) ){ - *wctype = "WAVE "; - } - if( !strncmp( ctype + 4, "-LSR", 4 ) ){ - *wspecsys = "LSRK"; - } else if( !strncmp( ctype + 4, "LSRK", 4 ) ){ - *wspecsys = "LSRK"; - } else if( !strncmp( ctype + 4, "-LSD", 4 ) ){ - *wspecsys = "LSRD"; - } else if( !strncmp( ctype + 4, "-HEL", 4 ) ){ - *wspecsys = "BARYCENT"; - } else if( !strncmp( ctype + 4, "-EAR", 4 ) || !strncmp( ctype + 4, "-GEO", 4 ) ){ - *wspecsys = "GEOCENTR"; - } else if( !strncmp( ctype + 4, "-OBS", 4 ) || !strncmp( ctype + 4, "-TOP", 4 ) ){ - *wspecsys = "TOPOCENT"; - } - if( *wctype && *wspecsys ) { - ret = 1; - } else { - *wctype = NULL; - *wspecsys = NULL; - } - } - -/* Return the result. */ - return ret; -} - -static int IsSkyOff( AstFrameSet *fset, int iframe, int *status ){ -/* -* Name: -* IsSkyOff - -* Purpose: -* See if a given Frame contains an offset SkyFrame. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int IsSkyOff( AstFrameSet *fset, int iframe, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns a flag indicating if the specified Frame within the -* supplied FrameSet is, or contains, a SkyFrame that represents -* offset coordinates. This is the case if the Frame is a SkyFrame -* and its SkyRefIs attribute is "Pole" or "Origin", or is a CmpFrame -* containing such a SkyFrame. - -* Parameters: -* fset -* The FrameSet. -* iframe -* Index of the Frame to check within "fset" -* status -* Pointer to the inherited status variable. - -* Retuned Value: -* +1 if the Frame is an offset SkyFrame. Zero otherwise. - -* Notes: -* - Zero is returned if an error has already occurred. -*/ - -/* Local Variables: */ - AstFrame *frm; - const char *skyrefis; - int oldrep; - int result; - -/* Initialise. */ - result = 0; - -/* Check the inherited status. */ - if( !astOK ) return result; - -/* Get a pointer to the required Frame in the FrameSet */ - frm = astGetFrame( fset, iframe ); - -/* Since the current Frame may not contain a SkyFrame, we temporarily - switch off error reporting. */ - oldrep = astReporting( 0 ); - -/* Get the SkyRefIs attribute value. */ - skyrefis = astGetC( frm, "SkyRefIs" ); - -/* If it is "Pole" or "Origin", return 1. */ - if( skyrefis && ( !Ustrcmp( skyrefis, "POLE", status ) || - !Ustrcmp( skyrefis, "ORIGIN", status ) ) ) result = 1; - -/* Cancel any error and switch error reporting back on again. */ - astClearStatus; - astReporting( oldrep ); - -/* Annul the Frame pointer. */ - frm = astAnnul( frm ); - -/* Return the result. */ - return result; -} - -static const char *IsSpectral( const char *ctype, char stype[5], char algcode[5], int *status ) { -/* -* Name: -* IsSpectral - -* Purpose: -* See if a given FITS-WCS CTYPE value describes a spectral axis. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* char *IsSpectral( const char *ctype, char stype[5], char algcode[5], int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The given CTYPE value is checked to see if it conforms to the -* requirements of a spectral axis CTYPE value as specified by -* FITS-WCS paper 3. If so, the spectral system and algorithm codes -* are extracted from it and returned, together with the default units -* for the spectral system. - -* Parameters: -* ctype -* Pointer to a null terminated string holding the CTYPE value to -* check. -* stype -* An array in which to return the null-terminated spectral system type -* (e.g. "FREQ", "VELO", "WAVE", etc). A null string is returned if -* the CTYPE value does not describe a spectral axis. -* algcode -* An array in which to return the null-terminated algorithm code -* (e.g. "-LOG", "", "-F2W", etc). A null string is returned if the -* spectral axis is linear. A null string is returned if the CTYPE -* value does not describe a spectral axis. -* status -* Pointer to the inherited status variable. - -* Retuned Value: -* A point to a static string holding the default units associated -* with the spectral system specified by the supplied CTYPE value. -* NULL is returned if the CTYPE value does not describe a spectral -* axis. - -* Notes: -* - The axis is considered to be a spectral axis if the first 4 -* characters form one of the spectral system codes listed in FITS-WCS -* paper 3. The algorithm code is not checked, except to ensure that -* it begins with a minus sign, or is blank. -* - A NULL pointer is returned if an error has already occurred. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS - int ctype_len; - -/* Initialise */ - stype[ 0 ] = 0; - algcode[ 0 ] = 0; - -/* Check the inherited status. */ - if( !astOK ) return NULL; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(NULL); - -/* Initialise more stuff */ - isspectral_ret = NULL; - -/* If the length of the string is less than 4, then it is not a spectral - axis. */ - ctype_len = strlen( ctype ); - if( ctype_len >= 4 ) { - -/* Copy the first 4 characters (the coordinate system described by the - axis) into a null-terminated buffer. */ - strncpy( stype, ctype, 4 ); - stype[ 4 ] = 0; - stype[ astChrLen( stype ) ] = 0; - -/* Copy any remaining characters (the algorithm code) into a null-terminated - buffer. Only copy a maximum of 4 characters. */ - if( ctype_len > 4 ) { - if( ctype_len <= 8 ) { - strcpy( algcode, ctype + 4 ); - } else { - strncpy( algcode, ctype + 4, 4 ); - algcode[ 4 ] = 0; - } - algcode[ astChrLen( algcode ) ] = 0; - } else { - algcode[ 0 ] = 0; - } - -/* See if the first 4 characters of the CTYPE value form one of the legal - spectral coordinate type codes listed in FITS-WCS Paper III. Also note - the default units associated with the system. */ - if( !strcmp( stype, "FREQ" ) ) { - isspectral_ret = "Hz"; - } else if( !strcmp( stype, "ENER" ) ) { - isspectral_ret = "J"; - } else if( !strcmp( stype, "WAVN" ) ) { - isspectral_ret = "/m"; - } else if( !strcmp( stype, "VRAD" ) ) { - isspectral_ret = "m/s"; - } else if( !strcmp( stype, "WAVE" ) ) { - isspectral_ret = "m"; - } else if( !strcmp( stype, "VOPT" ) ) { - isspectral_ret = "m/s"; - } else if( !strcmp( stype, "ZOPT" ) ) { - isspectral_ret = ""; - } else if( !strcmp( stype, "AWAV" ) ) { - isspectral_ret = "m"; - } else if( !strcmp( stype, "VELO" ) ) { - isspectral_ret = "m/s"; - } else if( !strcmp( stype, "BETA" ) ) { - isspectral_ret = ""; - } - -/* Also check that the remaining part of CTYPE (the algorithm code) begins - with a minus sign or is blank. */ - if( algcode[ 0 ] != '-' && strlen( algcode ) > 0 ) isspectral_ret = NULL; - } - -/* Return null strings if the axis is not a spectral axis. */ - if( ! isspectral_ret ) { - stype[ 0 ] = 0; - algcode[ 0 ] = 0; - } - -/* Return the result. */ - return isspectral_ret; -} - -static AstMapping *LinearWcs( FitsStore *store, int i, char s, - const char *method, const char *class, int *status ) { -/* -* Name: -* LinearWcs - -* Purpose: -* Create a Mapping describing a FITS-WCS linear algorithm - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *LinearWcs( FitsStore *store, int i, char s, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function uses the contents of the supplied FitsStore to create -* a Mapping which goes from Intermediate World Coordinate (known as "w" -* in the context of FITS-WCS paper III) to a linearly related axis. -* -* The returned Mapping is a ShiftMap which simply adds on the value of -* CRVALi. - -* Parameters: -* store -* Pointer to the FitsStore structure holding the values to use for -* the WCS keywords. -* i -* The zero-based index of the spectral axis within the FITS header -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a Mapping, or NULL if an error occurs. -*/ - -/* Local Variables: */ - AstMapping *ret; - double crv; - -/* Check the global status. */ - ret = NULL; - if( !astOK ) return ret; - -/* Get the CRVAL value for the specified axis. */ - crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( crv == AST__BAD ) crv = 0.0; - -/* Create a 1D ShiftMap which adds this value onto the IWCS value. */ - if( crv != 0.0 ) { - ret = (AstMapping *) astShiftMap( 1, &crv, "", status ); - } else { - ret = (AstMapping *) astUnitMap( 1, "", status ); - } - return ret; -} - -static AstMapping *LogAxis( AstMapping *map, int iax, int nwcs, double *lbnd_p, - double *ubnd_p, double crval, int *status ){ -/* -* Name: -* LogAxes - -* Purpose: -* Test a Frame axis to see if it logarithmically spaced in pixel coords. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *LogAxis( AstMapping *map, int iax, int nwcs, double *lbnd_p, -* double *ubnd_p, double crval ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A specified axis of the supplied Mappinhg is tested to see if it -* corresponds to the form -* -* S = Sr.exp( w/Sr ) -* -* where "w" is one of the Mapping inputs, "S" is the specified -* Mapping output, and "Sr" is the supplied value of "crval". This -* is the form for a FITS log axis as defined in FITS-WCS paper III. -* -* If the above test is passed, a Mapping is returned from "S" to "w" -* (the inverseof the above expression). - -* Parameters: -* map -* Pointer to the Mapping. This will usually be a Mapping from -* pixel coords to WCS coords. -* iax -* The index of the output of "map" which correspoinds to "S". -* nwcs -* The number of outputs from "map". -* lbnd_p -* Pointer to an array of double, with one element for each -* Mapping input coordinate. This should contain the lower bound -* of the input pixel box in each input dimension. -* ubnd_p -* Pointer to an array of double, with one element for each -* Mapping input coordinate. This should contain the upper bound -* of the input pixel box in each input dimension. -* crval -* The reference value ("Sr") to use. Must not be zero. - -* Returned Value: -* If the specified axis is logarithmically spaced, a Mapping with -* "nwcs" inputs and "nwcs" outputs is returned. This Mapping transforms - -* its "iax"th input using the transformation: -* -* w = Sr.Log( S/Sr ) -* -* (where "S" is the Mapping is the "iax"th input and "w" is the -* "iax"th output). Other inputs are copied to the corresponding -* output without change. NULL is returned if the specified axis is -* not logarithmically spaced. -*/ - -/* Local Variables: */ - AstMapping *result; /* Returned Mapping */ - AstMapping *tmap0; /* A temporary Mapping */ - AstMapping *tmap1; /* A temporary Mapping */ - AstMapping *tmap2; /* A temporary Mapping */ - AstMapping *tmap3; /* A temporary Mapping */ - AstMapping *tmap4; /* A temporary Mapping */ - const char *fexps[ 1 ]; /* Forward MathMap expressions */ - const char *iexps[ 1 ]; /* Inverse MathMap expressions */ - -/* Initialise */ - result = NULL; - -/* Check the inherited status and crval value. */ - if( !astOK || crval == 0.0 ) return result; - -/* If the "log" algorithm is appropriate, the supplied axis (s) is related - to pixel coordinate (p) by s = Sr.EXP( a*p - b ). If this is the case, - then the log of s will be linearly related to pixel coordinates. To test - this, we create a CmpMap which produces log(s). */ - fexps[ 0 ] = "logs=log(s)"; - iexps[ 0 ] = "s=exp(logs)"; - tmap1 = (AstMapping *) astMathMap( 1, 1, 1, fexps, 1, iexps, - "simpfi=1,simpif=1", status ); - tmap2 = AddUnitMaps( tmap1, iax, nwcs, status ); - tmap0 = (AstMapping *) astCmpMap( map, tmap2, 1, "", status ); - tmap2 = astAnnul( tmap2 ); - -/* See if this Mapping is linear. */ - if( IsMapLinear( tmap0, lbnd_p, ubnd_p, iax, status ) ) { - -/* Create the Mapping which defines the IWC axis. This is the Mapping from - WCS to IWCS - "W = Sr.log( S/Sr )". Other axes are left unchanged by the - Mapping. The IWC axis has the same axis index as the WCS axis. */ - tmap2 = (AstMapping *) astZoomMap( 1, 1.0/crval, "", status ); - tmap3 = (AstMapping *) astCmpMap( tmap2, tmap1, 1, "", status ); - tmap2 = astAnnul( tmap2 ); - tmap2 = (AstMapping *) astZoomMap( 1, crval, "", status ); - tmap4 = (AstMapping *) astCmpMap( tmap3, tmap2, 1, "", status ); - tmap3 = astAnnul( tmap3 ); - tmap2 = astAnnul( tmap2 ); - result = AddUnitMaps( tmap4, iax, nwcs, status ); - tmap4 = astAnnul( tmap4 ); - } - -/* Free resources. */ - tmap0 = astAnnul( tmap0 ); - tmap1 = astAnnul( tmap1 ); - -/* Return the result. */ - return result; -} - -static AstMapping *LogWcs( FitsStore *store, int i, char s, - const char *method, const char *class, int *status ) { -/* -* Name: -* LogWcs - -* Purpose: -* Create a Mapping describing a FITS-WCS logarithmic algorithm - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *LogWcs( FitsStore *store, int i, char s, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function uses the contents of the supplied FitsStore to create -* a Mapping which goes from Intermediate World Coordinate (known as "w" -* in the context of FITS-WCS paper III) to a logarthmic version of w - -* called "S" given by: -* -* S = Sr.exp( w/Sr ) -* -* where Sr is the value of S corresponding to w=0. - -* Parameters: -* store -* Pointer to the FitsStore structure holding the values to use for -* the WCS keywords. -* i -* The zero-based index of the axis within the FITS header -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a Mapping, or NULL if an error occurs. -*/ - -/* Local Variables: */ - AstMapping *ret; - char forexp[ 12 + AST__DBL_DIG*2 ]; - char invexp[ 12 + AST__DBL_DIG*2 ]; - const char *fexps[ 1 ]; - const char *iexps[ 1 ]; - double crv; - -/* Check the global status. */ - ret = NULL; - if( !astOK ) return ret; - -/* Get the CRVAL value for the specified axis. Use a default of zero. */ - crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( crv == AST__BAD ) crv = 0.0; - -/* Create the MathMap, if possible. */ - if( crv != 0.0 ) { - sprintf( forexp, "s=%.*g*exp(w/%.*g)", AST__DBL_DIG, crv, AST__DBL_DIG, crv ); - sprintf( invexp, "w=%.*g*log(s/%.*g)", AST__DBL_DIG, crv, AST__DBL_DIG, crv ); - fexps[ 0 ] = forexp; - iexps[ 0 ] = invexp; - ret = (AstMapping *) astMathMap( 1, 1, 1, fexps, 1, iexps, "simpfi=1,simpif=1", status ); - } - -/* Return the result */ - return ret; -} - -static int LooksLikeClass( AstFitsChan *this, const char *method, - const char *class, int *status ){ - -/* -* Name: -* LooksLikeClass - -* Purpose: -* Does the FitsChan seem to use FITS-CLASS encoding? - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int LooksLikeClass( AstFitsChan *this, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns non-zero if the supplied FitsChan probably uses FITS-CLASS -* encoding. This is the case if it contains a DELTAV keyword and a -* keyword of the form VELO-xxx", where xxx is one of the accepted -* standards of rest, or "VLSR". - -* Parameters: -* this -* Pointer to the FitsChan. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Non-zero if the encoding in use lookslike FITS-CLASS. -*/ - -/* Local Variables... */ - int ret; /* Returned value */ - -/* Initialise */ - ret = 0; - -/* Check the global status. */ - if( !astOK ) return ret; - -/* See if there is a "DELTAV" card, and a "VELO-xxx" or "VLSR" card. */ - if( astKeyFields( this, "DELTAV", 0, NULL, NULL ) && ( - astKeyFields( this, "VLSR", 0, NULL, NULL ) || - astKeyFields( this, "VELO-OBS", 0, NULL, NULL ) || - astKeyFields( this, "VELO-HEL", 0, NULL, NULL ) || - astKeyFields( this, "VELO-EAR", 0, NULL, NULL ) || - astKeyFields( this, "VELO-LSR", 0, NULL, NULL ) ) ) { - ret = 1; - } - -/* Return the result. */ - return ret; -} - -static void MakeBanner( const char *prefix, const char *middle, - const char *suffix, - char banner[ AST__FITSCHAN_FITSCARDLEN - - FITSNAMLEN + 1 ], int *status ) { -/* -* Name: -* MakeBanner - -* Purpose: -* Create a string containing a banner comment. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void MakeBanner( const char *prefix, const char *middle, -* const char *suffix, -* char banner[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ], int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function creates a string which can be written as a FITS -* comment card to produce a banner heading (or tail) for an AST -* Object when it is written to a FitsChan. The banner will occupy -* the maximum permitted width for text in a FITS comment card. - -* Parameters: -* prefix -* A pointer to a constant null-terminated string containing the -* first part of the text to appear in the banner. -* middle -* A pointer to a constant null-terminated string containing the -* second part of the text to appear in the banner. -* suffix -* A pointer to a constant null-terminated string containing the -* third part of the text to appear in the banner. -* banner -* A character array to receive the null-terminated result string. -* status -* Pointer to the inherited status variable. - -* Notes: -* - The text to appear in the banner is constructed by -* concatenating the three input strings supplied. -*/ - -/* Local Variables: */ - char token[] = "AST"; /* Identifying token */ - int i; /* Loop counter for input characters */ - int len; /* Number of output characters */ - int ltok; /* Length of token string */ - int mxlen; /* Maximum permitted output characters */ - int start; /* Column number where text starts */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Calculate the maximum number of characters that the output banner - can hold and the length of the token string. */ - mxlen = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN; - ltok = (int) strlen( token ); - -/* Calculate the column in which to start the text, so that it is - centred in the banner (with 4 non-text characters on each side). */ - start = ltok + 2 + ( mxlen - ltok - 1 - - (int) ( strlen( prefix ) + - strlen( middle ) + - strlen( suffix ) ) - 1 - ltok ) / 2; - if ( start < ltok + 2 ) start = ltok + 2; - -/* Start building the banner with the token string. */ - len = 0; - for ( i = 0; token[ i ] && ( len < mxlen ); i++ ) { - banner[ len++ ] = token[ i ]; - } - -/* Then pad with spaces up to the start of the text. */ - while ( len < start - 1 ) banner[ len++ ] = ' '; - -/* Insert the prefix data, truncating it if it is too long. */ - for ( i = 0; prefix[ i ] && ( len < mxlen - ltok - 1 ); i++ ) { - banner[ len++ ] = prefix[ i ]; - } - -/* Insert the middle data, truncating it if it is too long. */ - for ( i = 0; middle[ i ] && ( len < mxlen - ltok - 1 ); i++ ) { - banner[ len++ ] = middle[ i ]; - } - -/* Insert the suffix data, truncating it if it is too long. */ - for ( i = 0; suffix[ i ] && ( len < mxlen - ltok - 1 ); i++ ) { - banner[ len++ ] = suffix[ i ]; - } - -/* Pad the end of the text with spaces. */ - while ( len < mxlen - ltok ) banner[ len++ ] = ' '; - -/* Finish the banner with the token string. */ - for ( i = 0; token[ i ] && ( len < mxlen ); i++ ) { - banner[ len++ ] = token[ i ]; - } - -/* Terminate the output string. */ - banner[ len ] = '\0'; -} - -static AstMapping *MakeColumnMap( AstFitsTable *table, const char *col, - int isindex, int interp, const char *method, - const char *class, int *status ){ -/* -* Name: -* MakeColumnMap - -* Purpose: -* Create a Mapping describing a look-up table supplied in a cell of a -* FITS binary table. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *MakeColumnMap( AstFitsTable *table, const char *col, -* int isindex, int interp, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns a Mapping representing the array of values -* stored in row 1 of a named column of a supplied FitsTable. The -* array of values is treated as a look-up table following the prescription -* of FITS-WCS paper III (the "-TAB" algorithm). If the array has (N+1) -* dimensions (where N is one or more), the returned Mapping has N -* inputs and N outputs. The inputs correspond to FITS GRID coords -* within the array. FITS-WCS paper III requires that the first dimension -* in the array has a length of "N" and contains the N output values -* at each input values. - -* Parameters: -* table -* Pointer to the Fitstable. -* col -* A string holding the name of the column to use. -* isindex -* Non-zero if the column hold an index array, zero if it holds a -* coordinate array. -* interp -* The value to use for the Interp attribute of the LutMap. A value -* of zero tells the LutMap class to use linear interpolation. Other -* values tell the LutMap class to use nearest neighbour interpolation. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the Mapping, or NULL if an error occurs. -*/ - -/* Local Variables: */ - AstMapping *result; - char *key; - double *lut; - int *dims; - int ndim; - int nel; - -/* Initialise */ - result = NULL; - -/* Check the inherited status */ - if( !astOK ) return result; - -/* Get the number of dimensions spanned by the value in the named column. */ - ndim = astGetColumnNdim( table, col ); - -/* First deal with index vectors. */ - if( isindex ) { - -/* FITS-WCS paper II mandates that index arrays must be 1-dimensional. */ - if( ndim != 1 && astOK ) { - astError( AST__BADTAB, "%s(%s): Column '%s' has %d dimensions but it " - "holds an index vector and should therefore be 1-dimensional.", - status, method, class, col, ndim ); - } - -/* Get the length of the index vector. */ - nel = astGetColumnLength( table, col ); - -/* Allocate memory to hold the array values, and to hold the cell key. */ - lut = astMalloc( nel*sizeof( double ) ); - key = astMalloc( strlen( col ) + 5 ); - if( astOK ) { - -/* Create the key for the table cell holding the required array. FITS-WCS - paper III mandates that tables always occur in the first row of the - table (and that the table only has one row). Ignore trailing spaces in - the column name. */ - sprintf( key, "%.*s(1)", (int) astChrLen( col ), col ); - -/* Copy the array values into the above memory. */ - if( astMapGet1D( table, key, nel, &nel, lut ) ) { - -/* Create a 1D LutMap. FITS-WCS paper III (sec 6.1.2) mandates that the input - corresponds to FITS grid coord (i.e. 1.0 at the centre of the first entry). - Ensure the LutMap uses linear interpolation. */ - result = (AstMapping *) astLutMap( nel, lut, 1.0, 1.0, - "LutInterp=%d", status, interp ); - -/* Report an error if the table cell was empty. */ - } else if( astOK ) { - astError( AST__BADTAB, "%s(%s): Row 1 of the binary table " - "contains no value for column '%s'.", status, method, - class, col ); - } - } - -/* Free memory. */ - lut = astFree( lut ); - key = astFree( key ); - -/* Now deal with coordinate arrays. */ - } else { - -/* Get the shape of the array. */ - dims = astMalloc( sizeof( int )*ndim ); - astColumnShape( table, col, ndim, &ndim, dims ); - -/* For coordinate arrays, check the length of the first axis is "ndim-1", as - required by FITS-WCS paper III. */ - if( astOK && dims[ 0 ] != ndim - 1 && !isindex ) { - astError( AST__BADTAB, "%s(%s): The first dimension of the coordinate " - "array has length %d (should be %d since the array has %d " - "dimensions).", status, method, class, dims[ 0 ], ndim - 1, - ndim ); - } - -/* We can currently only handle 1D look-up tables. These are stored in - notionally two-dimensional arrays in which the first dimension is - degenarate (i.e. spans only a single element). */ - if( ndim > 2 ) { - if( astOK ) astError( AST__INTER, "%s(%s): AST can currently only " - "handle 1-dimensional coordinate look-up tables " - "(the supplied table has %d dimensions).", status, - method, class, ndim - 1 ); - -/* Handle 1-dimensional look-up tables. */ - } else if( astOK ){ - -/* Allocate memory to hold the array values, and to hold the cell key. */ - lut = astMalloc( dims[ 1 ]*sizeof( double ) ); - key = astMalloc( strlen( col ) + 5 ); - if( astOK ) { - -/* Create the key for the table cell holding the required array. FITS-WCS - paper III mandates that tables always occur in the first row of the - table (and that the table only has one row). Ignore trailing spaces in - the column name. */ - sprintf( key, "%.*s(1)", (int) astChrLen( col ), col ); - -/* Copy the array values into the above memory. */ - if( astMapGet1D( table, key, dims[ 1 ], dims, lut ) ) { - -/* Create a 1D LutMap. FITS-WCS paper III (sec 6.1.2) mandates that the input - corresponds to FITS grid coord (i.e. 1.0 at the centre of the first entry). - Ensure the LutMap uses linear interpolation. */ - result = (AstMapping *) astLutMap( dims[ 1 ], lut, 1.0, 1.0, - "LutInterp=%d", status, - interp ); - -/* Report an error if the table cell was empty. */ - } else if( astOK ) { - astError( AST__BADTAB, "%s(%s): Row 1 of the binary table " - "contains no value for column '%s'.", status, method, - class, col ); - } - } - -/* Free memory. */ - lut = astFree( lut ); - key = astFree( key ); - } - dims = astFree( dims ); - } - -/* Issue a context message and annul the returned Mapping if an error - has occurred. */ - if( !astOK ) { - astError( astStatus, "%s(%s): Cannot read a look-up table for a " - "tabular WCS axis from column '%s' of a FITS binary table.", - status, method, class, col ); - result = astAnnul( result ); - } - -/* Return the result. */ - return result; -} - -static AstFrameSet *MakeFitsFrameSet( AstFitsChan *this, AstFrameSet *fset, - int ipix, int iwcs, int encoding, - const char *method, const char *class, - int *status ) { -/* -* Name: -* MakeFitsFrameSet - -* Purpose: -* Create a FrameSet which conforms to the requirements of the FITS-WCS -* papers. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstFrameSet *MakeFitsFrameSet( AstFitsChan *this, AstFrameSet *fset, -* int ipix, int iwcs, int encoding, -* const char *method, const char *class, -* int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function constructs a new FrameSet holding the pixel and WCS -* Frames from the supplied FrameSet, but optionally extends the WCS -* Frame to include any extra axes needed to conform to the FITS model. - -* Currently, this function does the following: -* -* - if the WCS Frame contains a 1D spectral Frame with a defined celestial -* reference position (SpecFrame attributes RefRA and RefDec), then -* it ensures that the WCS Frame also contains a pair of celestial -* axes (such axes are added if they do not already exist within the -* supplied WCS Frame). The pixel->WCS Mapping is adjusted accordingly. -* -* - if the WCS Frame contains a spectral axis and a pair of celestial -* axes, then the SpecFrame attributes RefRA and RefDec are set to the -* reference position defined by the celestial axes. The pixel->WCS -* Mapping is adjusted accordingly. -* -* - NULL is returned if the WCS Frame contains more than one spectral -* axis. -* -* - NULL is returned if the WCS Frame contains more than one pair of -* celestial axes. -* -* - Any isolated sky axes (i.e. not contained within a SkyFrame) are -* re-mapped from radians into degrees. - -* Parameters: -* this -* The FitsChan. -* fset -* The FrameSet to check. -* ipix -* The index of the FITS pixel Frame within "fset". -* iwcs -* The index of the WCS Frame within "fset". -* encoding -* The encoding in use. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A new FrameSet which confoms to the requirements of the FITS-WCS -* papers. The base Frame in this FrameSet will be the FITS pixel -* Frame, and the current Frame will be the WCS Frame. NULL is -* returned if an error has already occurred, or if the FrameSet cannot -* be produced for any reason. -*/ - -/* Local Variables: */ - AstFitsChan *fc; /* Pointer to temporary FitsChan */ - AstFrame *pframe; /* Pointer to the primary Frame */ - AstFrame *pixfrm; /* Pointer to the FITS pixel Frame */ - AstFrame *tfrm0; /* Pointer to a temporary Frame */ - AstFrame *tfrm; /* Pointer to a temporary Frame */ - AstFrame *wcsfrm; /* Pointer to the FITS WCS Frame */ - AstFrameSet *ret; /* The returned FrameSet */ - AstFrameSet *tfs; /* Pointer to a temporary FrameSet */ - AstMapping *map1; /* Pointer to pre-WcsMap Mapping */ - AstMapping *map3; /* Pointer to post-WcsMap Mapping */ - AstMapping *map; /* Pointer to the pixel->wcs Mapping */ - AstMapping *remap; /* Total Mapping from internal to external units */ - AstMapping *smap; /* Simplified Mapping */ - AstMapping *tmap0; /* Pointer to a temporary Mapping */ - AstMapping *tmap1; /* Pointer to a temporary Mapping */ - AstMapping *tmap2; /* Pointer to a temporary Mapping */ - AstMapping *tmap; /* Pointer to a temporary Mapping */ - AstMapping *umap; /* 1D Mapping from internal to external units */ - AstSpecFrame *skyfrm; /* Pointer to the SkyFrame within WCS Frame */ - AstSpecFrame *specfrm; /* Pointer to the SpecFrame within WCS Frame */ - AstWcsMap *map2; /* Pointer to WcsMap */ - char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* A FITS header card */ - char equinox_attr[ 13 ];/* Name of Equinox attribute for sky axes */ - char system_attr[ 12 ]; /* Name of System attribute for sky axes */ - const char *eqn; /* Pointer to original sky Equinox value */ - const char *extunit; /* External units string */ - const char *intunit; /* Internal units string */ - const char *skysys; /* Pointer to original sky System value */ - double con; /* Constant axis value */ - double reflat; /* Celestial latitude at reference point */ - double reflon; /* Celestial longitude at reference point */ - int *perm; /* Pointer to axis permutation array */ - int iax; /* Axis inex */ - int icurr; /* Index of original current Frame in returned FrameSet */ - int ilat; /* Celestial latitude index within WCS Frame */ - int ilon; /* Celestial longitude index within WCS Frame */ - int npix; /* Number of pixel axes */ - int nwcs; /* Number of WCS axes */ - int ok; /* Is the supplied FrameSet usable? */ - int paxis; /* Axis index within the primary Frame */ - int rep; /* Was error reporting switched on? */ - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Get copies of the pixel Frame, the WCS Frame and the Mapping. */ - tfrm = astGetFrame( fset, ipix ); - pixfrm = astCopy( tfrm ); - tfrm = astAnnul( tfrm ); - tfrm = astGetFrame( fset, iwcs ); - wcsfrm = astCopy( tfrm ); - tfrm = astAnnul( tfrm ); - tmap = astGetMapping( fset, ipix, iwcs ); - map = astCopy( tmap ); - tmap = astAnnul( tmap ); - -/* Store the number of pixel and WCS axes. */ - npix = astGetNaxes( pixfrm ); - nwcs = astGetNaxes( wcsfrm ); - -/* Search the WCS Frame for SkyFrames and SpecFrames. */ - umap = NULL; - remap = NULL; - specfrm = NULL; - skyfrm = NULL; - ok = 1; - ilat = -1; - ilon = -1; - for( iax = 0; iax < nwcs; iax++ ) { - -/* Obtain a pointer to the primary Frame containing the current WCS axis. */ - astPrimaryFrame( wcsfrm, iax, &pframe, &paxis ); - -/* If the current axis is a SpecFrame, save a pointer to it. If we have already - found a SpecFrame, abort. */ - if( astIsASpecFrame( pframe ) ) { - if( specfrm ) { - ok = 0; - break; - } - specfrm = astClone( pframe ); - -/* If the current axis is a SkyFrame, save a pointer to it, and its WCS - index. If we have already found a different SkyFrame, abort. */ - } else if( IsASkyFrame( pframe ) ) { - if( skyfrm ) { - if( pframe != (AstFrame *) skyfrm ) { - ok = 0; - break; - } - } else { - skyfrm = astClone( pframe ); - } - if( paxis == 0 ) { - ilon = iax; - } else { - ilat = iax; - } - -/* If the internal and external units differ, attempt to remap the axis - into its external units. */ - } else { - -/* Get the string describing the external units (the "Unit" attribute). */ - extunit = astGetUnit( pframe, paxis ); - -/* Get the string describing the internal units (the "InternalUnit" - attribute). */ - intunit = astGetInternalUnit( pframe, paxis ); - -/* If they are the same, we do not need to modify this axis. */ - if( astOK && strcmp( extunit, intunit ) ){ - -/* Otherwise, get the mapping from the internal units to the external - units, if possible. Ignore any error reported by unitmapper. */ - rep = astReporting( 0 ); - umap = astUnitMapper( intunit, extunit, NULL, NULL ); - if( !astOK ) astClearStatus; - astReporting( rep ); - - if( !umap ) { - -/* If the above failed, ensure that the external units are the same as - the internal units (except that internal radians are converted to - external degrees). */ - if( !strcmp( intunit, "rad" ) ) { - umap = (AstMapping *) astZoomMap( 1, AST__DR2D, " ", status ); - extunit = "deg"; - } else { - extunit = intunit; - } - - astSetUnit( wcsfrm, iax, extunit ); - } - } - } - -/* If no change is needed for the mapping for this axis, use a UnitMap. */ - if( !umap ) umap = (AstMapping *) astUnitMap( 1, " ", status ); - -/* Extend the parallel CmpMap to encompass the current axis. */ - if( remap ) { - tmap = (AstMapping *) astCmpMap( remap, umap, 0, " ", status ); - (void) astAnnul( remap ); - remap = tmap; - } else { - remap = astClone( umap ); - } - -/* Free resources. */ - umap = astAnnul( umap ); - pframe = astAnnul( pframe ); - } - -/* See if the pixel->wcs mapping needs to be modified to take account of - any changes to axis units. */ - smap = astSimplify( remap ); - if( ! astIsAUnitMap( smap ) ) { - tmap = (AstMapping *) astCmpMap( map, remap, 1, " ", status ); - (void) astAnnul( map ); - map = tmap; - } - remap = astAnnul( remap ); - smap = astAnnul( smap ); - -/* If the supplied FrameSet is usable... */ - if( ok ) { - -/* If we did not find a SpecFrame, return a FrameSet made from the base - and current Frames in the supplied FrameSet. */ - if( !specfrm ) { - ret = astFrameSet( pixfrm, "", status ); - astAddFrame( ret, AST__BASE, map, wcsfrm ); - -/* If we have a SpecFrame, proceed. */ - } else { - -/* Check that both the RefRA and RefDec attributes of the SpecFrame are set. - If not, return a FrameSet made from the base and current Frames in the - supplied FrameSet. Also do this if the original WCS Frame contains 3 - or more axes (since it is almost always inappropriate to add extra sky - axes in such circumestances). But if the other axes form a skyfram, - then we need to make sure they use the right refrence point. */ - if( !astTestRefRA( specfrm ) || !astTestRefDec( specfrm ) || - ( nwcs > 2 && !skyfrm ) ) { - ret = astFrameSet( pixfrm, "", status ); - astAddFrame( ret, AST__BASE, map, wcsfrm ); - -/* If we have a celestial reference position for the spectral axis, ensure - it is described correctly by a pair of celestial axes. */ - } else { - -/* If the WCS Frame does not contain any celestial axes, we add some now. */ - if( !skyfrm ) { - -/* The easiest way to create the required mapping from pixel to celestial - to create a simple FITS header and read it in via a FitsChan to create a - FrameSet. */ - fc = astFitsChan( NULL, NULL, "", status ); - astPutFits( fc, "CRPIX1 = 0", 0 ); - astPutFits( fc, "CRPIX2 = 0", 0 ); - astPutFits( fc, "CDELT1 = 0.0003", 0 ); - astPutFits( fc, "CDELT2 = 0.0003", 0 ); - astPutFits( fc, "CTYPE1 = 'RA---TAN'", 0 ); - astPutFits( fc, "CTYPE2 = 'DEC--TAN'", 0 ); - astPutFits( fc, "RADESYS = 'FK5'", 0 ); - astPutFits( fc, "EQUINOX = 2000.0", 0 ); - sprintf( card, "CRVAL1 = %.*g", AST__DBL_DIG, - AST__DR2D*astGetRefRA( specfrm ) ); - astPutFits( fc, card, 0 ); - sprintf( card, "CRVAL2 = %.*g", AST__DBL_DIG, - AST__DR2D*astGetRefDec( specfrm ) ); - astPutFits( fc, card, 0 ); - sprintf( card, "MJD-OBS = %.*g", AST__DBL_DIG, - TDBConv( astGetEpoch( specfrm ), AST__UTC, 1, - "astWrite", "FitsChan", status ) ); - astPutFits( fc, card, 0 ); - astClearCard( fc ); - tfs = astRead( fc ); - if( tfs ) { - -/* Create the new pixel->wcs Mapping. First get the 2-input,2-output - Mapping between pixel and sky coords from the above FrameSet. Then add - this Mapping in parallel with the original pixel->wcs Mapping. */ - tmap0 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); - tmap1 = (AstMapping *) astCmpMap( map, tmap0, 0, "", status ); - tmap0 = astAnnul( tmap0 ); - -/* We now have a (npix+2)-input,(nwcs+2)-output Mapping. We now add a - PermMap in series with this which feeds the constant value 0.0 (the - CRPIX value in the above set of FITS headers) into the 2 pixel axes - corresponding to RA and Dec. This PermMap has npix-inputs and (npix+2) - outputs. The total Mapping then has npix inputs and (nwcs+2) outputs. */ - perm = astMalloc( sizeof( int )*(size_t) ( npix + 2 ) ); - if( astOK ) { - for( iax = 0; iax < npix; iax++ ) perm[ iax ] = iax; - perm[ npix ] = -1; - perm[ npix + 1 ] = -1; - con = 0.0; - tmap0 = (AstMapping *) astPermMap( npix, perm, npix + 2, perm, &con, "", status ); - tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, "", status ); - tmap0 = astAnnul( tmap0 ); - tmap1 = astAnnul( tmap1 ); - -/* We now create the new WCS Frame with the extra RA and Dec axes. This - is just a CmpFrame made up of the original WCS Frame and the new - SkyFrame. */ - tfrm = astGetFrame( tfs, AST__CURRENT ); - tfrm0 = (AstFrame *) astCmpFrame( wcsfrm, tfrm, "", status ); - tfrm = astAnnul( tfrm ); - -/* Construct the returned FrameSet. */ - ret = astFrameSet( pixfrm, "", status ); - astAddFrame( ret, AST__BASE, tmap2, tfrm0 ); - tmap2 = astAnnul( tmap2 ); - tfrm0 = astAnnul( tfrm0 ); - -/* Free remaining resources. */ - perm = astFree( perm ); - } - tfs = astAnnul( tfs ); - } - fc = astAnnul( fc ); - -/* If the WCS Frame does contain celestial axes we make sure that the - SpecFrame uses the same reference point. */ - } else { - -/* The returned FrameSet has no extra Frames (although some attributes - may be changed) so just create a new FrameSet equaivalent to the supplied - FrameSet. */ - tfs = astFrameSet( pixfrm, "", status ); - astAddFrame( tfs, AST__BASE, map, wcsfrm ); - -/* The RefRA and RefDec attributes of the SpecFrame must be set in FK5 - J2000. Therefore we need to know the celestial reference point in - FK5 J2000. Modify the SkyFrame within the FrameSet to represent FK5 - J2000, noting the original sky system and equinox first so that they - can be re-instated (if set) later on. */ - sprintf( system_attr, "System(%d)", ilon + 1 ); - if( astTest( tfs, system_attr ) ) { - skysys = astGetC( tfs, system_attr ); - } else { - skysys = NULL; - } - astSetC( tfs, system_attr, "FK5" ); - sprintf( equinox_attr, "Equinox(%d)", ilon + 1 ); - if( astTest( tfs, equinox_attr ) ) { - eqn = astGetC( tfs, equinox_attr ); - } else { - eqn = NULL; - } - astSetC( tfs, equinox_attr, "J2000" ); - -/* The reference point for the celestial axes is defined by the WcsMap - contained within the Mapping. Split the mapping up into a list of serial - component mappings, and locate the first WcsMap in this list. The first - Mapping returned by this call is the result of compounding all the - Mappings up to (but not including) the WcsMap, the second returned Mapping - is the (inverted) WcsMap, and the third returned Mapping is anything - following the WcsMap. Only proceed if one and only one WcsMap is found. */ - tmap0 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); - if( SplitMap( tmap0, astGetInvert( tmap0 ), ilon, ilat, &map1, &map2, &map3, status ) ){ - -/* The reference point in the celestial coordinate system is found by - transforming the fiducial point in native spherical co-ordinates - into absolute physical coordinates using map3. */ - if( GetFiducialWCS( map2, map3, ilon, ilat, &reflon, &reflat, status ) ){ - -/* Use reflon and reflat (which represent FK5 J2000 RA and Dec) to set - the values of the SpecFrame RefRA and RefDec attributes. Format the - values first so that we can use the FrameSet astSetC method, and so - maintain the FrameSet integrity. Use "tfs" rather than "wcsfrm" when - calling astFormat, as "wcsfrm" is not affected by the above change - to the current frame of "tfs" (i.e. astAddFrame takes a deep copy of the - supplied Frame). */ - astSetC( tfs, "RefRA", astFormat( tfs, ilon, reflon ) ); - astSetC( tfs, "RefDec", astFormat( tfs, ilat, reflat ) ); - -/* If succesfull, return a pointer to the FrameSet. */ - if( astOK ) ret = astClone( tfs ); - } - -/* Release resources. */ - map1 = astAnnul( map1 ); - map2 = astAnnul( map2 ); - map3 = astAnnul( map3 ); - -/* If no WcsMap was found, the celestial axes have no reference point and - so we can retain the original spectral reference point, so just return - the temporary FrameSet. */ - } else if( astOK ) { - ret = astClone( tfs ); - } - tmap0 = astAnnul( tmap0 ); - -/* Re-instate the original sky system and equinox. */ - if( skysys ) astSetC( tfs, system_attr, skysys ); - if( eqn ) astSetC( tfs, equinox_attr, eqn ); - -/* Release resources. */ - tfs = astAnnul( tfs ); - } - } - } - } - -/* Add a new current Frame into the FrameSet which increases the chances of - the requested encoding being usable. The index of the original current - Frame is returned, or AST__NOFRAME if no new Frame was added. */ - icurr = AddEncodingFrame( this, ret, encoding, method, class, status ); - -/* If a new Frame was added, remove the original current Frame. */ - if( icurr != AST__NOFRAME ) astRemoveFrame( ret, icurr ); - -/* Free resources. */ - if( specfrm ) specfrm = astAnnul( specfrm ); - if( skyfrm ) skyfrm = astAnnul( skyfrm ); - pixfrm = astAnnul( pixfrm ); - wcsfrm = astAnnul( wcsfrm ); - map = astAnnul( map ); - -/* Return NULL if an error has occurred. */ - if( !astOK && ret ) ret = astAnnul( ret ); - -/* Return the result. */ - return ret; -} - -static void MakeIndentedComment( int indent, char token, - const char *comment, const char *data, - char string[ AST__FITSCHAN_FITSCARDLEN - - FITSNAMLEN + 1 ], int *status ) { -/* -* Name: -* MakeIndentedComment - -* Purpose: -* Create a comment string containing an indentation bar. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void MakeIndentedComment( int indent, char token, -* const char *comment, const char *data, -* char string[ AST__FITSCHAN_FITSCARDLEN - -* FITSNAMLEN + 1 ], int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function creates a string that may be used as text in a -* FITS comment card. The string contains a textual comment -* preceded by a bar (a line of characters) whose length can be -* used to indicate a level of indentation (in the absence of any -* way of indenting FITS keywords). - -* Parameters: -* indent -* The level of indentation, in characters. -* token -* The character used to form the indentation bar. -* comment -* A pointer to a constant null-terminated string containing the text -* of the comment to be included. -* data -* A pointer to a constant null-terminated string containing any -* textual data to be appended to the comment. -* string -* A character array to receive the output string. -* status -* Pointer to the inherited status variable. - -* Notes: -* - The comment text that appears in the output string is formed by -* concatenating the "comment" and "data" strings. -*/ - -/* Local Variables: */ - int i; /* Loop counter for input characters */ - int len; /* Number of output characters */ - int mxlen; /* Maximum length of output string */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Calculate the maximum number of characters that the output string - can accommodate. */ - mxlen = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN; - -/* Start the string with "indent" copies of the token character, but - without exceeding the output string length. */ - len = 0; - while ( ( len < indent ) && ( len < mxlen ) ) string[ len++ ] = token; - -/* Pad with spaces up to the start of the comment, if necessary. */ - while ( len < ( FITSCOMCOL - FITSNAMLEN - 1 ) ) { - string[ len++ ] = ' '; - } - -/* Add "/ " to introduce the comment (strictly not necessary as the - whole card will be a comment, but it matches the other non-comment - cards). Truncate if necessary. */ - for ( i = 0; ( i < 2 ) && ( len < mxlen ); i++ ) { - string[ len++ ] = "/ "[ i ]; - } - -/* Append the comment string, truncating it if it is too long. */ - for ( i = 0; comment[ i ] && ( len < mxlen ); i++ ) { - string[ len++ ] = comment[ i ]; - } - -/* Append the data string, again truncating if too long. */ - for ( i = 0; data[ i ] && ( len < mxlen ); i++ ) { - string[ len++ ] = data[ i ]; - } - -/* Terminate the output string. */ - string[ len ] = '\0'; -} - -static void MakeIntoComment( AstFitsChan *this, const char *method, - const char *class, int *status ){ - -/* -* Name: -* MakeIntoComment - -* Purpose: -* Convert a card into a FITS COMMENT card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void MakeIntoComment( AstFitsChan *this, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function formats the card stored just prior to the current card, -* and re-stores it as a COMMENT card. It is used (when writing an Object -* to a FitsChan) to output values that are not "set" and which are -* therefore provided for information only, and should not be read back. -* the COMMENT card has the effect of "commenting out" the value. - -* Parameters: -* this -* Pointer to the FitsChan. -* method -* Calling method. -* class -* Object class. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Character buffer for FITS card data */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Move the current card backwards by one card. */ - MoveCard( this, -1, method, class, status ); - -/* Format the new current card. */ - FormatCard( this, card, method, status ); - -/* Write the resulting string to the FitsChan as the contents of a COMMENT - card, overwriting the existing card. The current card is incremented - by this call so that it refers to the same card as on entry. */ - astSetFitsCom( this, "COMMENT", card, 1 ); -} - -static int MakeIntWorld( AstMapping *cmap, AstFrame *fr, int *wperm, char s, - FitsStore *store, double *dim, double fitstol, - int sipok, const char *method, const char *class, - int *status ){ -/* -* Name: -* MakeIntWorld - -* Purpose: -* Create FITS header values which map grid into intermediate world -* coords. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int MakeIntWorld( AstMapping *cmap, AstFrame *fr, int *wperm, char s, -* FitsStore *store, double *dim, double fitstol, -* int sipok, const char *method, const char *class, -* int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function adds values to the supplied FitsStore which describe -* the transformation from grid (pixel) coords to intermediate world -* coords. The values added to the FitsStore correspond to the CRPIXj, -* PCi_j, CDELTi and WCSAXES keywords, and are determined by examining the -* supplied Mapping, which must be linear with an optional shift of -* origin, otherwise a value of zero is returned. The exception to -* this rule is that if the Mapping contains a PolyMap, and the "sipok" -* argument is non-zero, an attempt is made to create a set of SIP -* headers to describe the non-linear transformation. -* -* Much of the complication in the algorithm arises from the need to -* support cases where the supplied Mapping has more outputs than -* inputs. In these case we add some "degenerate" axes to the grid -* coord system, choosing their unit vectors to be orthogonal to all -* the other grid axes. It is assumed that degenerate axes will never -* be used to find a position other than at the axis value of 1.0. -* -* NOTE, appropriate values for CRVAL keywords should have been stored -* in the FitsStore before calling this function (since this function may -* modify them). - -* Parameters: -* cmap -* A pointer to a Mapping which transforms grid coordinates into -* intermediate world coordinates. The number of outputs must be -* greater than or equal to the number of inputs. -* fr -* Pointer to the final WCS coordinate Frame. -* wperm -* Pointer to an array of integers with one element for each axis of -* the "fr" Frame. Each element holds the zero-based index of the -* FITS-WCS axis (i.e. the value of "i" in the keyword names "CTYPEi", -* "CDi_j", etc) which describes the Frame axis. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* store -* A pointer to the FitsStore into which the calculated CRPIX and -* CDi_j values are to be put. -* dim -* An array holding the image dimensions in pixels. AST__BAD can be -* supplied for any unknwon dimensions. -* fitstol -* The maximum departure from linearity that can be introduced by -* "cmap" on any axis for it to be considered linear. Expressed as -* a fraction of a grid pixel. -* sipok -* Flag indicating if SIP headers should be produced if there is -* a suitable PolyMap in the Mapping chain. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if the CRPIX and CDi_j values are -* succesfully calculated. Zero is returned otherwise. - -* Notes: -* - Zero is returned if an error occurs. -*/ - -/* Local Variables: */ - AstFrame *pfrm; - AstFrame *sfrm; - AstMapping *map; - AstMapping *sipmap; - AstPointSet *psetg; - AstPointSet *psetw; - double **fullmat; - double **partmat; - double **ptrw; - double **ptrg; - double *c; - double *cdelt; - double *cdmat; - double *colvec; - double *d; - double *g0; - double *g; - double *m; - double *mat; - double *tol; - double *w0; - double *y; - double cd; - double cd_sip[4]; - double crp; - double crpix_sip[2]; - double crv; - double cv; - double det; - double err; - double k; - double mxcv; - double skydiag0; - double skydiag1; - double val; - int *iw; - int *lin; - int *pperm; - int *skycol; - int havesip; - int i; - int ii; - int j; - int jax; - int jj; - int lonax; - int latax; - int nin; - int nout; - int nwcs; - int paxis; - int ret; - int sipax[2]; - int sing; - int skycol0; - int skycol1; - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Get the number of inputs and outputs for the Mapping. Return if the - number of outputs is smaller than the number of inputs. */ - nin = astGetNin( cmap ); - nout = astGetNout( cmap ); - if( nout < nin ) return ret; - -/* Simplify the supplied Mapping to reduce rounding errors when - transforming points. */ - map = astSimplify( cmap ); - -/* See if the WCS Frame contains a SkyFrame, and if so get the indices of - the Mapping outputs that correspond to the longitude and latitude - axes. */ - lonax = -1; - latax = -1; - for( i = 0; i < nout; i++ ) { - astPrimaryFrame( fr, i, &pfrm, &paxis ); - if( astIsASkyFrame( pfrm ) ) { - if( paxis == 0 ) { - lonax = i; - } else { - latax = i; - } - } - } - -/* If there is a pair of celestial axes in the WCS Frame, and if the - celestial axes can be described using SIP distortion, then put the - headers describing the SIP distortion into the FitsStore. This also - returns the CRPIX and CD values to use with the celestial axes. */ - havesip = 0; - if( sipok && lonax != -1 ){ - sipmap = SIPIntWorld( map, lonax, latax, s, store, dim, sipax, - crpix_sip, cd_sip, method, class, status ); - -/* If SIP headers were stored successfully, use the modified mapping from - now on. This is the same as "map" but does not include the PolyMap - from which the SIP headers were determined. This is done so that he - PolyMap does not break the linearity test performed below in order to - determine CRPIX and CD values for any other non-celestial axes. */ - if( sipmap ) { - (void) astAnnul( map ); - map = sipmap; - havesip = 1; - } - } - -/* Note the number of final World Coordinate axes (not necessarily the - same as "nout", since some intermediate axes may be discarded by a - later PermMap. */ - nwcs = astGetNaxes( fr ); - -/* Allocate work space. */ - g = astMalloc( sizeof(double)*(size_t) nin ); - g0 = astMalloc( sizeof(double)*(size_t) nin ); - w0 = astMalloc( sizeof(double)*(size_t) nout ); - tol = astMalloc( sizeof(double)*(size_t) nout ); - partmat = astMalloc( sizeof(double *)*(size_t) nout ); - lin = astMalloc( sizeof(int)*(size_t) nout ); - pperm = astMalloc( sizeof(int)*(size_t) nout ); - skycol = astMalloc( sizeof(int)*(size_t) nout ); - cdmat = astMalloc( sizeof(double)*(size_t) (nout*nout) ); - cdelt = astMalloc( sizeof(double)*(size_t) nout ); - -/* For safety, initialise all other pointers. */ - if( partmat ) for( j = 0; j < nout; j++ ) partmat[ j ] = NULL; - fullmat = NULL; - -/* Create a PointSet to hold an input (grid) position for each axis, plus - an extra one. Create two other PointSets to hold corresponding - output (IWC) coordinates. */ - psetg = astPointSet( nin + 1, nin, "", status ); - ptrg = astGetPoints( psetg ); - psetw = astPointSet( nin + 1, nout, "", status ); - ptrw = astGetPoints( psetw ); - -/* Check the pointers can be used safely. */ - if( astOK ) { - -/* Assume success. */ - ret = 1; - -/* The next section finds a 'root' grid position for which the - corresponding IWC coordinates are all good. It also finds these IWC - coordinates, together with the IWC coordinates of "nin" points which - are a unit distance away from the root grid position along each - grid axis. It also finds an estimate of the rounding error in each - Mapping output. - ================================================================= */ - ret = FindBasisVectors( map, nin, nout, dim, psetg, psetw, status ); - -/* Save the grid root position in "g0". */ - for( j = 0; j < nin; j++ ) g0[ j ] = ptrg[ j ][ 0 ]; - -/* Save the transformed root position in "w0". This is the grid root - position represented as a vector within the Intermediate World - Coordinate system. */ - for( j = 0; j < nout; j++ ) { - w0[ j ] = ptrw[ j ][ 0 ]; - -/* Find the tolerance for positions on the j'th IWC axis. This is a - fraction "fitstol" of the largest change in the j'th IWC axis value - caused by moving out 1 pixel along any grid axis. */ - tol[ j ] = 0.0; - for( i = 0; i < nin; i++ ) { - err = fabs( ptrw[ j ][ i + 1 ] - w0[ j ] ); - if( err > tol[ j ] ) tol[ j ] = err; - } - tol[ j ] *= fitstol; - -/* If the tolerance is zero (e.g. as is produced for degenerate axes), - then use a tolerance equal to a very small fraction of hte degenerate - axis value. If the axis value is zero use a fixed small value. */ - if( tol[ j ] == 0.0 ) tol[ j ] = w0[ j ]*DBL_EPSILON*1.0E5; - if( tol[ j ] == 0.0 ) tol[ j ] = sqrt( DBL_MIN ); - } - -/* The next section finds the CD matrix. - ===================================== */ - -/* Initialise the CD matrix elements to "all missing". */ - for( i = 0; i < nout*nout; i++ ) cdmat[ i ] = AST__BAD; - -/* The elements of column "j" of the CD matrix form a vector (in Intermediate - World Coords) which corresponds to a unit vector along grid axis "j". - We now find these vectors for all the grid axes represented by the - inputs to the supplied Mapping. */ - for( i = 0; i < nin && ret; i++ ) { - -/* Form a unit vector along the current input axis. */ - for( ii = 0; ii < nin; ii++ ) g[ ii ] = 0.0; - g[ i ] = 1.0; - -/* Fit a straight line (within IWC) to the current input axis of the Mapping. - The IWC vector corresponding to a unit vector along the current input axis - is returned if the Mapping is linear. A NULL pointer is returned if the - Mapping is not linear. */ - partmat[ i ] = FitLine( map, g, g0, w0, dim[ i ], tol, status ); - -/* If unsuccesful, indicate failure and break out of the loop. */ - if( !partmat[ i ] ) { - ret = 0; - break; - } - } - -/* If we are using SIP distortion, replace the values for the celestial - axes found above with the values found by SIPIntWorld. */ - if( havesip ) { - partmat[ sipax[0] ][ lonax ] = cd_sip[ 0 ]; - partmat[ sipax[1] ][ lonax ] = cd_sip[ 1 ]; - partmat[ sipax[0] ][ latax ] = cd_sip[ 2 ]; - partmat[ sipax[1] ][ latax ] = cd_sip[ 3 ]; - } - -/* If the number of outputs for "map" is larger than the number of inputs, - then we will still be missing some column vectors for the CDi_j matrix - (which has to be square). We invent these such that the they are - orthogonal to all the other column vectors. Only do this if the - Mapping is linear. */ - if( ret ) { - fullmat = OrthVectorSet( nout, nin, partmat, status ); - if( !fullmat ) ret = 0; - } - -/* Check everything is OK. */ - if( ret ) { - -/* Check that the full matrix is invertable, and if not, see if there is - any way to make it invertable. */ - MakeInvertable( fullmat, nout, dim, status ); - -/* Set up an array holding index of the Mapping output corresponding to - each IWC axis (the inverse of "wperm"). Also look for matching pairs of - celestial WCS axes. For the first such pair, note the corresponding - column indices and the diagonal element of the matrix which gives the - scaling for the axis (taking account of the permutation of WCS axes). - Also note if the Mapping from intermediate world coords to final world - coords is linear for each axis (this is assumed to be the case if the - axis is part of a simple Frame). */ - sfrm = NULL; - skydiag0 = AST__BAD; - skydiag1 = AST__BAD; - skycol0 = -1; - skycol1 = -1; - for( i = 0; i < nout; i++ ) { - pperm[ wperm[ i ] ] = i; - astPrimaryFrame( fr, i, &pfrm, &paxis ); - if( IsASkyFrame( pfrm ) ) { - skycol[ wperm[ i ] ] = paxis + 1; - lin[ i ] = 0; - if( !sfrm ) { - sfrm = astClone( pfrm ); - skycol0 = wperm[ i ]; - skydiag0 = fullmat[ skycol0 ][ i ]; - } else if( sfrm == pfrm ) { - skycol1 = wperm[ i ]; - skydiag1 = fullmat[ skycol1 ][ i ]; - } - } else { - skycol[ wperm[ i ] ] = 0; - lin[ i ] = !strcmp( astGetClass( pfrm ), "Frame" ); - } - pfrm = astAnnul( pfrm ); - } - if( sfrm ) sfrm = astAnnul( sfrm ); - -/* We now have the complete CDi_j matrix. Now to find the CRPIX values. - These are the grid coords of the reference point (which corresponds to - the origin of Intermediate World Coords). The "w0" array currently holds - the position of the root position, as a position within IWC, and the - "g0" array holds the corresponding position in grid coordinates. We - also have IWC vectors which correspond to unit vectors on each grid - axis. The CRPIX values are defined by the matrix equation - w0 = fullmat*( g0 - crpix ) - The "g0" array only contains "nin" values. If nout>nin, then the - missing g0 values will be assumed to be zero when we come to find the - CRPIX values below. - We use palDmat to solve this system of simultaneous equations to get - crpix. The "y" array initially holds "w0" but is over-written to hold - "g0 - crpix". */ - mat = astMalloc( sizeof( double )*(size_t)( nout*nout ) ); - y = astMalloc( sizeof( double )*(size_t) nout ); - iw = astMalloc( sizeof( int )*(size_t) nout ); - if( astOK ) { - m = mat; - for( i = 0; i < nout; i++ ) { - for( j = 0; j < nout; j++ ) *(m++) = fullmat[ j ][ i ]; - y[ i ] = w0[ i ]; - } - palDmat( nout, mat, y, &det, &sing, iw ); - } - mat = astFree( mat ); - iw = astFree( iw ); - -/* Loop round all axes, storing the column vector pointer. */ - for( j = 0; j < nout; j++ ) { - colvec = fullmat[ j ]; - -/* Get the CRPIX values from the "y" vector created above by palDmat. - First deal with axes for which there are Mapping inputs. If we are - using SIP distortion, replace the crpix values for the celestial - axes found above with the values found by SIPIntWorld. */ - if( j < nin ) { - crp = g0[ j ] - y[ j ]; - if( havesip ) { - if( j == sipax[0] ){ - crp = crpix_sip[0]; - } else if( j == sipax[1] ){ - crp = crpix_sip[1]; - } - } - -/* If this is a grid axis which has been created to represent a "missing" - input to the mapping, we need to add on 1.0 to the crpix value found - above. This is because the "w0" vector corresponds to a value of zero - on any missing axes, but the FITS grid value for any missing axes is - 1.0. */ - } else { - crp = 1.0 - y[ j ]; - } - -/* Store the CD and CRPIX values for axes which correspond to inputs - of "map". The CD matrix elements are stored in an array and are - converted later to the corresponding PC and CDELT values. */ - if( j < nin || crp == 0.0 ) { - for( i = 0; i < nout; i++ ) { - cdmat[ wperm[ i ]*nout+j ] = colvec[ i ]; - } - SetItem( &(store->crpix), 0, j, s, crp, status ); - -/* The length of the unit vector along any "degenerate" axes was fixed - arbitrarily at 1.0 by the call to OrthVectorSet. We can probably - choose a more appropriate vector length. The choice shouldn't make any - difference to the transformation, but an appropriate value will look - more natural to human readers. */ - } else { - -/* First, try to arrange for longitude/latitude axis pairs to have the same - scale. Do we have a matching pair of celestial axes? */ - k = AST__BAD; - if( skydiag0 != AST__BAD && skydiag1 != AST__BAD ) { - -/* Is the current column the one which corresponds to the first celestial - axis, and does the other sky column correspond to a Mapping input? */ - if( skycol0 == j && skycol1 < nin ) { - -/* If so, scale this column so that its diagonal element is the negative - of the diagonal element of the other axis. This is on the assumption that - the scales on the two axes should be equal, and that longitude increases - east whilst latitude increases north, and that the CD matrix does not - introduce an axis permutation. */ - if( skydiag0 != 0.0 ) k = -skydiag1/skydiag0; - -/* Now see if the current column the one which corresponds to the second - celestial axis. Do the same as above. */ - } else if( skycol1 == j && skycol0 < nin ) { - if( skydiag1 != 0.0 ) k = -skydiag0/skydiag1; - -/* If neither of the above conditions was met, assume a diagonal element - value of 1.0 degrees for latitude axes, and -1.0 degrees for longitude - axes. */ - } - } - -/* If this failed, the next choice is to arrange for diagonally opposite - elements to be equal and opposite in value. Look for the element of the - column which has the largest diagonally opposite element, and choose a - scaling factor which makes this column element equal to the negative value - of its diagonally opposite element. Be careful to take axis permutations - into account when finding the value of the diagonal element. */ - if( k == AST__BAD ) { - mxcv = 0.0; - ii = pperm[ j ]; - for( i = 0; i < nout; i++ ) { - jj = wperm[ i ]; - if( jj < nin ) { - cv = fullmat[ jj ][ ii ]; - if( !astEQUAL( colvec[ i ], 0.0 ) && fabs( cv ) > mxcv ) { - mxcv = fabs( cv ); - k = -cv/colvec[ i ]; - } - } - } - } - -/* If still no scaling factor is available, use a scaling factor which - produces a diagonal element of 1.0 degree if the corresponding row is a - sky latitude axis, -1.0 degree of sky longitude axes, and 1.0 for other - axes. */ - if( k == AST__BAD && colvec[ pperm[ j ] ] != 0.0 ) { - if( skycol[ j ] ) { - k = AST__DD2R/colvec[ pperm[ j ] ]; - if( skycol[ j ] == 1 ) k = -k; - } else { - k = 1.0/colvec[ pperm[ j ] ]; - } - } - -/* If we still do not have a scaling, use 1.0 (no scaling). */ - if( k == AST__BAD ) k = 1.0; - -/* Now scale and store the column elements. */ - for( i = 0; i < nout; i++ ) { - cdmat[ wperm[ i ]*nout+j ] = k*colvec[ i ]; - } - -/* Find the corresponding modified CRPIX value and store it. */ - crp = 1.0 + ( crp - 1.0 )/k; - SetItem( &(store->crpix), 0, j, s, crp, status ); - } - -/* Free resources */ - if( pfrm ) pfrm = astAnnul( pfrm ); - } - -/* Any "degenerate" axes added in the above process for which the - intermediate->world mapping is linear, and which depend only on one - pixel axis, can be adjusted so that the reference point is at grid - coord 1.0. */ - for( i = 0; i < nout; i++ ) { - if( lin[ i ] ) { - -/* Check only one pixel axis contributes to this intermediate world axis - and find which one it is. */ - jax = -1; - for( j = 0; j < nout; j++ ) { - if( !astEQUAL( fullmat[ j ][ i ], 0.0 ) ) { - if( jax == -1 ) { - jax = j; - } else { - jax = -1; - break; - } - } - } - -/* We only adjust values for "degenerate" axes. */ - if( jax >= nin ) { - -/* Check that this pixel axis only contributes to the single world axis - currently being considered. */ - for( ii = 0; ii < nout; ii++ ) { - if( ii != i ) { - if( !astEQUAL( fullmat[ jax ][ ii ], 0.0 ) ) { - jax = -1; - break; - } - } - } - if( jax != -1 ) { - -/* Get the original CRVAL, CRPIX and CD values. Check they are defined.*/ - crv = GetItem( &(store->crval), wperm[ i ], 0, s, NULL, - method, class, status ); - cd = cdmat[ wperm[ i ]*nout + jax ]; - crp = GetItem( &(store->crpix), 0, jax, s, NULL, method, class, status ); - if( crv != AST__BAD && crp != AST__BAD && - cd != AST__BAD ) { - -/* Modify the CRPIX to be 1.0 and modify the CRVAL value accordingly. */ - SetItem( &(store->crpix), 0, jax, s, 1.0, status ); - SetItem( &(store->crval), wperm[ i ], 0, s, - cd*( 1.0 - crp ) + crv, status ); - } - } - } - } - } - -/* Finally, if there are fewer input axes than output axes, put a value for - the WCSAXES keyword into the store. */ - if( nin < nwcs ) SetItem( &(store->wcsaxes), 0, 0, s, nwcs, status ); - -/* Release resources. */ - y = astFree( y ); - } - -/* Produce and store PC and CDELT values from the above CD matrix */ - SplitMat( nout, cdmat, cdelt, status ); - c = cdmat; - d = cdelt; - for( i = 0; i < nout; i++ ){ - for( j = 0; j < nout; j++ ){ - val = *(c++); - if( i == j ){ - if( astEQUAL( val, 1.0 ) ) val = AST__BAD; - } else { - if( astEQUAL( val, 0.0 ) ) val = AST__BAD; - } - if( val != AST__BAD ) SetItem( &(store->pc), i, j, s, val, status ); - } - SetItem( &(store->cdelt), i, 0, s, *(d++), status ); - } - } - -/* Annul pointsets. */ - psetg = astAnnul( psetg ); - psetw = astAnnul( psetw ); - -/* Free other resources*/ - map = astAnnul( map ); - if( fullmat ) for( j = 0; j < nout; j++ ) fullmat[ j ] = astFree( fullmat[ j ] ); - if( partmat ) for( j = 0; j < nout; j++ ) partmat[ j ] = astFree( partmat[ j ] ); - fullmat = astFree( fullmat ); - partmat = astFree( partmat ); - cdmat = astFree( cdmat ); - cdelt = astFree( cdelt ); - g = astFree( g ); - g0 = astFree( g0 ); - w0 = astFree( w0 ); - tol = astFree( tol ); - lin = astFree( lin ); - skycol = astFree( skycol ); - pperm = astFree( pperm ); - -/* If an error has occurred, return zero. */ - if( !astOK ) ret = 0; - -/* Return the answer. */ - return ret; -} - -static void MakeInvertable( double **fullmat, int n, double *dim, int *status ){ -/* -* Name: -* MakeInvertable - -* Purpose: -* Modify a supplied square CD matrix if possible to make it invertable. - -* Type: -* Private function. - -* Synopsis: -* void MakeInvertable( double **fullmat, int n, double *dim, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A search is made for matrix inputs that have no effect on any -* matrix outputs. if any such matrix inputs are associated with -* degenerate pixel axes (i.e. pixel axes that span only a single -* pixel), then the matrix input should always have the value zero and -* so the corresponding diagonal element of the matrix can be set to -* 1.0 without changing and of the outputs. - -* Parameters: -* fullmat -* A pointer to an array with "n" elements corresponding to the n -* inputs of the matrix, each element being a pointer to an array -* with "n" elements corresponding to the n outputs of the matrix. -* n -* The number of inputs and outputs for the square matrix. -* dim -* Pointer to an array of "n" input (i.e. pixel) axis dimensions. -* Individual elements will be AST__BAD if dimensions are not known. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - int i; /* Input index */ - int j; /* Output index */ - int unused; /* Does the current input have no effect on any output? */ - -/* Check inherited status */ - if( !astOK ) return; - -/* Look for any inputs that have no effect on any of the outputs. If such - an input is associated with a degenerate grid axis (i.e. a grid axis - with a dimension of 1.0), then the input value will always be zero and - so the corresponding diagonal element of the matrix can eb set to 1.0 - without affecting the output value (which will always be zero since - zero times anything is zero). Loop over all inputs. */ - for( i = 0; i < n; i++ ) { - -/* Assume this input has no effect on any output. */ - unused = 1; - -/* Loop over all outputs. */ - for( j = 0; j < n; j++ ) { - -/* If the corresponding matrix term is non-zero, the the input will have - an effect on the output, so set the unused flag false and break out of - the output loop. */ - if( fullmat[ i ][ j ] != 0.0 ) { - unused = 0; - break; - } - } - -/* If the input is unused, and it is associated with a degenerate pixel - axis, we can set the corresponding diagonal element of the matrix to - 1.0. */ - if( unused && dim[ i ] == 1.0 ) fullmat[ i ][ i ] = 1.0; - } -} -#if defined(THREAD_SAFE) - -static int ManageLock( AstObject *this_object, int mode, int extra, - AstObject **fail, int *status ) { -/* -* Name: -* ManageLock - -* Purpose: -* Manage the thread lock on an Object. - -* Type: -* Private function. - -* Synopsis: -* #include "object.h" -* AstObject *ManageLock( AstObject *this, int mode, int extra, -* AstObject **fail, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the astManageLock protected -* method inherited from the parent class). - -* Description: -* This function manages the thread lock on the supplied Object. The -* lock can be locked, unlocked or checked by this function as -* deteremined by parameter "mode". See astLock for details of the way -* these locks are used. - -* Parameters: -* this -* Pointer to the Object. -* mode - -* An integer flag indicating what the function should do: -* -* AST__LOCK: Lock the Object for exclusive use by the calling -* thread. The "extra" value indicates what should be done if the -* Object is already locked (wait or report an error - see astLock). -* -* AST__UNLOCK: Unlock the Object for use by other threads. -* -* AST__CHECKLOCK: Check that the object is locked for use by the -* calling thread (report an error if not). -* extra -* Extra mode-specific information. -* fail -* If a non-zero function value is returned, a pointer to the -* Object that caused the failure is returned at "*fail". This may -* be "this" or it may be an Object contained within "this". Note, -* the Object's reference count is not incremented, and so the -* returned pointer should not be annulled. A NULL pointer is -* returned if this function returns a value of zero. -* status -* Pointer to the inherited status variable. - -* Returned Value: - -* A local status value: -* 0 - Success -* 1 - Could not lock or unlock the object because it was already -* locked by another thread. -* 2 - Failed to lock a POSIX mutex -* 3 - Failed to unlock a POSIX mutex -* 4 - Bad "mode" value supplied. - -* Notes: -* - This function attempts to execute even if an error has already -* occurred. -*/ - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to FitsChan structure */ - int result; /* Returned status value */ - -/* Initialise */ - result = 0; - -/* Check the supplied pointer is not NUL. */ - if( ! this_object ) return result; - -/* Obtain a pointers to the FitsChan structure. */ - this = (AstFitsChan *) this_object; - -/* Invoke the ManageLock method inherited from the parent class. */ - if( !result ) result = (*parent_managelock)( this_object, mode, extra, - fail, status ); - -/* Invoke the astManageLock method on any Objects contained within - the supplied Object. */ - if( !result ) result = astManageLock( this->keyseq, mode, extra, fail ); - if( !result ) result = astManageLock( this->keywords, mode, extra, fail ); - return result; -} -#endif - -static int Match( const char *test, const char *temp, int maxfld, int *fields, - int *nfld, const char *method, const char *class, int *status ){ -/* -* Name: -* Match - -* Purpose: -* Sees if a test keyword name matches a template. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int Match( const char *test, const char *temp, int maxfld, int *fields, -* int *nfld, const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* All characters in the template other than "%" (and the field width -* and type specifiers which follow a "%") must be matched by an -* identical character (ignoring case) in the test string. If a "%" occurs -* in the template, then the next character in the template should be a -* single digit specifying a field width. If it is zero, then the test -* string may contain zero or more matching characters. Otherwise, -* the test string must contain exactly the specified number of matching -* characters (i.e. 1 to 9). The field width digit may be omitted, in -* which case the test string must contain one or more matching -* characters. The next character in the template specifies the type of -* matching characters and must be one of "d", "c" or "f". Decimal digits -* are matched by "d", all upper (but not lower) case alphabetical -* characters are matched by "c", and all characters which are legal within -* a FITS keyword (i.e. upper case letters, digits, underscores and -* hyphens) are matched by "f". - -* Parameters: -* test -* Pointer to a null terminated string holding the keyword name to -* be tested. -* temp -* Pointer to a null terminated string holding the template. -* maxfld -* The maximum number of integer field values which should be -* returned in "fields". -* fields -* A pointer to an array of at least "maxfld" integers. This is -* returned holding the values of any integer fields specified -* in the template. The values are extracted from the test string, -* and stored in the order they appear in the template string. -* nfld -* Pointer to a location at which is returned the total number of -* integer fields in the test string. This may be more than the -* number returned in "fields" if "maxfld" is smaller than "*nfld". -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero is returned if the test string does not match the template -* string, and one is returned if it does. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - char type; /* Field type specifier */ - const char *a; /* Pointer to next test character */ - const char *b; /* Pointer to next template character */ - int extend; /* Can the width of the first field be extended? */ - int i; /* Field index */ - int match; /* Does "test" match "temp"? */ - int nfret; /* No. of fields returned */ - int tmp; /* Field value */ - -/* Check global status. */ - if( !astOK ) return 0; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(NULL); - -/* On the first entry to this function, indicate that no integer fields - have yet been returned, and save a pointer to the start of the template - string. */ - if( !match_nentry ) { - *nfld = 0; - match_template = temp; - } - -/* Increment the number of entries into this function. */ - match_nentry++; - -/* Initialise pointers to the start of each string. */ - a = test; - b = temp; - -/* Initialise the returned flag to indicate that the two strings do not - match. */ - match = 0; - -/* Check that the initial part of the test string can match the first - field in the template. */ - if( MatchFront( a, b, &type, &extend, &match_na, &match_nb, method, class, match_template, status ) ){ - -/* If it does, increment the pointers to skip over the characters - used up in the comparison. */ - a += match_na; - b += match_nb; - -/* If the ends of both strings have been reached, they match. */ - if( *a == 0 && *b == 0 ){ - match = 1; - -/* Otherwise, if the end of the template has been reached but there are - still characters to be read from the test string, we could still have - a match if all the remaining test characters match an extandable field. */ - } else if( *b == 0 && *a != 0 && extend ){ - -/* Loop until all the matching characters have been read from the end of - the test string. */ - while( *a != 0 && MatchChar( *a, type, method, class, match_template, status ) ) a++; - -/* If we reached the end of the test string, we have a match. */ - if( *a == 0 ) match = 1; - -/* Otherwise, we need to carry on checking the remaining fields. */ - } else { - -/* Call this function recursively to see if the remainder of the - strings match. */ - if( Match( a, b, maxfld, fields, nfld, method, class, status ) ){ - match = 1; - -/* If the remainder of the strings do not match, we may be able to make - them match by using up some extra test characters on the first field. - This can only be done if the first field has an unspecified field width, - and if the next test character if of a type which matches the first - field in the template. */ - } else if( extend ){ - -/* Loop until all the suitable characters have been read from the - test string. Break out of the loop early if we find a field width - which results in the whole string matching. */ - while( MatchChar( *a, type, method, class, match_template, status ) ){ - a++; - if( Match( a, b, maxfld, fields, nfld, method, class, status ) ){ - match = 1; - break; - } - } - } - } - } - -/* If the strings match and the leading field is an integer, decode - the field and store it in the supplied array (if there is room). */ - if( match && type == 'd' && a > test ){ - if( *nfld < maxfld ){ - sprintf( match_fmt, "%%%dd", (int) ( a - test ) ); - astSscanf( test, match_fmt, fields + *nfld ); - } - (*nfld)++; - } - -/* Decrement the number of entries into this function. */ - match_nentry--; - -/* If we are leaving this function for the last time, reverse the - order of the returned integer fields so that they are returned - in the same order that they occur in the template. */ - if( !match_nentry ){ - nfret = ( *nfld < maxfld ) ? (*nfld) : maxfld; - match_pa = fields; - match_pb = fields + nfret - 1; - for( i = 0; i < nfret/2; i++ ){ - tmp = *match_pa; - *(match_pa++) = *match_pb; - *(match_pb--) = tmp; - } - } - -/* Return the result. */ - return match; -} - -static int MatchChar( char test, char type, const char *method, - const char *class, const char *template, int *status ){ -/* -* Name: -* MatchChar - -* Purpose: -* See if a given character is of a specified type. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int MatchChar( char test, char type, const char *method, -* const char *class, const char *template, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function checks that the supplied test character belongs -* to the set of characters specified by the parameter "type". - -* Parameters: -* test -* The character to test. -* type -* The character specifying the set of acceptable characters. This -* should be one of the field type characters accepted by function -* Match (e.g. "d", "c" or "f"). -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* template -* Pointer to the start of the whole template string, for use in error -* messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero is returned if the test character does not belongs to the -* specified character set, and one is returned if it does. - -* Notes: -* - An error is reported if the type specifier is not legal. -* - Zero is returned if an error has already occurred, or if ths -* function fails for any reason. -*/ - -/* Local Variables: */ - int ret; /* Returned flag */ - -/* Check global status. */ - ret = 0; - if( !astOK ) return ret; - -/* Check for "d" specifiers (digits). */ - if( type == 'd' ){ - ret = isdigit( (int) test ); - -/* Check for "c" specifiers (upper case letters). */ - } else if( type == 'c' ){ - ret = isupper( (int) test ); - -/* Check for "s" specifiers (any legal FITS keyword character). */ - } else if( type == 'f' ){ - ret = isFits( (int) test ); - -/* Report an error for any other specifier. */ - } else if( astOK ){ - ret = 0; - astError( AST__BDFMT, "%s(%s): Illegal field type or width " - "specifier '%c' found in filter template '%s'.", status, - method, class, type, template ); - } - -/* Return the answer. */ - return ret; -} - -static int MatchFront( const char *test, const char *temp, char *type, - int *extend, int *ntest, int *ntemp, - const char *method, const char *class, - const char *template, int *status ){ -/* -* Name: -* MatchFront - -* Purpose: -* Sees if the start of a test string matches the start of a template. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int MatchFront( const char *test, const char *temp, char *type, -* int *extend, int *ntest, int *ntemp, -* const char *method, const char *class, -* const char *template ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function looks for a match between the first field in the -* template string and the string at the start of the test string, -* using the syntax described in function Match. - -* Parameters: -* test -* Pointer to a null terminated string holding the keyword name to -* be tested. -* temp -* Pointer to a null terminated string holding the template. -* type -* Pointer to a location at which to return a character specifying the -* sort of field that was matched. This will be one of the legal field -* types accepted by Match (e.g. "d", "c" or "f"), or null (zero) if -* the first field in the template string was a literal character (i.e. -* did not start with a "%"). -* extend -* Pointer to a location at which to return a flag which will be non-zero -* if the further test characters could be matched by the first field in -* the template. This will be the case if the template field only -* specifies a minimum number of matching characters (i.e. if the field -* width can be extended). For instance, "%d" can be extended, but "%1d" -* cannot. -* ntest -* Pointer to a location at which to return the number of characters -* matched in the test string. This will be the minimum number allowed -* by the template field. -* ntemp -* Pointer to a location at which to return the number of characters -* read from the template string (i.e. the number of characters in the -* field specification). -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* template -* Pointer to the start of the whole template string, for use in error -* messages. - -* Returned Value: -* Zero is returned if the test string starts with fewer than the -* minimum number of characters matching the template string, and one -* is returned if it does. - -* Notes: -* - Zero is returned if an error has already occurred, or if this -* function fails for any reason. -*/ - -/* Local Variables: */ - const char *a; /* Pointer to next test character */ - const char *b; /* Pointer to next template character */ - int i; /* Character index */ - int match; /* Does "test" match "temp"? */ - -/* Check global status. */ - if( !astOK ) return 0; - -/* Initialise pointers to the start of each string. */ - a = test; - b = temp; - -/* Initialise the returned value to indicate that the strings match. */ - match = 1; - -/* If the current character in the template is not a % sign, it must - match the current character in the test string (except for case). */ - if( *b != '%' ){ - if( toupper( (int) *b ) != toupper( (int) *a ) ) { - match = 0; - -/* If the characters match, return all the required information. */ - } else { - *type = 0; - *extend = 0; - *ntest = 1; - *ntemp = 1; - } - -/* If the current character of the template is a %, we need to match - a field. */ - } else { - *ntemp = 3; - -/* The next character in the template string determines the field width. - Get the lowest number of characters which must match in the test string, - and set a flag indicating if this lowest limit can be extended. */ - b++; - if( *b == '0' ){ - *ntest = 0; - *extend = 1; - } else if( *b == '1' ){ - *ntest = 1; - *extend = 0; - } else if( *b == '2' ){ - *ntest = 2; - *extend = 0; - } else if( *b == '3' ){ - *ntest = 3; - *extend = 0; - } else if( *b == '4' ){ - *ntest = 4; - *extend = 0; - } else if( *b == '5' ){ - *ntest = 5; - *extend = 0; - } else if( *b == '6' ){ - *ntest = 6; - *extend = 0; - } else if( *b == '7' ){ - *ntest = 7; - *extend = 0; - } else if( *b == '8' ){ - *ntest = 8; - *extend = 0; - } else if( *b == '9' ){ - *ntest = 9; - *extend = 0; - -/* If no field width was given, one or more test characters are matched. - Step back a character so that the current character will be re-used as - the type specifier. */ - } else { - *ntest = 1; - *extend = 1; - b--; - (*ntemp)--; - } - -/* The next template character gives the type of character which should - be matched. */ - b++; - *type = *b; - -/* Report an error if the template string ended within the field - specifier. */ - if( !*b ){ - match = 0; - astError( AST__BDFMT, "%s(%s): Incomplete field specifier found " - "at end of filter template '%s'.", status, method, class, - template ); - -/* Otherwise, check that the test string starts with the minimum allowed - number of characters matching the specified type. */ - } else { - for( i = 0; i < *ntest; i++ ){ - if( !MatchChar( *a, *type, method, class, template, status ) ){ - match = 0; - break; - } - a++; - } - } - } - -/* Return the answer. */ - return match; -} - -static void MarkCard( AstFitsChan *this, int *status ){ - -/* -* Name: -* MarkCard - -* Purpose: -* Mark the current card as having been read into an AST object. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void MarkCard( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The current card is marked as having been "provisionally used" in -* the construction of an AST object. If the Object is constructed -* succesfully, such cards are marked as having been definitely used, -* and they are then considered to have been removed from the FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan containing the list of cards. -* status -* Pointer to the inherited status variable. - -* Notes: -* - The card remains the current card even though it is now marked -* as having been read. -*/ - int flags; - -/* Return if the global error status has been set, or the current card - is not defined. */ - if( !astOK || !this->card ) return; - -/* Set the PROVISIONALLY_USED flag in the current card, but only if the - PROTECTED flag is not set. */ - flags = ( (FitsCard *) this->card )->flags; - if( !( flags & PROTECTED ) ) { - ( (FitsCard *) this->card )->flags = flags | PROVISIONALLY_USED; - } -} - -static int MoveCard( AstFitsChan *this, int move, const char *method, - const char *class, int *status ){ - -/* -* Name: -* MoveCard - -* Purpose: -* Move the current card a given number of cards forward or backwards. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int MoveCard( AstFitsChan *this, int move, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The current card is increment by the given number of cards, ignoring -* cards which have been read into an AST object if the ignore_used flag -* is set non-zero. - -* Parameters: -* this -* Pointer to the FitsChan containing the list of cards. -* move -* The number of cards by which to move the current card. Positive -* values move towards the end-of-file. Negative values move -* towards the start of the file (i.e. the list head). -* method -* Pointer to string holding name of calling method. -* class -* Pointer to string holding object class. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The number of cards actually moved. This may not always be equal to -* the requested number (for instance, if the end or start of the -* FitsChan is encountered first). - -* Notes: -* - If the end-of-file is reached before the required number of -* cards have been skipped, the current card is set NULL, to indicate -* an end-of-file condition. -* - If the start of the file is reached before the required number of -* cards have been skipped, the current card is left pointing to the -* first usable card. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - FitsCard *card; /* The current card */ - FitsCard *card0; /* The previous non-deleted card */ - int moved; /* The number of cards moved by so far */ - -/* Return if the supplied object is NULL or the FitsChan is - empty, or zero movement is requested. */ - if( !this || !this->head || !move ) return 0; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Get a pointer to the current card. */ - card = (FitsCard *) this->card; - -/* Initialise the number of cards moved so far. */ - moved = 0; - -/* First deal with positive movements (towards the end-of-file). */ - if( move > 0 ){ - -/* Loop round moving on to the next card until the correct number of - moves have been made, or the end-of-file is reached. */ - while( moved < move && card ){ - -/* Get a pointer to the next card in the list, reporting an error if the - links are inconsistent. */ - card = GetLink( card, NEXT, method, class, status ); - -/* If we have moved past the last card and are now pointing back at the - list head, then indicate that we are at end-of-file by setting the - card pointer NULL. */ - if( (void *) card == this->head ){ - card = NULL; - -/* Otherwise, increment the number of cards moved. We ignore cards which - have been read into an AST object if the external "ignore_used" flag is - set. */ - } else if( card ){ - if( !CARDUSED(card) ) moved++; - } - } - -/* Now deal with negative movements (towards the list head), so long as - we are not currently at the list head. */ - } else if( (void *) card != this->head ){ - -/* If we are currently at end-of-file, replace the NULL pointer for the - current card with a pointer to the list head. The first step backwards - will make the last card the current card. */ - if( !card ) card = (FitsCard *) this->head; - -/* Loop round until the correct number of cards have been moved. */ - while( moved < -move && card ){ - -/* If cards which have been read into an AST object are to be included in the - count of moved cards, get a pointer to the previous card in the list, - reporting an error if the links are inconsistent. */ - if( !ignore_used ){ - card = GetLink( card, PREVIOUS, method, class, status ); - -/* If cards which have been read into an AST object are to be ignored... */ - } else { - -/* We need to find the previous card which has not been read into an AST - object. We do not search beyond the start of the list. */ - card0 = GetLink( card, PREVIOUS, method, class, status ); - while( card0 && CARDUSED(card0) && (void *) card0 != this->head ){ - card0 = GetLink( card0, PREVIOUS, method, class, status ); - } - -/* If no such card was found we leave the card where it is. */ - if( card0 && ( card0->flags & USED ) ) { - break; - -/* Otherwise, move back to card found above. */ - } else { - card = card0; - } - } - -/* Increment the number of cards moved. */ - moved++; - -/* If the current card is the list head, break out of the loop. */ - if( (void *) card == this->head ) break; - } - } - -/* Store the new current card. */ - this->card = (void *) card; - -/* Return the answer. */ - return moved; -} - -static double NearestPix( AstMapping *map, double val, int axis, int *status ){ -/* -* Name: -* NearestPix - -* Purpose: -* Find an axis value which corresponds to an integer pixel value. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* double NearestPix( AstMapping *map, double val, int axis, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The supplied axis value is transformed using the inverse of the -* supplied Mapping (other axes are given the value AST__BAD). The -* resulting axis values are rounded to the nearest whole number, and -* then transformed back using the supplied Mapping in the forward -* direction. If the nominated axis value is good, it is returned as -* the function value, otherwise the supplied value is returned unchanged. - -* Parameters: -* map -* A Mapping (usually the input coordinates will correspond to -* pixel coordinates). -* val -* A value for one of the outputs of the "map" Mapping. -* axis -* The index of the Mapping output to which "val" refers. -* status -* Pointer to the inherited status variable. - -* Retuned Value: -* The modified output axis value. -*/ - -/* Local Variables: */ - AstMapping *tmap; /* Mapping to be used */ - AstPointSet *pset1; /* Pixel coords PointSet */ - AstPointSet *pset2; /* WCS coords PointSet */ - double **ptr1; /* Pointer to data in pset1 */ - double **ptr2; /* Pointer to data in pset2 */ - double result; /* Returned value */ - int *ins; /* Array holding input axis indices */ - int i; /* Loop count */ - int nin; /* Number of Mapping inputs */ - int nout; /* Number of Mapping outputs */ - -/* Initialise. */ - result = val; - -/* Check inherited status, and that the supplied value is good. */ - if( !astOK || result == AST__BAD ) return result; - -/* If the supplied Mapping has no inverse, trying splitting off the - transformation for the required axis, which may have an inverse. - If succesful, use the 1-in,1-out Mapping returned by astMapSPlit - instead of the supplied Mapping, and adjust the axis index accordingly. */ - if( !astGetTranInverse( map ) ) { - astInvert( map ); - ins = astMapSplit( map, 1, &axis, &tmap ); - if( tmap ) { - astInvert( tmap ); - axis = 0; - } else { - tmap = astClone( map ); - } - ins = astFree( ins ); - astInvert( map ); - } else { - tmap = astClone( map ); - } - -/* If the Mapping still has no inverse, return the supplied value - unchanged. */ - if( astGetTranInverse( tmap ) ) { - -/* Get the number of input and output coordinates. */ - nin = astGetNin( tmap ); - nout = astGetNout( tmap ); - -/* Create PointSets to hold a single input position and the corresponding - output position. */ - pset1 = astPointSet( 1, nin, "", status ); - ptr1 = astGetPoints( pset1 ); - pset2 = astPointSet( 1, nout, "", status ); - ptr2 = astGetPoints( pset2 ); - if( astOK ) { - -/* Assign AST__BAD values to all output axes, except for the specified - axis, which is given the supplied axis value. */ - for( i = 0; i < nout; i++ ) ptr2[ i ][ 0 ] = AST__BAD; - ptr2[ axis ][ 0 ] = val; - -/* Transform this output position into an input position. */ - (void) astTransform( tmap, pset2, 0, pset1 ); - -/* Round all good axis values in the resulting input position to the nearest - integer. */ - for( i = 0; i < nin; i++ ) { - if( ptr1[ i ][ 0 ] != AST__BAD ) { - ptr1[ i ][ 0 ] = (int) ( ptr1[ i ][ 0 ] + 0.5 ); - } - } - -/* Transform this input position back into output coords. */ - (void) astTransform( tmap, pset1, 1, pset2 ); - -/* If the resulting axis value is good, return it. */ - if( ptr2[ axis ] [ 0 ] != AST__BAD ) result = ptr2[ axis ] [ 0 ]; - } - -/* Free resources. */ - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - } - tmap = astAnnul( tmap ); - -/* Return the result. */ - return result; -} - -static void NewCard( AstFitsChan *this, const char *name, int type, - const void *data, const char *comment, int flags, - int *status ){ - -/* -* Name: -* NewCard - -* Purpose: -* Insert a new card in front of the current card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void NewCard( AstFitsChan *this, const char *name, int type, -* const void *data, const char *comment, int flags, -* int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The supplied keyword name, data type and value, and comment are -* stored in a new FitsCard structure, and this structure is -* inserted into the circular linked list stored in the supplied -* FitsChan. It is inserted in front of the current card. - -* Parameters: -* this -* Pointer to the FitsChan containing the list of cards. -* name -* Pointer to a string holding the keyword name of the new card. -* type -* An integer value representing the data type of the keyword. -* data -* Pointer to the data associated with the keyword. -* comment -* Pointer to a null-terminated string holding a comment. -* flags -* The flags to assign to the card. -* status -* Pointer to the inherited status variable. - -* Notes: -* - The new card is inserted into the list in front of the current card, -* so that the "next" link from the new card points to the current card. -* If the FitsChan is currently at end-of-file (indicated by a NULL -* pointer being stored for the current card), then the card is appended -* to the end of the list. The pointer to the current card is left -* unchanged. -* - Keyword names are converted to upper case before being stored. -* - Any trailing white space in a string value is saved as supplied. -* - Logical values are converted to zero or one before being stored. -* - The "comment" and/or "data" pointers may be supplied as NULL. -*/ - -/* Local Variables: */ - FitsCard *new; /* Pointer to the new card */ - FitsCard *prev; /* Pointer to the previous card in the list */ - char *b; /* Pointer to next stored character */ - const char *a; /* Pointer to next supplied character */ - int lval; /* Logical data value restricted to 0 or 1 */ - int nc; /* No. of characters to store */ - -/* Check the global status. */ - if( !astOK ) return; - -/* Get memory to hold the new FitsCard structure. */ - new = (FitsCard *) astMalloc( sizeof( FitsCard ) ); - -/* Check the pointer can be used. */ - if( astOK ){ - -/* Copy the keyword name, converting to upper case. */ - a = name; - b = new->name; - while( *a ) *(b++) = (char) toupper( (int) *(a++) ); - *b = 0; - -/* Ensure that a KeyMap exists to hold the keywords currently in the - FitsChan. */ - if( !this->keywords ) this->keywords = astKeyMap( " ", status ); - -/* Add the keyword name to the KeyMap. The value associated with the - KeyMap entry is not used and is set arbitrarily to zero. */ - astMapPut0I( this->keywords, new->name, 0, NULL ); - -/* Copy the data type. */ - new->type = type; - -/* Copy any data (ignore any data supplied for an UNDEF value). */ - if( data && type != AST__UNDEF ){ - -/* Logical values are converted to zero or one before being stored. */ - if( type == AST__LOGICAL ){ - lval = *( (int *) data ) ? 1 : 0; - new->size = sizeof( int ); - new->data = astStore( NULL, (void *) &lval, sizeof( int ) ); - -/* String values... */ - } else if( type == AST__STRING || type == AST__CONTINUE ){ - -/* Find the number of characters excluding the trailing null character. */ - nc = strlen( data ); - -/* Store the string, reserving room for a terminating null. */ - new->size = (size_t)( nc + 1 ); - new->data = astStore( NULL, (void *) data, (size_t)( nc + 1 ) ); - -/* Terminate it. */ - ( (char *) new->data)[ nc ] = 0; - -/* Other types are stored as supplied. */ - } else if( type == AST__INT ){ - new->size = sizeof( int ); - new->data = astStore( NULL, (void *) data, sizeof( int ) ); - } else if( type == AST__FLOAT ){ - new->size = sizeof( double ); - new->data = astStore( NULL, (void *) data, sizeof( double ) ); - } else if( type == AST__COMPLEXF ){ - if( *( (double *) data ) != AST__BAD ) { - new->size = 2*sizeof( double ); - new->data = astStore( NULL, (void *) data, 2*sizeof( double ) ); - } else { - nc = strlen( BAD_STRING ); - new->size = (size_t)( nc + 1 ); - new->data = astStore( NULL, BAD_STRING, (size_t)( nc + 1 ) ); - ( (char *) new->data)[ nc ] = 0; - } - } else if( type == AST__COMPLEXI ){ - new->size = 2*sizeof( int ); - new->data = astStore( NULL, (void *) data, 2*sizeof( int ) ); - } else { - new->size = 0; - new->data = NULL; - } - } else { - new->size = 0; - new->data = NULL; - } - -/* Find the first non-blank character in the comment, and find the used - length of the remaining string. We retain leading and trailing white - space if the card is a COMMENT card. */ - if( comment ){ - a = comment; - if( type != AST__COMMENT ) { - while( isspace( *a ) ) a++; - nc = ChrLen( a, status ); - } else { - nc = strlen( a ); - } - } else { - nc = 0; - } - -/* Copy any comment, excluding leading and trailing white space unless - this is a COMMENT card */ - if( nc > 0 ){ - new->comment = astStore( NULL, (void *) a, (size_t)( nc + 1 ) ); - ( (char *) new->comment)[ nc ] = 0; - } else { - new->comment = NULL; - } - -/* Set the supplied flag values. */ - new->flags = flags; - -/* Insert the copied card into the list, in front of the current card. If - the current card is the list head, make the new card the list head. */ - if( this->card ){ - prev = ( ( FitsCard *) this->card )->prev; - ( ( FitsCard *) this->card )->prev = new; - new->prev = prev; - prev->next = new; - new->next = (FitsCard *) this->card; - if( this->card == this->head ) this->head = (void *) new; - -/* If the FitsChan is at end-of-file, append the new card to the end of - the list (i.e. insert it just before the list head). */ - } else { - if( this->head ){ - prev = ( (FitsCard *) this->head )->prev; - ( (FitsCard *) this->head )->prev = new; - new->prev = prev; - prev->next = new; - new->next = (FitsCard *) this->head; - -/* If there are no cards in the list, start a new list. */ - } else { - new->prev = new; - new->next = new; - this->head = (void *) new; - this->card = NULL; - } - } - } - -/* Return. */ - return; -} - -static AstMapping *NonLinSpecWcs( AstFitsChan *this, char *algcode, - FitsStore *store, int i, char s, - AstSpecFrame *specfrm, const char *method, - const char *class, int *status ) { - -/* -* Name: -* NonLinSpecWcs - -* Purpose: -* Create a Mapping describing a FITS-WCS non-linear spectral algorithm - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* AstMapping *NonLinSpecWcs( AstFitsChan *this, char *algcode, -* FitsStore *store, int i, char s, -* AstSpecFrame *specfrm, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function uses the contents of the supplied FitsStore to create -* a Mapping which goes from Intermediate World Coordinate (known as "w" -* in the context of FITS-WCS paper III) to the spectral system -* described by the supplied SpecFrame. -* -* The returned Mapping implements the non-linear "X2P" algorithms -* described in FITS-WCS paper III. The axis is linearly sampled in -* system "X" but expressed in some other system (specified by the -* supplied SpecFrame). - -* Parameters: -* this -* Pointer to the FitsChan. -* algcode -* Pointer to a string holding the non-linear "-X2P" code for the -* required algorithm. This includes aleading "-" character. -* store -* Pointer to the FitsStore structure holding the values to use for -* the WCS keywords. -* i -* The zero-based index of the spectral axis within the FITS header -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* specfrm -* Pointer to the SpecFrame. This specified the "S" system - the -* system in which the CRVAL kewyords (etc) are specified. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a Mapping, or NULL if an error occurs. -*/ - -/* Local Variables: */ - AstFrameSet *fs; - AstMapping *map1; - AstMapping *ret; - AstSpecFrame *xfrm; - AstMapping *map2; - char buf[ 100 ]; - char pc; - double crv; - double ds; - double in_a; - double in_b; - double out_a; - double out_b; - int ok; - int s_sys; - -/* Check the global status. */ - ret = NULL; - if( !astOK ) return ret; - -/* Identify the spectral "X" system within the "X2P" algorithm code, and - create a SpecFrame describing the X system ("X" is the system in - which the axis is linearly sampled). This is done by copying the - supplied SpecFrame and then setting its System attribute. Copying - the supplied SpecFrame ensures that all the other attributes (RestFreq, - etc.) are set correctly. */ - ok = 1; - xfrm = astCopy( specfrm ); - if( algcode[ 1 ] == 'F' ) { - astSetSystem( xfrm, AST__FREQ ); - astSetUnit( xfrm, 0, "Hz" ); - } else if( algcode[ 1 ] == 'W' ) { - astSetSystem( xfrm, AST__WAVELEN ); - astSetUnit( xfrm, 0, "m" ); - } else if( algcode[ 1 ] == 'V' ) { - astSetSystem( xfrm, AST__VREL ); - astSetUnit( xfrm, 0, "m/s" ); - } else if( algcode[ 1 ] == 'A' ) { - astSetSystem( xfrm, AST__AIRWAVE ); - astSetUnit( xfrm, 0, "m" ); - } else { - ok = 0; - } - -/* If the X system was identified, find a Mapping from the "S" (specfrm) - system to the X system. */ - map1 = NULL; - if( ok ) { - ok = 0; - fs = astConvert( specfrm, xfrm, "" ); - if( fs ) { - map1 = astGetMapping( fs, AST__BASE, AST__CURRENT ); - fs = astAnnul( fs ); - ok = 1; - } - -/* Issue a warning if the "P" system is not the correct one for the given - "S" system. We can however continue, sine AST interprets illegal "P" - systems correctly. */ - pc = 0; - s_sys = astGetSystem( specfrm ); - if( s_sys == AST__FREQ || s_sys == AST__ENERGY || - s_sys == AST__WAVENUM || s_sys == AST__VRADIO ) { - pc = 'F'; - } else if( s_sys == AST__WAVELEN || s_sys == AST__VOPTICAL || - s_sys == AST__REDSHIFT ){ - pc = 'W'; - } else if( s_sys == AST__AIRWAVE ) { - pc = 'A'; - } else if( s_sys == AST__BETA || s_sys == AST__VREL ) { - pc = 'V'; - } else if( astOK ) { - pc = algcode[ 3 ]; - astError( AST__INTER, "%s: Function NonLinSpecWcs does not yet " - "support spectral axes of type %s (internal AST " - "programming error).", status, method, astGetC( specfrm, "System" ) ); - } - if( algcode[ 3 ] != pc ) { - sprintf( buf, "The spectral CTYPE value %s%s is not legal - " - "using %s%.3s%c instead.", astGetC( specfrm, "System" ), - algcode, astGetC( specfrm, "System" ), algcode, pc ); - Warn( this, "badctype", buf, method, class, status ); - } - } - -/* If succesfull, use this Mapping to find the reference value (CRVAL) - in the "X" system. */ - if( ok ) { - -/* Get the CRVAL value for the spectral axis (this will be in the S system). */ - crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( crv == AST__BAD ) crv = 0.0; - -/* Convert it to the X system. */ - astTran1( map1, 1, &crv, 1, &crv ); - -/* Invert this Mapping so that it forward transformation goes from X to S. */ - astInvert( map1 ); - -/* Find the rate of change of S with respect to X (dS/dX) at the reference - point (x = crv). */ - ds = astRate( map1, &crv, 0, 0 ); - if( ds != AST__BAD && ds != 0.0 ) { - -/* FITS-WCS paper III says that dS/dw must be 1.0 at the reference point. - Therefore dX/dw = dX/dS at the reference point. Also, since the spectral - axis is linear in X, dX/dw must be constant. Therefore the Mapping from - IWC to X is a WinMap which scales the IWC axis ("w") by dX/dw and adds - on the X value at the reference point. */ - if( crv != 0.0 ) { - in_a = 0.0; - out_a = crv; - in_b = crv*ds; - out_b = 2.0*crv; - map2 = (AstMapping *) astWinMap( 1, &in_a, &in_b, &out_a, &out_b, "", status ); - } else { - map2 = (AstMapping *) astZoomMap( 1, 1.0/ds, "", status ); - } - -/* The Mapping to be returned is the concatenation of the above Mapping - (from w to X) with the Mapping from X to S. */ - ret = (AstMapping *) astCmpMap( map2, map1, 1, "", status ); - map1 = astAnnul( map1 ); - map2 = astAnnul( map2 ); - } - } - xfrm = astAnnul( xfrm ); - -/* Return the result */ - return ret; -} - -static double *OrthVector( int n, int m, double **in, int *status ){ -/* -* Name: -* OrthVector - -* Purpose: -* Find a unit vector which is orthogonal to a set of supplied vectors. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* double *OrthVector( int n, int m, double **in, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A set of M vectors is supplied, each vector being N-dimensional. -* It is assumed that M < N and that the supplied vectors span M -* axes within the N dimensional space. An N-dimensional unit vector is -* returned which is orthogonal to all the supplied vectors. -* -* The required vector is orthogonal to all the supplied vectors. -* Therefore the dot product of the required vector with each of the -* supplied vectors must be zero. This gives us M equations of the - -* form: -* -* a1*r1 + a2*r2 + a3*r3 + .... + aN*rN = 0.0 -* b1*r1 + b2*r2 + b3*r3 + .... + bN*rN = 0.0 -* ... -* -* where (a1,a2,..,aN), (b1,b2,..,bN), ... are the supplied vectors -* and (r1,r2,...,rN) is the required vector. Since M is less -* than N the system of linear simultaneous equations is under -* specified and we need to assign arbitrary values to some of the -* components of the required vector in order to allow the equations -* to be solved. We arbitrarily assume that 1 element of the required -* vector has value 1.0 and (N-M-1) have value zero. The selection of -* *which* elements to set constant is based on the magnitudes of the -* columns of coefficients (a1,b1...), (a2,b2,...), etc. The M components -* of the required vector which are *not* set constant are the ones which -* have coefficient columns with the *largest* magnitude. This choice is -* made in order to minimise the risk of the remaining matrix of -* coefficients being singular (for instance, if a component of the -* required vector has a coefficient of zero in every supplied vector -* then the column magnitude will be zero and that component will be -* set to 1.0). After choosing the M largest columns, the largest -* remaining column is assigned a value of 1.0 in the required vector, -* and all other columns are assigned the value zero in the required - -* vector. This means that the above equations becomes: -* -* a1*r1 + a2*r2 + a3*r3 + .... + aM*rM = -aM+1 -* b1*r1 + b2*r2 + b3*r3 + .... + bM*rM = -bM+1 -* ... -* -* Where the indices are now not direct indices into the supplied and -* returned vectors, but indices into an array of indices which have -* been sorted into column magnitude order. This is now a set of MxM - -* simultaneous linear equations which we can solve using palDmat: -* -* MAT.R = V -* -* where MAT is the the matrix of columns (coefficients) on the left -* hand side of the above set of simultaneous equations, R is the -* required vector (just the components which have *not* been set -* constant), and V is a constant vector equal to the column of values -* on the right hand side in the above set of simultaneous equations. -* The palDmat function solves this equation to obtain R. - -* Parameters: -* n -* The number of dimensions -* m -* The number of supplied vectors. -* in -* A pointer to an array with "m" elements, each element being a -* pointer to an array with "n" elements. Each of these "n" element -* array holds one of the supplied vectors. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The pointer to some newly allocated memory holding the returned N -* dimensional unit vector. The memory should be freed using astFree when -* no longer needed. - -* Notes: -* - NULL is returned if an error occurs. -* - NULL is returned (without error) if the required vector cannot -* be found (.e.g becuase the supplied M vectors span less than M axes). -*/ - -/* Local Variables: */ - double *colmag; - double *d; - double *e; - double *mat; - double *mel; - double *ret; - double *rhs; - double det; - double sl; - int *colperm; - int *iw; - int done; - int i; - int ih; - int ii; - int il; - int j; - int sing; - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Return if any of the M supplied vectors are NULL. */ - for( i = 0; i < m; i++ ) { - if( !in[ i ] ) return ret; - } - -/* Allocate rquired memory. */ - ret = astMalloc( sizeof( double )*(size_t) n ); - rhs = astMalloc( sizeof( double )*(size_t) m ); - mat = astMalloc( sizeof( double )*(size_t) m*m ); - iw = astMalloc( sizeof( int )*(size_t) m ); - colmag = astMalloc( sizeof( double )*(size_t) n ); - colperm = astMalloc( sizeof( int )*(size_t) n ); - -/* Check memory can be used safely. */ - if( astOK ) { - -/* Find the magnitude of each column of coefficients in the full set of - simultaneous linear equations (before setting any components of the - required vector constant). Also initialise the column permutation array - to indicate that the columns are in their original order. The outer - loop loops through the columns and the inner loop loops through rows - (i.e. equations). */ - for( i = 0; i < n; i++ ) { - colperm[ i ] = i; - colmag[ i ] = 0.0; - for( j = 0; j < m; j++ ) { - colmag[ i ] += in[ j ][ i ]*in[ j ][ i ]; - } - } - -/* Now re-arrange the column indices within the permutation array so that - they are in order of decreasing ciolumn magnitude (i.e. colperm[0] will - be left holding the index of the column with the largest magnitude). A - simple bubble sort is used. */ - ii = 1; - done = 0; - while( !done ) { - done = 1; - for( i = ii; i < n; i++ ) { - ih = colperm[ i ]; - il = colperm[ i - 1 ]; - if( colmag[ ih ] > colmag[ il ] ) { - colperm[ i ] = il; - colperm[ i - 1 ] = ih; - done = 0; - } - } - ii++; - } - -/* The first M elements in "colperm" now hold the indices of the - columns which are to be used within the MAT matrix, the next element - of "colperm" hold the index of the column which is to be included in the - V vector (other elements hold the indices of the columns which are - being ignored because they will be mutiplied by a value of zero - the - assumed value of the corresponding components of the returned vector). We - now copy the these values into arrays which can be passed to palDmat. - First, initialise a pointer used to step through the mat array. */ - mel = mat; - -/* Loop through all the supplied vectors. Get a pointer to the first - element of the vector. */ - for( i = 0; i < m; i++ ) { - d = in[ i ]; - -/* Copy the required M elements of this supplied vector into the work array - which will be passed to palDmat. */ - for( j = 0; j < m; j++ ) *(mel++) = d[ colperm[ j ] ]; - -/* Put the next right-hand side value into the "rhs" array. */ - rhs[ i ] = -d[ colperm[ m ] ]; - } - -/* Use palDmat to find the first M elements of the returned array. These - are stored in "rhs", over-writing the original right-hand side values. */ - palDmat( m, mat, rhs, &det, &sing, iw ); - -/* If the supplied vectors span fewer than M axes, the above call will fail. - In this case, annul the returned vector. */ - if( sing != 0 ) { - ret = astFree( ret ); - -/* If succesful, copy the M elements of the solution vector into the - required M elements of the returned vector. Also find the squared length - of the vector. */ - } else { - sl = 0.0; - e = rhs; - for( j = 0; j < m; j++ ) { - sl += (*e)*(*e); - ret[ colperm[ j ] ] = *(e++); - } - -/* Put 1.0 into the next element of the returned vector. */ - sl += 1.0; - ret[ colperm[ m ] ] = 1.0; - -/* Fill up the rest of the returned vector with zeros. */ - for( j = m + 1; j < n; j++ ) ret[ colperm[ j ] ] = 0.0; - -/* Normalise the returned vector so that it is a unit vector.Also ensure - that any zeros are "+0.0" insteasd of "-0.0". */ - e = ret; - sl = sqrt( sl ); - for( j = 0; j < n; e++,j++ ) { - *e /= sl; - if( *e == 0.0 ) *e = 0.0; - } - } - } - -/* Free workspace. */ - rhs = astFree( rhs ); - mat = astFree( mat ); - iw = astFree( iw ); - colmag = astFree( colmag ); - colperm = astFree( colperm ); - -/* Free the returned vector if an error has occurred. */ - if( !astOK ) ret = astFree( ret ); - -/* Return the answer. */ - return ret; -} - -static double **OrthVectorSet( int n, int m, double **in, int *status ){ -/* -* Name: -* OrthVectorSet - -* Purpose: -* Find a set of mutually orthogonal vectors. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* double **OrthVectorSet( int n, int m, double **in, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A set of M vectors is supplied, each vector being N-dimensional. -* It is assumed that the supplied vectors span M axes within the -* N dimensional space. A pointer to a set of N vectors is returned. -* The first M returned vectors are copies of the M supplied vectors. -* The remaining returned vectors are unit vectors chosen to be -* orthogonal to all other vectors in the returned set. - -* Parameters: -* n -* The number of dimensions -* m -* The number of supplied vectors. -* in -* A pointer to an array with "m" elements, each element being a -* pointer to an array with "n" elements. Each of these "n" element -* array holds one of the supplied vectors. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The pointer to some newly allocated memory holding the returned N -* vectors. The pointer locates an array of N elements, each of which -* is a pointer to an array holding the N elements of a single vector. -* The memory (including the inner pointers) should be freed using -* astFree when no longer needed. - -* Notes: -* - NULL is returned if an error occurs. -* - NULL is returned (without error) if the required vectors cannot -* be found (e.g. becuase the supplied M vectors span less than M axes). -*/ - -/* Local Variables: */ - double **ret; - int i; - int bad; - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Allocate required memory. */ - ret = astMalloc( sizeof( double * )*(size_t) n ); - -/* Check memory can be used safely. */ - bad = 0; - if( astOK ) { - -/* Copy the supplied vectors into the returned array. */ - for( i = 0; i < m; i++ ) { - ret[ i ] = astStore( NULL, in[ i ], sizeof( double )*n ); - } - -/* For the remaining vectors, find a vector which is orthogonal to all - the vectors currently in the returned set. */ - for( ; i < n; i++ ) { - ret[ i ] = OrthVector( n, i, ret, status ); - if( !ret[ i ] ) bad = 1; - } - } - -/* Free the returned vectors if an error has occurred. */ - if( bad || !astOK ) { - for( i = 0; ret && i < n; i++ ) ret[ i ] = astFree( ret[ i ] ); - ret = astFree( ret ); - } - -/* Return the answer. */ - return ret; -} - -static AstMapping *OtherAxes( AstFitsChan *this, AstFrameSet *fs, double *dim, - int *wperm, char s, FitsStore *store, - double *crvals, int *axis_done, - const char *method, const char *class, - int *status ){ - -/* -* Name: -* OtherAxes - -* Purpose: -* Add values to a FitsStore describing unknown axes in a Frame. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* AstMapping *OtherAxes( AstFitsChan *this, AstFrameSet *fs, double *dim, -* int *wperm, char s, FitsStore *store, -* double *crvals, int *axis_done, -* const char *method, const char *class, -* int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* FITS WCS keyword values are added to the supplied FitsStore which -* describe any as yet undescribed axes in the supplied FrameSet. These -* axes are assumed to be linear and to follow the conventions -* of FITS-WCS paper I (if in fact they are not linear, then the -* grid->iwc mapping determined by MakeIntWorld will not be linear and -* so the axes will be rejected). -* -* Note, this function does not store values for keywords which define -* the transformation from pixel coords to Intermediate World Coords -* (CRPIX, PC and CDELT), but a Mapping is returned which embodies these -* values. This Mapping is from the current Frame in the FrameSet (WCS -* coords) to a Frame representing IWC. The IWC Frame has the same number -* of axes as the WCS Frame which may be greater than the number of base -* Frame (i.e. pixel) axes. - -* Parameters: -* this -* Pointer to the FitsChan. -* fs -* Pointer to the FrameSet. The base Frame should represent FITS pixel -* coordinates, and the current Frame should represent FITS WCS -* coordinates. The number of base Frame axes should not exceed the -* number of current Frame axes. -* dim -* An array holding the image dimensions in pixels. AST__BAD can be -* supplied for any unknwon dimensions. -* wperm -* Pointer to an array of integers with one element for each axis of -* the current Frame. Each element holds the zero-based -* index of the FITS-WCS axis (i.e. the value of "i" in the keyword -* names "CTYPEi", "CRVALi", etc) which describes the Frame axis. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* store -* The FitsStore in which to store the FITS WCS keyword values. -* crvals -* Pointer to an array holding the default CRVAL value for each -* axis in the WCS Frame. -* axis_done -* An array of flags, one for each Frame axis, which indicate if a -* description of the corresponding axis has yet been stored in the -* FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* If any axis descriptions were added to the FitsStore, a Mapping from -* the current Frame of the supplied FrameSet, to the IWC Frame is returned. -* Otherwise, a UnitMap is returned. Note, the Mapping only defines the IWC -* transformation for the described axes. Any other (previously -* described) axes are passed unchanged by the returned Mapping. -*/ - -/* Local Variables: */ - AstFitsTable *table; /* Pointer to structure holding -TAB table info */ - AstFrame *wcsfrm; /* WCS Frame within FrameSet */ - AstMapping *axmap; /* Mapping from WCS to IWC */ - AstMapping *map; /* FITS pixel->WCS Mapping */ - AstMapping *ret; /* Returned Mapping */ - AstMapping *tmap0; /* Pointer to a temporary Mapping */ - AstMapping *tmap1; /* Pointer to a temporary Mapping */ - AstPermMap *pm; /* PermMap pointer */ - AstPointSet *pset1; /* PointSet holding central pixel position */ - AstPointSet *pset2; /* PointSet holding reference WCS position */ - char buf[80]; /* Text buffer */ - const char *lab; /* Pointer to axis Label */ - const char *sym; /* Pointer to axis Symbol */ - double **ptr1; /* Pointer to data for pset1 */ - double **ptr2; /* Pointer to data for pset2 */ - double *lbnd_p; /* Pointer to array of lower pixel bounds */ - double *ubnd_p; /* Pointer to array of upper pixel bounds */ - double crval; /* The value for the FITS CRVAL keyword */ - int *inperm; /* Pointer to permutation array for input axes */ - int *outperm; /* Pointer to permutation array for output axes */ - int extver; /* Table version number for -TAB headers */ - int fits_i; /* FITS WCS axis index */ - int i; /* Loop count */ - int iax; /* WCS Frame axis index */ - int icolindex; /* Index of table column holding index vector */ - int icolmain; /* Index of table column holding main coord array */ - int interp; /* Interpolation method for look-up tables */ - int log_axis; /* Is the axis logarithmically spaced? */ - int nother; /* Number of axes still to be described */ - int npix; /* Number of pixel axes */ - int nwcs; /* Number of WCS axes */ - int tab_axis; /* Can the axis be described by the -TAB algorithm? */ - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Get the number of WCS axes. */ - nwcs = astGetNaxes( fs ); - -/* Count the number of WCS axes which have not yet been described. */ - nother = 0; - for( iax = 0; iax < nwcs; iax++ ) { - if( ! axis_done[ iax ] ) nother++; - } - -/* Only proceed if there are some axes to described. */ - if( nother ) { - -/* Get a pointer to the WCS Frame. */ - wcsfrm = astGetFrame( fs, AST__CURRENT ); - -/* Get a pointer to the pixel->wcs Mapping. */ - map = astGetMapping( fs, AST__BASE, AST__CURRENT ); - -/* Store the number of pixel and WCS axes. */ - npix = astGetNin( fs ); - nwcs = astGetNout( fs ); - -/* Store the upper and lower pixel bounds. */ - lbnd_p = astMalloc( sizeof( double )*(size_t) npix ); - ubnd_p = astMalloc( sizeof( double )*(size_t) npix ); - if( astOK ) { - for( iax = 0; iax < npix; iax++ ) { - lbnd_p[ iax ] = 1.0; - ubnd_p[ iax ] = ( dim[ iax ] != AST__BAD ) ? dim[ iax ] : 500; - } - } - -/* Transform the central pixel coords into WCS coords */ - pset1 = astPointSet( 1, npix, "", status ); - ptr1 = astGetPoints( pset1 ); - pset2 = astPointSet( 1, nwcs, "", status ); - ptr2 = astGetPoints( pset2 ); - if( astOK ) { - for( iax = 0; iax < npix; iax++ ) { - ptr1[ iax ][ 0 ] = ( dim[ iax ] != AST__BAD ) ? floor( 0.5*dim[ iax ] ) : 1.0; - } - (void) astTransform( map, pset1, 1, pset2 ); - } - -/* Loop round all WCS axes, producing descriptions of any axes which have not - yet been described. */ - for( iax = 0; iax < nwcs && astOK; iax++ ) { - if( ! axis_done[ iax ] ) { - -/* Get the (one-based) FITS WCS axis index to use for this Frame axis. */ - fits_i = wperm[ iax ]; - -/* Use the supplied default CRVAL value. If bad, use the WCS value - corresponding to the central pixel found above (if this value is bad, - abort). */ - crval = crvals ? crvals[ iax ] : AST__BAD; - if( crval == AST__BAD ) crval = ptr2[ iax ][ 0 ]; - if( crval == AST__BAD ) { - break; - } else { - SetItem( &(store->crval), fits_i, 0, s, crval, status ); - } - -/* Initialise flags indicating the type of the axis. */ - log_axis = 0; - tab_axis = 0; - -/* Get the table version number to use if we end up using the -TAB - algorithm. This is the set value of the TabOK attribute (if positive). */ - extver = astGetTabOK( this ); - -/* See if the axis is linear. If so, create a ShiftMap which subtracts off - the CRVAL value. */ - - if( IsMapLinear( map, lbnd_p, ubnd_p, iax, status ) ) { - crval = -crval; - tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status ); - axmap = AddUnitMaps( tmap0, iax, nwcs, status ); - tmap0 = astAnnul( tmap0 ); - crval = -crval; - -/* If it is not linear, see if it is logarithmic. If the "log" algorithm is - appropriate (as defined in FITS-WCS paper III), the supplied Frame (s) is - related to pixel coordinate (p) by - s = Sr.EXP( a*p - b ). If this - is the case, the log of s will be linearly related to pixel coordinates. - Test this. If the test is passed a Mapping is returned from WCS to IWC. */ - } else if( (axmap = LogAxis( map, iax, nwcs, lbnd_p, ubnd_p, - crval, status ) ) ) { - log_axis = 1; - -/* If it is not linear or logarithmic, and the TabOK attribute is - non-zero, describe it using the -TAB algorithm. */ - } else if( extver > 0 ){ - -/* Get any pre-existing FitsTable from the FitsStore. This is the table - in which the tabular data will be stored (if the Mapping can be expressed - in -TAB form). */ - if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL; - -/* See if the Mapping can be expressed in -TAB form. */ - tmap0 = IsMapTab1D( map, 1.0, NULL, wcsfrm, dim, iax, fits_i, - &table, &icolmain, &icolindex, &interp, - status ); - if( tmap0 ) { - tab_axis = 1; - -/* The values stored in the table index vector are GRID coords. So we - need to ensure that IWC are equivalent to GRID coords. So set CRVAL - to zero. */ - crval = 0.0; - -/* Store TAB-specific values in the FitsStore. First the name of the - FITS binary table extension holding the coordinate info. */ - SetItemC( &(store->ps), fits_i, 0, s, AST_TABEXTNAME, status ); - -/* Next the table version number. This is the set (positive) value for the - TabOK attribute. */ - SetItem( &(store->pv), fits_i, 1, s, extver, status ); - -/* Also store the table version in the binary table header. */ - astSetFitsI( table->header, "EXTVER", extver, - "Table version number", 0 ); - -/* Next the name of the table column containing the main coords array. */ - SetItemC( &(store->ps), fits_i, 1, s, - astColumnName( table, icolmain ), status ); - -/* Next the name of the column containing the index array */ - if( icolindex >= 0 ) SetItemC( &(store->ps), fits_i, 2, s, - astColumnName( table, icolindex ), status ); - -/* The interpolation method (an AST extension to the published -TAB - algorithm, communicated through the QVi_4a keyword). */ - SetItem( &(store->pv), fits_i, 4, s, interp, status ); - -/* Also store the FitsTable itself in the FitsStore. */ - astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL ); - -/* Create the WCS -> IWC Mapping (AST uses grid coords as IWC coords for - the -TAB algorithm). First, get a Mapping that combines the TAB axis - Mapping( tmap0) in parallel with one or two UnitMaps in order to put - the TAB axis at the required index. */ - tmap1 = AddUnitMaps( tmap0, iax, nwcs, status ); - -/* Now get a PermMap that permutes the WCS axes into the FITS axis order. */ - inperm = astMalloc( sizeof( double )*nwcs ); - outperm = astMalloc( sizeof( double )*nwcs ); - if( astOK ) { - for( i = 0; i < nwcs; i++ ) { - inperm[ i ] = wperm[ i ]; - outperm[ wperm[ i ] ] = i; - } - } - pm = astPermMap( nwcs, inperm, nwcs, outperm, NULL, "", - status ); - -/* Combine these two Mappings in series, to get the Mapping from WCS to - IWC. */ - axmap = (AstMapping *) astCmpMap( pm, tmap1, 1, " ", - status ); - -/* Free resources. */ - inperm = astFree( inperm ); - outperm = astFree( outperm ); - pm = astAnnul( pm ); - tmap0 = astAnnul( tmap0 ); - tmap1 = astAnnul( tmap1 ); - } - if( table ) table = astAnnul( table ); - } - -/* If the axis cannot be described by any of the above methods, we - pretend it is linear. This will generate a non-linear PIXEL->IWC - mapping later (in MakeIntWorld) which will cause the write operation - to fail. */ - if( !axmap ) { - crval = -crval; - tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status ); - axmap = AddUnitMaps( tmap0, iax, nwcs, status ); - tmap0 = astAnnul( tmap0 ); - crval = -crval; - } - -/* Combine the Mapping for this axis in series with those of earlier axes. */ - if( ret ) { - tmap0 = (AstMapping *) astCmpMap( ret, axmap, 1, "", status ); - (void) astAnnul( ret ); - ret = tmap0; - } else { - ret = astClone( axmap ); - } - -/* Get axis label and symbol. */ - sym = astGetSymbol( wcsfrm, iax ); - lab = astGetLabel( wcsfrm, iax ); - -/* The axis symbols are taken as the CTYPE values. Append "-LOG" or "-TAB" if - the axis is logarithmic or tabular. */ - if( sym && strlen( sym ) ) { - (void) sprintf( buf, "%s", sym ); - } else { - (void) sprintf( buf, "AXIS%d", iax + 1 ); - } - if( log_axis ) { - SetAlgCode( buf, "-LOG", status ); - } else if( tab_axis ) { - SetAlgCode( buf, "-TAB", status ); - } - SetItemC( &(store->ctype), fits_i, 0, s, buf, status ); - -/* The axis labels are taken as the comment for the CTYPE keywords and as - the CNAME keyword (but only if a label has been set and is different to - the symbol). */ - if( lab && lab[ 0 ] && astTestLabel( wcsfrm, iax ) && strcmp( sym, lab ) ) { - SetItemC( &(store->ctype_com), fits_i, 0, s, (char *) lab, status ); - SetItemC( &(store->cname), fits_i, 0, s, (char *) lab, status ); - } else { - sprintf( buf, "Type of co-ordinate on axis %d", iax + 1 ); - SetItemC( &(store->ctype_com), fits_i, 0, s, buf, status ); - } - -/* If a value has been set for the axis units, use it as CUNIT. */ - if( astTestUnit( wcsfrm, iax ) ){ - SetItemC( &(store->cunit), fits_i, 0, s, (char *) astGetUnit( wcsfrm, iax ), status ); - } - -/* Indicate this axis has now been described. */ - axis_done[ iax ] = 1; - -/* Release Resources. */ - axmap = astAnnul( axmap ); - } - } - -/* Release Resources. */ - wcsfrm = astAnnul( wcsfrm ); - map = astAnnul( map ); - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - lbnd_p = astFree( lbnd_p ); - ubnd_p = astFree( ubnd_p ); - } - -/* If we have a Mapping to return, simplify it. Otherwise, create - a UnitMap to return. */ - if( ret ) { - tmap0 = ret; - ret = astSimplify( tmap0 ); - tmap0 = astAnnul( tmap0 ); - } else { - ret = (AstMapping *) astUnitMap( nwcs, "", status ); - } - -/* Return the result. */ - return ret; -} - -static int PCFromStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ){ - -/* -* Name: -* PCFromStore - -* Purpose: -* Store WCS keywords in a FitsChan using FITS-PC encoding. - -* Type: -* Private function. - -* Synopsis: -* int PCFromStore( AstFitsChan *this, FitsStore *store, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function copies the WCS information stored in the supplied -* FitsStore into the supplied FitsChan, using FITS-PC encoding. -* -* Zero is returned if the primary axis descriptions cannot be produced. -* Whether or not secondary axis descriptions can be produced does not -* effect the returned value (i.e. failure to produce a specific set of -* secondary axes does not prevent other axis descriptions from being -* produced). - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if succesfull, and zero is returned -* otherwise. -*/ - -/* Local Variables: */ - char *comm; /* Pointer to comment string */ - char *cval; /* Pointer to string keyword value */ - char combuf[80]; /* Buffer for FITS card comment */ - char keyname[10]; /* Buffer for keyword name string */ - char primsys[20]; /* Buffer for primnary RADECSYS value */ - char type[MXCTYPELEN];/* Buffer for CTYPE value */ - char s; /* Co-ordinate version character */ - char sign[2]; /* Fraction's sign character */ - char sup; /* Upper limit on s */ - double *c; /* Pointer to next array element */ - double *d; /* Pointer to next array element */ - double *matrix; /* Pointer to Frame PC/CD matrix */ - double *primpc; /* Pointer to primary PC/CD matrix */ - double fd; /* Fraction of a day */ - double mjd99; /* MJD at start of 1999 */ - double primdt; /* Primary mjd-obs value */ - double primeq; /* Primary equinox value */ - double primln; /* Primary lonpole value */ - double primlt; /* Primary latpole value */ - double primpv[10]; /* Primary projection parameter values */ - double val; /* General purpose value */ - int axlat; /* Index of latitude FITS WCS axis */ - int axlon; /* Index of longitude FITS WCS axis */ - int axspec; /* Index of spectral FITS WCS axis */ - int i; /* Axis index */ - int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ - int is; /* Co-ordinate version index */ - int iymdf[ 4 ]; /* Year, month, date, fractional day */ - int j; /* Axis index */ - int jj; /* SlaLib status */ - int m; /* Parameter index */ - int maxm; /* Upper limit on m */ - int naxis; /* No. of axes */ - int nc; /* Length of string */ - int ok; /* Frame written out succesfully? */ - int prj; /* Projection type */ - int ret; /* Returned value. */ - -/* Initialise */ - ret = 0; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Find the number of co-ordinate versions in the FitsStore. FITS-PC - can only encode 10 axis descriptions (including primary). */ - sup = GetMaxS( &(store->crval), status ); - if( sup > 'I' ) return ret; - -/* Initialise */ - primdt = AST__BAD; - primeq = AST__BAD; - primln = AST__BAD; - primlt = AST__BAD; - -/* Loop round all co-ordinate versions (0-9) */ - primpc = NULL; - for( s = ' '; s <= sup && astOK; s++ ){ - is = s - 'A' + 1; - -/* Assume the Frame can be created succesfully. */ - ok = 1; - -/* Save the number of wcs axes */ - val = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - naxis = (int) ( val + 0.5 ); - SetValue( this, FormatKey( "WCSAXES", -1, -1, s, status ), - &naxis, AST__INT, "Number of WCS axes", status ); - } else { - naxis = GetMaxJM( &(store->crpix), s, status ) + 1; - } - -/* PC matrix: - --------- */ - -/* This encoding does not allow the PC matrix to be specified for each - version - instead they all share the primary PC matrix. Therefore we - need to check that all versions can use the primary PC matrix. Allocate - memory to hold the PC matrix for this version. */ - matrix = (double *) astMalloc( sizeof(double)*naxis*naxis ); - if( matrix ){ - -/* Fill these array with the values supplied in the FitsStore. */ - c = matrix; - for( i = 0; i < naxis; i++ ){ - for( j = 0; j < naxis; j++ ){ - *c = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - if( *c == AST__BAD ) *c = ( i == j ) ? 1.0 : 0.0; - c++; - } - } - -/* If we are currently processing the primary axis description, take - a copy of the PC matrix. */ - if( s == ' ' ) { - primpc = (double *) astStore( NULL, (void *) matrix, - sizeof(double)*naxis*naxis ); - -/* Store each matrix element in turn. */ - c = matrix; - for( i = 0; i < naxis; i++ ){ - for( j = 0; j < naxis; j++ ){ - -/* Set the element bad if it takes its default value. */ - val = *(c++); - if( i == j ){ - if( astEQUAL( val, 1.0 ) ) val = AST__BAD; - } else { - if( astEQUAL( val, 0.0 ) ) val = AST__BAD; - } - -/* Only store elements which do not take their default values. */ - if( val != AST__BAD ){ - sprintf( keyname, "PC%.3d%.3d", i + 1, j + 1 ); - SetValue( this, keyname, &val, AST__FLOAT, NULL, status ); - } - } - } - -/* For secondary axis descriptions, a check is made that the PC values are - the same as the primary PC values stored earlier. If not, the current - Frame cannot be stored as a secondary axis description so continue on - to the next Frame. */ - } else { - if( primpc ){ - c = matrix; - d = primpc; - for( i = 0; i < naxis; i++ ){ - for( j = 0; j < naxis; j++ ){ - if( !astEQUAL( *c, *d ) ){ - ok = 0; - } else { - c++; - d++; - } - } - } - -/* Continue with the next Frame if the PC matrix for this Frame is different - to the primary PC matrix. */ - if( !ok ) goto next; - } - } - matrix = (double *) astFree( (void *) matrix ); - } - -/* CDELT: - ------ */ - for( i = 0; i < naxis; i++ ){ - val = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - goto next; - } - sprintf( combuf, "Pixel scale on axis %d", i + 1 ); - if( s == ' ' ) { - sprintf( keyname, "CDELT%d", i + 1 ); - } else { - sprintf( keyname, "C%dELT%d", is, i + 1 ); - } - SetValue( this, keyname, &val, AST__FLOAT, combuf, status ); - } - -/* CRPIX: - ------ */ - for( j = 0; j < naxis; j++ ){ - val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - goto next; - } - sprintf( combuf, "Reference pixel on axis %d", j + 1 ); - if( s == ' ' ) { - sprintf( keyname, "CRPIX%d", j + 1 ); - } else { - sprintf( keyname, "C%dPIX%d", is, j + 1 ); - } - SetValue( this, keyname, &val, AST__FLOAT, combuf, status ); - } - -/* CRVAL: - ------ */ - for( i = 0; i < naxis; i++ ){ - val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - goto next; - } - sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); - if( s == ' ' ) { - sprintf( keyname, "CRVAL%d", i + 1 ); - } else { - sprintf( keyname, "C%dVAL%d", is, i + 1 ); - } - SetValue( this, keyname, &val, AST__FLOAT, combuf, status ); - } - -/* CTYPE: - ------ */ - for( i = 0; i < naxis; i++ ){ - cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - nc = strlen( cval ); - if( !cval || ( nc > 4 && !strcmp( cval + 4, "-TAB" ) ) ) { - ok = 0; - goto next; - } - comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); - if( !comm ) { - sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); - comm = combuf; - } - if( s == ' ' ) { - sprintf( keyname, "CTYPE%d", i + 1 ); - } else { - sprintf( keyname, "C%dYPE%d", is, i + 1 ); - } - -/* FITS-PC cannot handle celestial axes of type "xxLT" or "xxLN". - Neither can it handle the "-TAB". */ - if( ( nc > 2 && !strncmp( cval + 2, "LT-", 3 ) ) || - ( nc > 2 && !strncmp( cval + 2, "LN-", 3 ) ) || - ( nc > 4 && !strncmp( cval + 4, "-TAB", 4 ) ) ){ - ok = 0; - goto next; - } - -/* Extract the projection type as specified by the last 4 characters - in the CTYPE keyword value. This will be AST__WCSBAD for non-celestial - axes. */ - prj = astWcsPrjType( cval + 4 ); - -/* Change the new SFL projection code to to the older equivalent GLS */ - if( prj == AST__SFL ) { - strcpy( type, cval ); - (void) strcpy( type + 4, "-GLS" ); - cval = type; - } - -/* FITS-PC cannot handle the AST-specific TPN projection. */ - if( prj == AST__TPN ) { - ok = 0; - goto next; - } - -/* Store the CTYPE value */ - SetValue( this, keyname, &cval, AST__STRING, comm, status ); - } - -/* Get and save CUNIT for all intermediate axes. These are NOT required, so - do not pass on if they are not available. */ - for( i = 0; i < naxis; i++ ){ - cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); - if( cval ) { - sprintf( combuf, "Units for axis %d", i + 1 ); - if( s == ' ' ) { - sprintf( keyname, "CUNIT%d", i + 1 ); - } else { - sprintf( keyname, "C%dNIT%d", is, i + 1 ); - } - SetValue( this, keyname, &cval, AST__STRING, combuf, status ); - } - } - -/* Get and save RADESYS. This is NOT required, so do not pass on if it is - not available. If RADECSYS is provided for a secondary axis, it must - be the same as the primary axis RADECSYS value. If it is not, pass on to - the next Frame. */ - cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); - if( cval ) { - if( s == ' ' ) { - strcpy( primsys, cval ); - SetValue( this, "RADECSYS", &cval, AST__STRING, - "Reference frame for RA/DEC values", status ); - } else if( strcmp( cval, primsys ) ) { - ok = 0; - goto next; - } - } - -/* Reference equinox. This is NOT required, so do not pass on if it is - not available. If equinox is provided for a secondary axis, it must - be the same as the primary axis equinox value. If it is not, pass on to - the next Frame. */ - val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); - if( s == ' ' ) { - primeq = val; - if( val != AST__BAD ) SetValue( this, "EQUINOX", &val, AST__FLOAT, - "Epoch of reference equinox", status ); - } else if( !astEQUAL( val, primeq ) ){ - ok = 0; - goto next; - } - -/* Latitude of native north pole. This is NOT required, so do not pass on - if it is not available. If latpole is provided for a secondary axis, it - must be the same as the primary axis value. If it is not, pass on to - the next Frame. */ - val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ); - if( s == ' ' ) { - primlt = val; - if( val != AST__BAD ) SetValue( this, "LATPOLE", &val, AST__FLOAT, - "Latitude of native north pole", status ); - } else if( !EQUALANG( val, primlt ) ){ - ok = 0; - goto next; - } - -/* Longitude of native north pole. This is NOT required, so do not pass on - if it is not available. If lonpole is provided for a secondary axis, it - must be the same as the primary axis value. If it is not, pass on to - the next Frame. */ - val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); - if( s == ' ' ) { - primln = val; - if( val != AST__BAD ) SetValue( this, "LONGPOLE", &val, AST__FLOAT, - "Longitude of native north pole", status ); - } else if( !EQUALANG( val, primln ) ){ - ok = 0; - goto next; - } - -/* Date of observation. This is NOT required, so do not pass on if it is - not available. If mjd-obs is provided for a secondary axis, it must be - the same as the primary axis value. If it is not, pass on to the next - Frame. */ - val = GetItem( &(store->mjdobs), 0, 0, s, NULL, method, class, status ); - if( s == ' ' ) { - primdt = val; - if( val != AST__BAD ) { - SetValue( this, "MJD-OBS", &val, AST__FLOAT, - "Modified Julian Date of observation", status ); - -/* The format used for the DATE-OBS keyword depends on the value of the - keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. - Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ - palCaldj( 99, 1, 1, &mjd99, &jj ); - if( val < mjd99 ) { - palDjcal( 0, val, iymdf, &jj ); - sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], - iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); - } else { - palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); - palDd2tf( 3, fd, sign, ihmsf ); - sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", - iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], - ihmsf[2], ihmsf[3] ); - } - -/* Now store the formatted string in the FitsChan. */ - cval = combuf; - SetValue( this, "DATE-OBS", &cval, AST__STRING, - "Date of observation", status ); - } - } else if( !astEQUAL( val, primdt ) ){ - ok = 0; - goto next; - } - -/* Look for the celestial and spectral axes. */ - FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); - -/* If both longitude and latitude axes are present ...*/ - if( axlon >= 0 && axlat >= 0 ) { - -/* Get the CTYPE values for the latitude axis. */ - cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); - -/* Extract the projection type as specified by the last 4 characters - in the CTYPE keyword value. */ - prj = ( cval ) ? astWcsPrjType( cval + 4 ) : AST__WCSBAD; - -/* Projection parameters. If provided for a secondary axis, they must be - the same as the primary axis value. If it is not, pass on to the next - Frame. PC encoding ignores parameters associated with the longitude - axis. The old PC TAN projection did not have any parameters. - Pass on if a TAN projection with parameters is found. The number of - parameters was limited to 10. Pass on if more than 10 are supplied. */ - maxm = GetMaxJM( &(store->pv), ' ', status ); - for( i = 0; i < naxis; i++ ){ - if( i != axlon ) { - for( m = 0; m <= maxm; m++ ){ - val = GetItem( &(store->pv), i, m, s, NULL, method, class, status ); - if( s == ' ' ){ - if( val != AST__BAD ) { - if( i != axlat || prj == AST__TAN || m >= 10 ){ - ok = 0; - goto next; - } else { - SetValue( this, FormatKey( "PROJP", m, -1, ' ', status ), &val, - AST__FLOAT, "Projection parameter", status ); - } - } - if( i == axlat && m < 10 ) primpv[m] = val; - } else { - if( ( ( i != axlat || m >= 10 ) && val != AST__BAD ) || - ( i == axlat && m < 10 && !astEQUAL( val, primpv[m] ) ) ){ - ok = 0; - goto next; - } - } - } - } - } - } - -/* See if a Frame was sucessfully written to the FitsChan. */ -next: - ok = ok && astOK; - -/* If so, indicate we have something to return. */ - if( ok ) ret = 1; - -/* Clear any error status so we can continue to produce the next Frame. - Retain the error if the primary axes could not be produced. After the - primary axes, do the A axes. */ - if( s != ' ' ) { - astClearStatus; - } else { - s = 'A' - 1; - } - -/* Remove the secondary "new" flags from the FitsChan. This flag is - associated with cards which have been added to the FitsChan during - this pass through the main loop in this function. If the Frame was - written out succesfully, just clear the flags. If anything went wrong - with this Frame, remove the flagged cards from the FitsChan. */ - FixNew( this, NEW2, !ok, method, class, status ); - -/* Set the current card so that it points to the last WCS-related keyword - in the FitsChan (whether previously read or not). */ - FindWcs( this, 1, 1, 0, method, class, status ); - } - -/* Annul the array holding the primary PC matrix. */ - primpc = (double *) astFree( (void *) primpc ); - -/* Return zero or ret depending on whether an error has occurred. */ - return astOK ? ret : 0; -} - -static void PreQuote( const char *value, - char string[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ], int *status ) { - -/* -* Name: -* PreQuote - -* Purpose: -* Pre-quote FITS character data. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void PreQuote( const char *value, -* char string[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ] ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function processes a string value in such a way that it can -* be stored as a FITS character value (associated with a keyword) -* and later retrieved unchanged, except for possible truncation. -* -* This pre-processing is necessary because FITS does not regard -* trailing white space as significant, so it is lost. This -* function adds double quote (") characters around the string if -* it is necessary in order to prevent this loss. These quotes are -* also added to zero-length strings and to strings that are -* already quoted (so that the original quotes are not lost when -* they are later un-quoted). -* -* This function will silently truncate any string that is too long -* to be stored as a FITS character value, but will ensure that the -* maximum number of characters are retained, taking account of any -* quoting required. - -* Parameters: -* value -* Pointer to a constant null-terminated string containing the -* input character data to be quoted. All white space is -* significant. -* string -* A character array into which the result string will be -* written, with a terminating null. The maximum number of -* characters from the input string that can be accommodated in -* this is (AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 4), but this -* will be reduced if quoting is necessary. - -* Notes: -* - The UnPreQuote function should be used to reverse the effect -* of this function on a string (apart from any truncation). -*/ - -/* Local Variables: */ - int dq; /* Number of double quotes needed */ - int dquotes; /* Final number of double quotes */ - int i; /* Loop counter for input characters */ - int j; /* Counter for output characters */ - int nc; /* Number of characters to be accommodated */ - int sq; /* Number of single quotes needed */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Initialise, setting the default number of double quotes (which - applies to a zero-length string) to 2. */ - dquotes = 2; - nc = 0; - sq = 0; - -/* Loop to consider each input character to see if it will fit into - the result string. */ - for ( i = 0; value[ i ]; i++ ) { - -/* If a single quote character is to be included, count it. When the - string is encoded as FITS character data, these quotes will be - doubled, so will increase the overall string length by one. */ - if ( value[ i ] == '\'' ) sq++; - -/* See how many double quotes are needed around the string (0 or - 2). These are needed if there is trailing white space that needs - protecting (this is not significant in FITS and will be removed), - or if the string already has quotes at either end (in which case an - extra set is needed to prevent the original ones being removed when - it is later un-quoted). Note we do not need to double existing - double quote characters within the string, because the position of - the ends of the string are known (from the quoting supplied by - FITS) so only the first and last characters need be inspected when - un-quoting the string. - In assessing the number of double quotes, assume the string will be - truncated after the current character. */ - dq = ( isspace( value[ i ] ) || - ( ( value[ 0 ] == '"' ) && ( value[ i ] == '"' ) ) ) ? 2 : 0; - -/* See if the length of the resulting string, including the current - character and all necessary quotes, is too long. If so, give up - here. */ - if ( ( nc + 1 + dq + sq ) > - ( AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 4 ) ) break; - -/* If the string is not too long, accept the character and note the - number of double quotes needed. */ - nc = i + 1; - dquotes = dq; - } - -/* If double quotes are needed, insert the opening quote into the - output string. */ - j = 0; - if ( dquotes ) string[ j++ ] = '"'; - -/* Follow this with the maximum number of input string characters that - can be accommodated. */ - for ( i = 0; i < nc; i++ ) string[ j++ ] = value[ i ]; - -/* Append the closing quote if necessary and terminate the output - string. */ - if ( dquotes ) string[ j++ ] = '"'; - string[ j ] = '\0'; -} - -static void PurgeWCS( AstFitsChan *this, int *status ){ - -/* -*++ -* Name: -c astPurgeWCS -f AST_PURGEWCS - -* Purpose: -* Delete all cards in the FitsChan describing WCS information. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" -c void astPurgeWCS( AstFitsChan *this ) -f CALL AST_PURGEWCS( THIS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* deletes all cards in a FitsChan that relate to any of the recognised -* WCS encodings. On exit, the current card is the first remaining card -* in the FitsChan. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. -*-- -*/ - -/* Local Variables: */ - AstObject *obj; - int oldclean; - -/* Check the global status. */ - if( !astOK ) return; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Ensure the Clean attribute is set so that WCS keywords are removed - even if an error occurs. */ - if( astTestClean( this ) ) { - oldclean = astGetClean( this ); - astSetClean( this, 1 ); - } else { - astSetClean( this, 1 ); - oldclean = -1; - } - -/* Loop round attempting to read AST objects form the FitsChan. This will - flag cards as used that are involved in the creation of these object - (including NATIVE encodings). Ignore any error that ocurs whilst doing - this. */ - astClearCard( this ); - if( astOK ) { - int oldreporting = astReporting( 0 ); - obj = astRead( this ); - while( obj ) { - obj = astAnnul( obj ); - astClearCard( this ); - obj = astRead( this ); - } - if( !astOK ) astClearStatus; - astReporting( oldreporting ); - } - -/* We now loop round to remove any spurious WCS-related cards left in the - FitsChan that did not form part of a complete WCS encoding. Find the - first WCS-related card left in the FitsChan. */ - FindWcs( this, 0, 0, 1, "DeleteWcs", "FitsChan", status ); - -/* Loop round marking each WCS-related card as used until none are left */ - while( this->card && astOK ) { - -/* Mark the current card as having been read. */ - ( (FitsCard*) this->card )->flags = USED; - -/* Find the next WCS-related card. */ - FindWcs( this, 0, 0, 0, "DeleteWcs", "FitsChan", status ); - } - -/* Rewind the FitsChan. */ - astClearCard( this ); - -/* Reset the Clean attribute. */ - if( oldclean == -1 ) { - astClearClean( this ); - } else { - astSetClean( this, oldclean ); - } - -} - -static void PutCards( AstFitsChan *this, const char *cards, int *status ) { - -/* -*++ -* Name: -c astPutCards -f AST_PUTCARDS - -* Purpose: -* Store a set of FITS header cards in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c void astPutCards( AstFitsChan *this, const char *cards ) -f CALL AST_PUTCARDS( THIS, CARDS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* stores a set of FITS header cards in a FitsChan. The cards are -* supplied concatenated together into a single character string. -* Any existing cards in the FitsChan are removed before the new cards -* are added. The FitsChan is "re-wound" on exit by clearing its Card -* attribute. This means that a subsequent invocation of -c astRead -f AST_READ -* can be made immediately without the need to re-wind the FitsChan -* first. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c cards -f CARDS = CHARACTER * ( * ) (Given) -c Pointer to a null-terminated character string -f A character string -* containing the FITS cards to be stored. Each individual card -* should occupy 80 characters in this string, and there should be -* no delimiters, new lines, etc, between adjacent cards. The final -* card may be less than 80 characters long. -c This is the format produced by the fits_hdr2str function in the -c CFITSIO library. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - An error will result if the supplied string contains any cards -* which cannot be interpreted. -*-- -*/ - -/* Local Variables: */ - const char *a; /* Pointer to start of next card */ - int clen; /* Length of supplied string */ - int i; /* Card index */ - int ncard; /* No. of cards supplied */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Empty the FitsChan. */ - astEmptyFits( this ); - -/* Loop round the supplied string in 80 character segments, inserting - each segment into the FitsChan as a header card. Allow the last card - to be less than 80 characters long. */ - clen = strlen( cards ); - ncard = clen/80; - if( ncard*80 < clen ) ncard++; - a = cards; - for( i = 0; i < ncard; i++, a += 80 ) astPutFits( this, a, 1 ); - -/* Rewind the FitsChan. */ - astClearCard( this ); -} - -static void PutFits( AstFitsChan *this, const char card[ AST__FITSCHAN_FITSCARDLEN + 1 ], - int overwrite, int *status ){ - -/* -*++ -* Name: -c astPutFits -f AST_PUTFITS - -* Purpose: -* Store a FITS header card in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c void astPutFits( AstFitsChan *this, const char card[ 80 ], -c int overwrite ) -f CALL AST_PUTFITS( THIS, CARD, OVERWRITE, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function stores a FITS header card in a FitsChan. The card -f This routine stores a FITS header card in a FitsChan. The card -* is either inserted before the current card (identified by the -* Card attribute), or over-writes the current card, as required. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c card -f CARD = CHARACTER * ( 80 ) (Given) -c Pointer to a possibly null-terminated character string -c containing the FITS card to be stored. No more than 80 -c characters will be used from this string (or fewer if a null -c occurs earlier). -f A character string string containing the FITS card to be -f stored. No more than 80 characters will be used from this -f string. -c overwrite -f OVERWRITE = LOGICAL (Given) -c If this value is zero, the new card is inserted in front of -f If this value is .FALSE., the new card is inserted in front of -* the current card in the FitsChan (as identified by the -c initial value of the Card attribute). If it is non-zero, the -f initial value of the Card attribute). If it is .TRUE., the -* new card replaces the current card. In either case, the Card -* attribute is then incremented by one so that it subsequently -* identifies the card following the one stored. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - If the Card attribute initially points at the "end-of-file" -* (i.e. exceeds the number of cards in the FitsChan), then the new -* card is appended as the last card in the FitsChan. -* - An error will result if the supplied string cannot be interpreted -* as a FITS header card. -*-- -*/ - -/* Local Variables: */ - char *comment; /* The keyword comment */ - char *name; /* The keyword name */ - char *value; /* The keyword value */ - const char *class; /* Object class */ - const char *method; /* Current method */ - double cfval[2]; /* Complex floating point keyword value */ - double fval; /* floating point keyword value */ - int cival[2]; /* Complex integer keyword value */ - int ival; /* Integer keyword value */ - int len; /* No. of characters to read from the value string */ - int nc; /* No. of characters read from value string */ - int type; /* Keyword data type */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Store the current method, and the class of the supplied object for use - in error messages.*/ - method = "astPutFits"; - class = astGetClass( this ); - -/* Split the supplied card up into name, value and commment strings, and - get pointers to local copies of them. The data type associated with the - keyword is returned. */ - type = Split( this, card, &name, &value, &comment, method, class, status ); - -/* Check that the pointers can be used. */ - if( astOK ){ - -/* Initialise the number of characters read from the value string. */ - nc = 0; - -/* Store the number of characters in the value string. */ - len = strlen( value ); - -/* Read and store floating point values from the value string. NB, this - list is roughly in the order of descreasing frequency of use (i.e. - most FITS keywords are simple floating point values, the next most - common are strings, etc). */ - if( type == AST__FLOAT ){ - if( 1 == astSscanf( value, " %lf %n", &fval, &nc ) && nc >= len ){ - astSetFitsF( this, name, fval, comment, overwrite ); - } else { - astError( AST__BDFTS, "%s(%s): Unable to read a floating point " - "FITS keyword value.", status, method, class ); - } - -/* Read and store string values from the value string. */ - } else if( type == AST__STRING ){ - astSetFitsS( this, name, value, comment, overwrite ); - -/* Read and store string values from the value string. */ - } else if( type == AST__CONTINUE ){ - astSetFitsCN( this, name, value, comment, overwrite ); - -/* Store comment card. */ - } else if( type == AST__COMMENT ){ - astSetFitsCom( this, name, comment, overwrite ); - -/* Read and store integer values from the value string. */ - } else if( type == AST__INT ){ - if( 1 == astSscanf( value, " %d %n", &ival, &nc ) && nc >= len ){ - astSetFitsI( this, name, ival, comment, overwrite ); - } else { - astError( AST__BDFTS, "%s(%s): Unable to read an integer FITS " - "keyword value.", status, method, class ); - } - -/* Read and store logical values from the value string. */ - } else if( type == AST__LOGICAL ){ - astSetFitsL( this, name, (*value == 'T'), comment, overwrite ); - -/* Read and store undefined values from the value string. */ - } else if( type == AST__UNDEF ){ - astSetFitsU( this, name, comment, overwrite ); - -/* Read and store complex floating point values from the value string. */ - } else if( type == AST__COMPLEXF ){ - if( 2 == astSscanf( value, " %lf %lf %n", cfval, cfval + 1, &nc ) && - nc >= len ){ - astSetFitsCF( this, name, cfval, comment, overwrite ); - } else { - astError( AST__BDFTS, "%s(%s): Unable to read a complex pair " - "of floating point FITS keyword values.", status, method, class ); - } - -/* Read and store complex integer values from the value string. */ - } else if( type == AST__COMPLEXI ){ - if( 2 == astSscanf( value, " %d %d %n", cival, cival + 1, &nc ) && - nc >= len ){ - astSetFitsCI( this, name, cival, comment, overwrite ); - } else { - astError( AST__BDFTS, "%s(%s): Unable to read a complex pair " - "of integer FITS keyword values.", status, method, class ); - } - -/* Report an error for any other type. */ - } else { - astError( AST__INTER, "%s: AST internal programming error - " - "FITS data-type '%d' not yet supported.", status, method, type ); - } - -/* Give a context message if an error occurred. */ - if( !astOK ){ - astError( astStatus, "%s(%s): Unable to store the following FITS " - "header card:\n%s\n", status, method, class, card ); - } - } - -/* Free the memory used to hold the keyword name, comment and value - strings. */ - (void) astFree( (void *) name ); - (void) astFree( (void *) comment ); - (void) astFree( (void *) value ); -} - -static void PutTable( AstFitsChan *this, AstFitsTable *table, - const char *extnam, int *status ) { - -/* -*++ -* Name: -c astPutTable -f AST_PUTTABLE - -* Purpose: -* Store a single FitsTable in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c void astPutTable( AstFitsChan *this, AstFitsTable *table, -c const char *extnam ) -f CALL AST_PUTTABLE( THIS, TABLE, EXTNAM, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* allows a representation of a single FITS binary table to be -* stored in a FitsChan. For instance, this may provide the coordinate -* look-up tables needed subequently when reading FITS-WCS headers -* for axes described using the "-TAB" algorithm. Since, in general, -* the calling application may not know which tables will be needed - -* if any - prior to calling -c astRead, the astTablesSource function -f AST_READ, the AST_TABLESOURCE routine -* provides an alternative mechanism in which a caller-supplied -* function is invoked to store a named table in the FitsChan. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c table -f TABLE = INTEGER (Given) -* Pointer to a FitsTable to be added to the FitsChan. If a FitsTable -* with the associated extension name already exists in the FitsChan, -* it is replaced with the new one. A deep copy of the FitsTable is -* stored in the FitsChan, so any subsequent changes made to the -* FitsTable will have no effect on the behaviour of the FitsChan. -c extnam -f EXTNAM = CHARACTER * ( * ) (Given) -* The name of the FITS extension associated with the table. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - Tables stored in the FitsChan may be retrieved using -c astGetTables. -f AST_GETTABLES. -c - The astPutTables method can add multiple FitsTables in a single call. -f - The AST_PUTTABLES method can add multiple FitsTables in a single call. -*-- -*/ - -/* Local Variables: */ - AstObject *ft; - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Create a KeyMap to hold the tables within the FitsChan, if this has not - already been done. */ - if( !this->tables ) this->tables = astKeyMap( " ", status ); - -/* Store a copy of the FitsTable in the FitsChan. */ - ft = astCopy( table ); - astMapPut0A( this->tables, extnam, ft, NULL ); - ft = astAnnul( ft ); -} - -static void PutTables( AstFitsChan *this, AstKeyMap *tables, int *status ) { - -/* -*++ -* Name: -c astPutTables -f AST_PUTTABLES - -* Purpose: -* Store one or more FitsTables in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c void astPutTables( AstFitsChan *this, AstKeyMap *tables ) -f CALL AST_PUTTABLES( THIS, TABLES, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* allows representations of one or more FITS binary tables to be -* stored in a FitsChan. For instance, these may provide the coordinate -* look-up tables needed subequently when reading FITS-WCS headers -* for axes described using the "-TAB" algorithm. Since, in general, -* the calling application may not know which tables will be needed - -* if any - prior to calling -c astRead, the astTablesSource function -f AST_READ, the AST_TABLESOURCE routine -* provides an alternative mechanism in which a caller-supplied -* function is invoked to store a named table in the FitsChan. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c tables -f TABLES = INTEGER (Given) -* Pointer to a KeyMap holding the tables that are to be added -* to the FitsChan. Each entry should hold a scalar value which is a -* pointer to a FitsTable to be added to the FitsChan. Any unusable -* entries are ignored. The key associated with each entry should be -* the name of the FITS binary extension from which the table was -* read. If a FitsTable with the associated key already exists in the -* FitsChan, it is replaced with the new one. A deep copy of each -* usable FitsTable is stored in the FitsChan, so any subsequent -* changes made to the FitsTables will have no effect on the -* behaviour of the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - Tables stored in the FitsChan may be retrieved using -c astGetTables. -f AST_GETTABLES. -* - The tables in the supplied KeyMap are added to any tables already -* in the FitsChan. -c - The astPutTable -f - The AST_PUTTABLE -* method provides a simpler means of adding a single table to a FitsChan. -*-- -*/ - -/* Local Variables: */ - AstObject *obj; - const char *key; - int ientry; - int nentry; - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Loop through all entries in the supplied KeyMap. */ - nentry = astMapSize( tables ); - for( ientry = 0; ientry < nentry; ientry++ ) { - key = astMapKey( tables, ientry ); - -/* Ignore entries that do not contain AST Object pointers, or are not - scalar. */ - if( astMapType( tables, key ) == AST__OBJECTTYPE && - astMapLength( tables, key ) == 1 ) { - -/* Get the pointer, amd ignore it if it is not a FitsTable. */ - astMapGet0A( tables, key, &obj ); - if( astIsAFitsTable( obj ) ) { - -/* Store it in the FitsChan. */ - astPutTable( this, (AstFitsTable *) obj, key ); - } - -/* Annul the Object pointer. */ - obj = astAnnul( obj ); - } - } -} - -static AstObject *Read( AstChannel *this_channel, int *status ) { -/* -* Name: -* Read - -* Purpose: -* Read an Object from a Channel. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstObject *Read( AstChannel *this_channel, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the astRead method -* inherited from the Channel class). - -* Description: -* This function reads an Object from a FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the new Object. This will always be a FrameSet. - -* Notes: -* - The pixel Frame is given a title of "Pixel Coordinates", and -* each axis in the pixel Frame is given a label of the form "Pixel -* axis <n>", where <n> is the axis index (starting at one). -* - The FITS CTYPE keyword values are used to set the labels for any -* non-celestial axes in the physical coordinate Frames, and the FITS -* CUNIT keywords are used to set the corresponding units strings. -* - On exit, the pixel Frame is the base Frame, and the physical -* Frame derived from the primary axis descriptions is the current Frame. -* - Extra Frames are added to hold any secondary axis descriptions. All -* axes within such a Frame refer to the same coordinate version ('A', -* 'B', etc). -* - For foreign encodings, the first card in the FitsChan must be -* the current card on entry (otherwise a NULL pointer is returned), -* and the FitsChan is left at end-of-file on exit. -* - For the Native encoding, reading commences from the current card -* on entry (which need not be the first in the FitsChan), and the -* current Card on exit is the first card following the last one read -* (or end-of-file). -*/ - -/* Local Variables: */ - AstObject *new; /* Pointer to returned Object */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - FitsStore *store; /* Intermediate storage for WCS information */ - const char *method; /* Pointer to string holding calling method */ - const char *class; /* Pointer to string holding object class */ - int encoding; /* The encoding scheme */ - int remove; /* Remove used cards? */ - -/* Initialise. */ - new = NULL; - -/* Check the global error status. */ - if ( !astOK ) return new; - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Store the calling method, and object class. */ - method = "astRead"; - class = astGetClass( this ); - -/* Get the encoding scheme used by the FitsChan. */ - encoding = astGetEncoding( this ); - -/* If we are reading from a FitsChan in which AST objects are encoded using - native AST-specific keywords, use the Read method inherited from the - Channel class. */ - if( encoding == NATIVE_ENCODING ){ - new = (*parent_read)( this_channel, status ); - -/* Indicate that used cards should be removed from the FitsChan. */ - remove = 1; - -/* If we are reading from a FitsChan in which AST objects are encoded using - any of the other supported encodings, the header may only contain a - single FrameSet. */ - } else { - remove = 0; - -/* Only proceed if the FitsChan is at start-of-file. */ - if( !astTestCard( this ) && astOK ){ - -/* Extract the required information from the FITS header into a standard - intermediary structure called a FitsStore. */ - store = FitsToStore( this, encoding, method, class, status ); - -/* Now create a FrameSet from this FitsStore. */ - new = FsetFromStore( this, store, method, class, status ); - -/* Release the resources used by the FitsStore. */ - store = FreeStore( store, status ); - -/* Indicate that used cards should be retained in the FitsChan. */ - remove = 0; - -/* If no object is being returned, rewind the fitschan in order to - re-instate the original current Card. */ - if( !new ) { - astClearCard( this ); - -/* Otherwise, ensure the current card is at "end-of-file". */ - } else { - astSetCard( this, INT_MAX ); - } - } - } - -/* If an error occurred, clean up by deleting the new Object and - return a NULL pointer. */ - if ( !astOK ) new = astDelete( new ); - -/* If no object is being returned, clear the "provisionally used" flags - associated with cards which were read. We do not do this if the user - wants to clean WCS cards from the FitsChan even if an error occurs. */ - if( !new && !astGetClean( this ) ) { - FixUsed( this, 0, 0, 0, method, class, status ); - -/* Otherwise, indicate that all the "provisionally used" cards have been - "definitely used". If native encoding was used, these cards are - totally removed from the FitsChan. */ - } else { - FixUsed( this, 0, 1, remove, method, class, status ); - } - -/* Return the pointer to the new Object. */ - return new; -} - -static double *ReadCrval( AstFitsChan *this, AstFrame *wcsfrm, char s, - const char *method, const char *class, int *status ){ - -/* -* Name: -* ReadCrval - -* Purpose: -* Obtain the reference point from the supplied FitsChan in the -* supplied WCS Frame. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* double *ReadCrval( AstFitsChan *this, AstFrame *wcsfrm, char s, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The original reference point in the "s" coordinate description is read -* from the CRVAL keywords in the supplied FitsChan, and the original -* FrameSet is re-read from the FitsChan. If possible, the reference -* position is then converted from the "s" coordinate description to the -* supplied WCS Frame, and a pointer to an array holding the axis -* values for the transformed reference point is returned. - -* Parameters: -* this -* The FitsChan. -* wcsfrm -* The WCS Frame in the FitsChan being written to. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a dynamically allocated array holding the reference -* point in the supplied WCS Frame. NULL is returned if is is not -* possible to determine the reference point for any reason (for -* instance, if the FitsChan does not contain values for the CRVAL -* keywords). -*/ - -/* Local Variables: */ - AstFitsChan *fc; /* A copy of the supplied FitsChan */ - AstFrame *tfrm; /* Temporary Frame pointer */ - AstFrameSet *fs; /* The FITS FrameSet */ - AstFrameSet *tfs; /* FrameSet connecting FITS and supplied WCS Frame */ - const char *id; /* Pointer to Object "Id" string */ - char buf[ 11 ]; /* FITS keyword template buffer */ - double *crval; /* CRVAL keyword values in supplied FitsChan */ - double *ret; /* Returned array */ - int hii; /* Highest found FITS axis index */ - int iax; /* Axis index (zero based) */ - int ifr; /* Frames index */ - int loi; /* Lowest found FITS axis index */ - int nax; /* Axis count */ - int nfr; /* No. of Frames in FITS FrameSet */ - int ok; /* Were CRVAL values found? */ - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* We want to re-create the original FrameSet represented by the original - contents of the supplied FitsChan. Some of the contents of the - FitsChan will already have been marked as "having been read" and so - will be ignored if we attempt to read a FrameSet directly from the - supplied FitsChan. Therefore we take a deep copy of the supplied - FitsChan and clear all the "previusly read" flags in the copy. */ - fc = astCopy( this ); - astClearEncoding( fc ); - FixUsed( fc, 1, 0, 0, method, class, status ); - -/* Copy the CRVAL values for the "s" axis descriptions into a dynamically - allocated array ("crval"). */ - if( s == ' ' ) { - strcpy( buf, "CRVAL%d" ); - } else { - sprintf( buf, "CRVAL%%d%c", s ); - } - if( astKeyFields( fc, buf, 1, &hii, &loi ) > 0 ) { - crval = astMalloc( sizeof( double )*(size_t) hii ); - ok = 1; - for( iax = 0; iax < hii; iax++ ){ - ok = ok && GetValue( fc, FormatKey( "CRVAL", iax + 1, -1, s, status ), - AST__FLOAT, (void *) (crval + iax), 0, 0, method, - class, status ); - } - } else { - crval = NULL; - ok = 0; - } - -/* If the CRVAL values were obtained succesfully, attempt to read a FrameSet - from the FitsChan copy. Do it in a new error report context so that we - can annull any error when the FrameSet is read. */ - if( ok ) { - int oldreporting = astReporting( 0 ); - astClearCard( fc ); - fs = astRead( fc ); - if( fs ) { - -/* We want to find a conversion from the Frame in this FrameSet which - represents the FITS-WCS "s" coordinate descriptions and the supplied WCS - Frame. So first find the Frame which has its Ident attribute set to - "s" and make it the current Frame. */ - nfr = astGetNframe( fs ); - for( ifr = 1; ifr <= nfr; ifr++ ) { - astSetCurrent( fs, ifr ); - tfrm = astGetFrame( fs, ifr ); - id = astTestIdent( tfrm ) ? astGetIdent( tfrm ) : NULL; - tfrm = astAnnul( tfrm ); - if( id && strlen( id ) == 1 && id[ 0 ] == s ) break; - } - -/* Check a Frame was found, and that we have CRVAL values for all axes in - the Frame. */ - if( ifr <= nfr && astGetNaxes( fs ) == hii ) { - -/* Attempt to find a conversion route from the Frame found above to the - supplied WCS Frame. */ - tfs = astConvert( fs, wcsfrm, astGetDomain( wcsfrm ) ); - if( tfs ) { - -/* Allocate memory to hold the returned reference point. */ - nax = astGetNaxes( wcsfrm ); - ret = astMalloc( sizeof( double )*(size_t) nax ); - -/* Transform the original reference position from the "s" Frame to the - supplied WCS Frame using the Mapping returned by astConvert. */ - astTranN( tfs, 1, hii, 1, crval, 1, nax, 1, ret ); - -/* Free resources. */ - tfs = astAnnul( tfs ); - } - } - -/* Free resources. */ - fs = astAnnul( fs ); - -/* Annul any error that occurred reading the FitsChan. */ - } else if( !astOK ) { - astClearStatus; - } - -/* Re-instate error reporting. */ - astReporting( oldreporting ); - } - -/* Free resources. */ - if( crval ) crval = astFree( crval ); - fc = astAnnul( fc ); - -/* If an error occurred, free the returned array. */ - if( !astOK ) ret = astFree( ret ); - -/* Return the result. */ - return ret; -} - -static void ReadFits( AstFitsChan *this, int *status ){ - -/* -*++ -* Name: -c astReadFits -f AST_READFITS - -* Purpose: -* Read cards into a FitsChan from the source function. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" -c void astReadFits( AstFitsChan *this ) -f CALL AST_READFITS( THIS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* reads cards from the source function that was specified when the -* FitsChan was created, and stores them in the FitsChan. This -* normally happens once-only, when the FitsChan is accessed for the -* first time. -c This function -f This routine -* provides a means of forcing a re-read of the external source, and -* may be useful if (say) new cards have been deposited into the -* external source. Any newcards read from the source are appended to -* the end of the current contents of the FitsChan. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - This function returns without action if no source function was -* specified when the FitsChan was created. -* - The SourceFile attribute is ignored by this -c function. -f routine. -* New cards are read from the source file whenever a new value is -* assigned to the SourceFile attribute. - -*-- -*/ - -/* Check the inherited status */ - if( !astOK ) return; - -/* If no source function is available, re-instate any saved source - function pointer. */ - if( !this->source ) { - this->source = this->saved_source; - this->saved_source = NULL; - } - -/* Call the source function. */ - ReadFromSource( this, status ); -} - -static void ReadFromSource( AstFitsChan *this, int *status ){ - -/* -* Name: -* ReadFromSource - -* Purpose: -* Fill the FitsChan by reading cards from the source function. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void ReadFromSource( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The source function specified when the FitsChan was created is -* called repeatedly until it returns a NULL pointer. The string -* returned by each such call is assumed to be a FITS header card, -* and is stored in the FitsChan using astPutFits. -* -* If no source function was provided, the FitsChan is left as supplied. -* This is different to a standard Channel, which tries to read data -* from standard input if no source function is provided. -* -* This function should be called at the start of most public or protected -* FitsChan functions, and most private functions that are used to override -* methods inherited form the Channel class. Previously, this function -* was called only once, from the FitsChan initialiser (astInitFitschan). -* However, calling it from astInitFitsChan means that application code -* cannot use the astPutChannelData function with a FitsChan, since the -* source function would already have been called by the time the -* FitsChan constructor returned (and thus before astPutChannelData -* could have been called). In order to ensure that the source -* function is called only once, this function now nullifies the source -* function pointer after its first use. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Notes: -* - The new cards are appended to the end of the FitsChan. -* - The first of the new cards is made the current card on exit. If no -* source function is supplied, the current card is left unchanged. -*/ - -/* Local Variables: */ - const char *(* source)( void ); /* Pointer to source function */ - const char *card; /* Pointer to externally-read header card */ - int icard; /* Current card index on entry */ - -/* Check the global status. */ - if( !astOK || !this ) return; - -/* Only proceed if source function and wrapper were supplied when the FitsChan - was created and are still available. */ - if( this->source && this->source_wrap ){ - -/* Save the source function pointer and then nullify the pointer in the - FitsChan structure. This avoids infinte loops. */ - source = this->source; - this->source = NULL; - -/* Save the source fubnction pointer in the FitsChan so that it can be - re-instated if required (e.g. by astReadFits). */ - this->saved_source = source; - -/* Ensure the FitsChan is at end-of-file. This will result in the - new cards being appended to the end of the FitsChan. */ - astSetCard( this, INT_MAX ); - -/* Store the current card index. */ - icard = astGetCard( this ); - -/* Obtain the first header card from the source function. This is an - externally supplied function which may not be thread-safe, so lock a - mutex first. Also store the channel data pointer in a global variable - so that it can be accessed in the source function using macro - astChannelData. */ - astStoreChannelData( this ); - LOCK_MUTEX2; - card = ( *this->source_wrap )( source, status ); - UNLOCK_MUTEX2; - -/* Loop until a NULL pointer is returned by the source function, or an - error occurs. */ - while( card && astOK ){ - -/* Store the card in the FitsChan. */ - astPutFits( this, card, 0 ); - -/* Free the memory holding the header card. */ - card = (char *) astFree( (void *) card ); - -/* Obtain the next header card. Also store the channel data pointer in a - global variable so that it can be accessed in the source function using - macro astChannelData. */ - astStoreChannelData( this ); - LOCK_MUTEX2; - card = ( *this->source_wrap )( source, status ); - UNLOCK_MUTEX2; - } - -/* Set the current card index so that the first of the new cards will be the - next card to be read from the FitsChan. */ - astSetCard( this, icard ); - } -} - -static void RemoveTables( AstFitsChan *this, const char *key, int *status ){ - -/* -*++ -* Name: -c astRemoveTables -f AST_REMOVETABLES - -* Purpose: -* Remove one or more tables from a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c void astRemoveTables( AstFitsChan *this, const char *key ) -f CALL AST_REMOVETABLES( THIS, KEY, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* removes the named tables from the FitsChan, it they exist (no error -* is reported if any the tables do not exist). - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c key -f KEY = CHARACTER * ( * ) (Given) -* The key indicating which tables to exist. A single key or a -* comma-separated list of keys can be supplied. If a blank string -* is supplied, all tables are removed. -f STATUS = INTEGER (Given and Returned) -f The global status. -*-- -*/ - -/* Local variables: */ - char **words; - int itable; - int ntable; - -/* Return if the global error status has been set, or the FitsChan - contains no tables KeyMap. */ - if( !astOK || !this->tables ) return; - -/* If the string is blank, remove all tables. */ - if( astChrLen( key ) == 0 ) { - ntable = astMapSize( this->tables ); - for( itable = 0; itable < ntable; itable++ ) { - astMapRemove( this->tables, astMapKey( this->tables, itable ) ); - } - -/* Otherwise, split the supplied comma-separated string up into individual - items. */ - } else { - words = astChrSplitC( key, ',', &ntable ); - -/* Attempt to remove each one, and then free the string. */ - if( astOK ) { - for( itable = 0; itable < ntable; itable++ ) { - astMapRemove( this->tables, words[ itable ] ); - words[ itable ] = astFree( words[ itable ] ); - } - } - -/* Free the list. */ - words = astFree( words ); - } -} - -static void RetainFits( AstFitsChan *this, int *status ){ - -/* -*++ -* Name: -c astRetainFits -f AST_RETAINFITS - -* Purpose: -* Indicate that the current card in a FitsChan should be retained. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" -c void astRetainFits( AstFitsChan *this ) -f CALL AST_RETAINFITS( THIS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* stores a flag with the current card in the FitsChan indicating that -* the card should not be removed from the FitsChan when an Object is -* read from the FitsChan using -c astRead. -f AST_READ. -* -* Cards that have not been flagged in this way are removed when a -* read operation completes succesfully, but only if the card was used -* in the process of creating the returned AST Object. Any cards that -* are irrelevant to the creation of the AST Object are retained whether -* or not they are flagged. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - This function returns without action if the FitsChan is -* initially positioned at the "end-of-file" (i.e. if the Card -* attribute exceeds the number of cards in the FitsChan). -* - The current card is not changed by this function. -*-- -*/ - -/* Local variables: */ - int flags; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Return if the global error status has been set, or the current card - is not defined. */ - if( !astOK || !this->card ) return; - -/* Set the PROTECTED flag in the current card. */ - flags = ( (FitsCard *) this->card )->flags; - ( (FitsCard *) this->card )->flags = flags | PROTECTED; -} - -static void RoundFString( char *text, int width, int *status ){ -/* -* Name: -* RoundString - -* Purpose: -* Modify a formatted floating point number to round out long -* sequences of zeros or nines. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void RoundFString( char *text, int width ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The supplied string is assumed to be a valid decimal representation of -* a floating point number. It is searched for sub-strings consisting -* of NSEQ or more adjacent zeros, or NSEQ or more adjacent nines. If found -* the string is modified to represent the result of rounding the -* number to remove the sequence of zeros or nines. - -* Parameters: -* text -* The formatted number. Modified on exit to round out long -* sequences of zeros or nines. The returned string is right justified. -* width -* The minimum field width to use. The value is right justified in -* this field width. Ignored if zero. -*/ - -/* Local Constants: */ -#define NSEQ 4 /* No. of adjacent 0's or 9's to produce rounding */ - -/* Local Variables: */ - char *a; - char *c; - char *dot; - char *exp; - char *last; - char *start; - char *end; - int i; - int neg; - int nnine; - int nonzero; - int nzero; - int replace; - int started; - int len; - int bu; - int nls; - -/* Check the inherited status. */ - if( !astOK ) return; - -/* Save the original length of the text. */ - len = strlen( text ); - -/* Locate the start of any exponent string. */ - exp = strpbrk( text, "dDeE" ); - -/* First check for long strings of adjacent zeros. - =============================================== */ - -/* Indicate that we have not yet found a decimal point in the string. */ - dot = NULL; - -/* The "started" flag controls whether *leading* zeros should be removed - if there are more than NSEQ of them. They are only removed if there is an - exponent. */ - started = ( exp != NULL ); - -/* We are not currently replacing digits with zeros. */ - replace = 0; - -/* We have not yet found any adjacent zeros. */ - nzero = 0; - -/* We have no evidence yet that the number is non-zero. */ - nonzero = 0; - -/* Loop round the supplied text string. */ - c = text; - while( *c && c != exp ){ - -/* If this is a zero, increment the number of adjacent zeros found, so - long as we have previously found a non-zero digit (or there is an - exponent). If this is the NSEQ'th adjacent zero, indicate that - subsequent digits should be replaced by zeros. */ - if( *c == '0' ){ - if( started && ++nzero >= NSEQ ) replace = 1; - -/* Note if the number contains a decimal point. */ - } else if( *c == '.' ){ - dot = c; - -/* If this character is a non-zero digit, indicate that we have found a - non-zero digit. If we have previously found a long string of adjacent - zeros, replace the digit by '0'. Otherwise, reset the count of - adjacent zeros, and indicate the final number is non-zero. */ - } else if( *c != ' ' && *c != '+' && *c != '-' ){ - started = 1; - if( replace ) { - *c = '0'; - } else { - nzero = 0; - nonzero = 1; - } - } - -/* Move on to the next character. */ - c++; - } - -/* If the final number is zero, just return the most simple decimal zero - value. */ - if( !nonzero ) { - strcpy( text, "0.0" ); - -/* Otherwise, we remove any trailing zeros which occur to the right of a - decimal point. */ - } else if( dot ) { - -/* Find the last non-zero digit. */ - while( c-- > text && *c == '0' ); - -/* If any trailing zeros were found... */ - if( c > text ) { - -/* Retain one trailing zero after a decimal point. */ - if( *c == '.' ) c++; - -/* We put a terminator following the last non-zero character. The - terminator is the exponent, if there was one, or a null character. - Remember to update the pointer to the start of the exponent. */ - c++; - if( exp ) { - a = exp; - exp = c; - while( ( *(c++) = *(a++) ) ); - } else { - *c = 0; - } - } - } - -/* Next check for long strings of adjacent nines. - ============================================= */ - -/* We have not yet found any adjacent nines. */ - nnine = 0; - -/* We have not yet found a non-nine digit. */ - a = NULL; - -/* We have not yet found a non-blank character */ - start = NULL; - last = NULL; - -/* Number is assumed positive. */ - neg = 0; - -/* Indicate that we have not yet found a decimal point in the string. */ - dot = NULL; - -/* Loop round the supplied text string. */ - c = text; - while( *c && c != exp ){ - -/* Note the address of the first non-blank character. */ - if( !start && *c != ' ' ) start = c; - -/* If this is a nine, increment the number of adjacent nines found. */ - if( *c == '9' ){ - ++nnine; - -/* Note if the number contains a decimal point. */ - } else if( *c == '.' ){ - dot = c; - -/* Note if the number is negative. */ - } else if( *c == '-' ){ - neg = 1; - -/* If this character is a non-nine digit, and we have not had a long - sequence of 9's, reset the count of adjacent nines, and update a pointer - to "the last non-nine digit prior to a long string of nines". */ - } else if( *c != ' ' && *c != '+' ){ - if( nnine < NSEQ ) { - nnine = 0; - a = c; - } - } - -/* Note the address of the last non-blank character. */ - if( *c != ' ' ) last = c; - -/* Move on to the next character. */ - c++; - } - -/* If a long string of adjacent nines was found... */ - if( nnine >= NSEQ ) { - c = NULL; - -/* If we found at least one non-nine digit. */ - if( a ) { - -/* "a" points to the last non-nine digit before the first of the group of 9's. - Increment this digit by 1. Since we know the digit is not a nine, there - is no danger of a carry. */ - *a = *a + 1; - -/* Fill with zeros up to the decimal point, or to the end if there is no - decimal point. */ - c = a + 1; - if( dot ) { - while( c < dot ) *(c++) = '0'; - } else { - while( *c ) *(c++) = '0'; - } - -/* Now make "c" point to the first character for the terminator. This is - usually the character following the last non-nine digit. However, if - the last non-nine digit appears immediately before a decimal point, then - we append ".0" to the string before appending the terminator. */ - if( *c == '.' ) { - *(++c) = '0'; - c++; - } - -/* If all digits were nines, the rounded number will occupy one more - character than the supplied number. We can only do the rounding if there - is a spare character (i.e.a space) in the supplied string. */ - } else if( last - start + 1 < len ) { - -/* Put the modified text at the left of the available space. */ - c = text; - -/* Start with a minus sing if needed, followed by the leading "1" (caused - by the overflow from the long string of 9's). */ - if( neg ) *(c++) = '-'; - *(c++) = '1'; - -/* Now find the number of zeros to place after the leading "1". This is - the number of characters in front of the terminator marking the end of - the integer part of the number. */ - if( dot ) { - nzero = dot - start; - } else if( exp ) { - nzero = exp - start; - } else { - nzero = last - start; - } - -/* If the number is negative, the above count will include the leading - minus sign, which is not a digit. So reduce the count by one. */ - if( neg ) nzero--; - -/* Now put in the correct number of zeros. */ - for( i = 0; i < nzero; i++ ) *(c++) = '0'; - -/* If the original string containsed a decimal point, make sure the - returned string also contains one. */ - if( dot ) { - *(c++) = '.'; - if( *c ) *(c++) = '0'; - } - } - -/* We put a terminator following the last non-zero character. The - terminator is the exponent, if there was one, or a null character. */ - if( c ) { - if( exp ) { - while( ( *(c++) = *(exp++) ) ); - } else { - *c = 0; - } - } - } - -/* If a minimum field width has been given, right justify the returned - string in the original field width. */ - if( width ) { - end = text + len; - c = text + strlen( text ); - if( c != end ) { - while( c >= text ) *(end--) = *(c--); - while( end >= text ) *(end--) = ' '; - } - } - -/* If a minimum field width was given, shunt the text to the left in - order to reduce the used field width to the specified value. This - requires there to be some leading spaces (because we do not want to - loose any non-blank characters from the left hand end of the string). - If there are insufficient leading spaces to allow the field width to - be reduced to the specified value, then reduce the field width as far - as possible. First find the number of spaces we would like to remove - from the front of the string (in order to reduce the used width to the - specified value). */ - bu = len - width; - -/* If we need to remove any leading spaces... */ - if( width > 0 && bu > 0 ) { - -/* Find the number of leading spaces which are available to be removed. */ - c = text - 1; - while( *(++c) == ' ' ); - nls = c - text; - -/* If there are insufficient leading spaces, just use however many there - are. */ - if( bu > nls ) bu = nls; - -/* Shift the string. */ - c = text; - a = c + bu; - while( ( *(c++) = *(a++) ) ); - } - -/* Undefine local constants. */ -#undef NSEQ -} - -static int SAOTrans( AstFitsChan *this, AstFitsChan *out, const char *method, - const char *class, int *status ){ -/* -* Name: -* SAOTrans - -* Purpose: -* Translate an SAO encoded header into a TPN encoded header. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int SAOTrans( AstFitsChan *this, AstFitsChan *out, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Search "this" for keywords that give a description of a distorted -* TAN projection using the SAO representation and, if found, write -* keywords to "out" that describe an equivalent projection using TPN -* representation. The definition of the SAO polynomial is taken from -* the platepos.c file included in Doug Mink's WCSTools. - -* Parameters: -* this -* Pointer to the FitsChan to read. -* out -* Pointer to a FitsCHan in which to store translated keywords. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Non-zero if "this" contained an SAO encoded header. Zero otherwise. - -*/ - -#define NC 13 - -/* Local Variables: */ - char keyname[10]; - double co[ 2 ][ NC ]; - double pv; - int i; - int is_sao; - int m; - int ok; - int result; - -/* Initialise */ - result = 0; - -/* Check the inherited status. */ - if( !astOK ) return result; - -/* Check there are exactly two CTYPE keywords in the header. */ - if( 2 == astKeyFields( this, "CTYPE%d", 0, NULL, NULL ) ){ - -/* Initialise all cooefficients. */ - memset( co, 0, sizeof( co ) ); - -/* Get the required SAO keywords. */ - is_sao = 1; - ok = 1; - for( i = 0; i < 2 && ok && is_sao; i++ ) { - - ok = 0; - for( m = 0; m < NC; m++ ) { - -/* Get the value of the next "COi_j" keyword. If any of the first 3 values - are missing on either axis, we assume this is not an SAO header. */ - sprintf( keyname, "CO%d_%d", i + 1, m + 1 ); - if( !GetValue( this, keyname, AST__FLOAT, &co[ i ][ m ], 0, 1, method, - class, status ) ) { - if( m < 3 ) is_sao = 0; - break; - } - -/* Check that we have at least one non-zero coefficient (excluding the - first constant term ). */ - if( co[ i ][ m ] != 0.0 && m > 0 ) ok = 1; - } - } - -/* If this is an SAO header.. */ - if( is_sao ) { - -/* Issue a warning if all coefficients for this axis are zero. */ - if( !ok ) { - Warn( this, "badpv", "This FITS header describes an SAO encoded " - "distorted TAN projection, but all the distortion " - "coefficients for at least one axis are zero.", method, class, - status ); - -/* Otherwise, calculate and store the equivalent PV projection parameters. */ - } else { - pv = co[ 0 ][ 0 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_0", &pv, - AST__FLOAT, NULL, status ); - - pv = co[ 0 ][ 1 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_1", &pv, - AST__FLOAT, NULL, status ); - - pv = co[ 0 ][ 2 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_2", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 0 ][ 3 ] != AST__BAD ) pv += co[ 0 ][ 3 ]; - if( co[ 0 ][ 10 ] != AST__BAD ) pv += co[ 0 ][ 10 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_4", &pv, - AST__FLOAT, NULL, status ); - - pv = co[ 0 ][ 5 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_5", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 0 ][ 4 ] != AST__BAD ) pv += co[ 0 ][ 4 ]; - if( co[ 0 ][ 10 ] != AST__BAD ) pv += co[ 0 ][ 10 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_6", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 0 ][ 6 ] != AST__BAD ) pv += co[ 0 ][ 6 ]; - if( co[ 0 ][ 11 ] != AST__BAD ) pv += co[ 0 ][ 11 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_7", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 0 ][ 8 ] != AST__BAD ) pv += co[ 0 ][ 8 ]; - if( co[ 0 ][ 12 ] != AST__BAD ) pv += co[ 0 ][ 12 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_8", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 0 ][ 9 ] != AST__BAD ) pv += co[ 0 ][ 9 ]; - if( co[ 0 ][ 11 ] != AST__BAD ) pv += co[ 0 ][ 11 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_9", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 0 ][ 7 ] != AST__BAD ) pv += co[ 0 ][ 7 ]; - if( co[ 0 ][ 12 ] != AST__BAD ) pv += co[ 0 ][ 12 ]; - if( pv != AST__BAD ) SetValue( out, "PV1_10", &pv, - AST__FLOAT, NULL, status ); - - pv = co[ 1 ][ 0 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_0", &pv, - AST__FLOAT, NULL, status ); - - pv = co[ 1 ][ 2 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_1", &pv, - AST__FLOAT, NULL, status ); - - pv = co[ 1 ][ 1 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_2", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 1 ][ 4 ] != AST__BAD ) pv += co[ 1 ][ 4 ]; - if( co[ 1 ][ 10 ] != AST__BAD ) pv += co[ 1 ][ 10 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_4", &pv, - AST__FLOAT, NULL, status ); - - pv = co[ 1 ][ 5 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_5", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 1 ][ 3 ] != AST__BAD ) pv += co[ 1 ][ 3 ]; - if( co[ 1 ][ 10 ] != AST__BAD ) pv += co[ 1 ][ 10 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_6", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 1 ][ 7 ] != AST__BAD ) pv += co[ 1 ][ 7 ]; - if( co[ 1 ][ 12 ] != AST__BAD ) pv += co[ 1 ][ 12 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_7", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 1 ][ 9 ] != AST__BAD ) pv += co[ 1 ][ 9 ]; - if( co[ 1 ][ 11 ] != AST__BAD ) pv += co[ 1 ][ 11 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_8", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 1 ][ 8 ] != AST__BAD ) pv += co[ 1 ][ 8 ]; - if( co[ 1 ][ 12 ] != AST__BAD ) pv += co[ 1 ][ 12 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_9", &pv, - AST__FLOAT, NULL, status ); - - pv = 0.0; - if( co[ 1 ][ 6 ] != AST__BAD ) pv += co[ 1 ][ 6 ]; - if( co[ 1 ][ 11 ] != AST__BAD ) pv += co[ 1 ][ 11 ]; - if( pv != AST__BAD ) SetValue( out, "PV2_10", &pv, - AST__FLOAT, NULL, status ); - -/* From an example header provided by Bill Joye, it seems that the SAO - polynomial includes the rotation and scaling effects of the CD matrix. - Therefore we mark as read all CDi_j, CDELT and CROTA values. Without - this, the rotation and scaling would be applied twice. First, mark the - original values as having been used, no matter which FitsChan they are - in. */ - GetValue( this, "CD1_1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "CD1_2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "CD2_1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "CD2_2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "PC1_1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "PC1_2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "PC2_1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "PC2_2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "CDELT1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "CDELT2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "CROTA1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( this, "CROTA2", AST__FLOAT, &pv, 0, 1, method, class, status ); - - GetValue( out, "CD1_1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "CD1_2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "CD2_1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "CD2_2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "PC1_1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "PC1_2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "PC2_1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "PC2_2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "CDELT1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "CDELT2", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "CROTA1", AST__FLOAT, &pv, 0, 1, method, class, status ); - GetValue( out, "CROTA2", AST__FLOAT, &pv, 0, 1, method, class, status ); - -/* Now store new default values in the returned FitsChan. */ - pv = 1.0; - SetValue( out, "PC1_1", &pv, AST__FLOAT, NULL, - status ); - SetValue( out, "PC2_2", &pv, AST__FLOAT, NULL, - status ); - SetValue( out, "CDELT1", &pv, AST__FLOAT, NULL, - status ); - SetValue( out, "CDELT2", &pv, AST__FLOAT, NULL, - status ); - - pv = 0.0; - SetValue( out, "PC1_2", &pv, AST__FLOAT, NULL, - status ); - SetValue( out, "PC2_1", &pv, AST__FLOAT, NULL, - status ); - -/* Indicate we have converted an SAO header. */ - result = 1; - } - } - } - -/* Return a flag indicating if an SAO header was found. */ - return result; -} -#undef NC - -static int SearchCard( AstFitsChan *this, const char *name, - const char *method, const char *class, int *status ){ - -/* -* Name: -* SearchCard - -* Purpose: -* Search the whole FitsChan for a card refering to given keyword. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int SearchCard( AstFitsChan *this, const char *name, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Searches the whole FitsChan for a card refering to the supplied keyword, -* and makes it the current card. The card following the current card is -* checked first. If this is not the required card, then a search is -* performed starting with the first keyword in the FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a string holding the keyword name. -* method -* Pointer to string holding name of calling method. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if a card was found refering to the given -* keyword. Otherwise zero is returned. - -* Notes: -* - If a NULL pointer is supplied for "name" then the current card -* is left unchanged. -* - The current card is set to NULL (end-of-file) if no card can be -* found for the supplied keyword. -*/ - -/* Local Variables: */ - int ret; /* Was a card found? */ - -/* Check the global status, and supplied keyword name. */ - if( !astOK || !name ) return 0; - -/* Indicate that no card has been found yet. */ - ret = 0; - -/* The required card is very often the next card in the FitsChan, so check the - next card, and only search the entire FitsChan if the check fails. */ - MoveCard( this, 1, method, class, status ); - if( !astFitsEof( this ) && - !Ustrncmp( CardName( this, status ), name, FITSNAMLEN, status ) ){ - ret = 1; - -/* If the next card is not the required card, rewind the FitsChan back to - the first card. */ - } else { - astClearCard( this ); - -/* Attempt to find the supplied keyword, searching from the first card. */ - ret = FindKeyCard( this, name, method, class, status ); - } - -/* Return. */ - return ret; -} - -static void SetAlgCode( char *buf, const char *algcode, int *status ){ -/* -* Name: -* SetAlgCode - -* Purpose: -* Create a non-linear CTYPE string from a system code and an algorithm -* code. - -* Type: -* Private function. - -* Synopsis: -* void SetAlgCode( char *buf, const char *algcode, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* FITS-WCS paper 1 says that non-linear axes must have a CTYPE of the -* form "4-3" (e.g. "VRAD-TAB"). This function handles the truncation -* of long system codes, or the padding of short system codes. - -* Parameters: -* buf -* A buffer in which is stored the system code. Modified on exit to -* hold the combined CTYPE value. It should have a length long -* enough to hold the system code and the algorithm code. -* algcode -* Pointer to a string holding the algorithm code (with a leading -* "-", e.g. "-TAB"). -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - int nc; - -/* Check inherited status */ - if( !astOK ) return; - -/* Pad the supplied string to at least 4 characters using "-" characters. */ - nc = strlen( buf ); - while( nc < 4 ) buf[ nc++ ] = '-'; - -/* Insert the null-terminated code at position 4. */ - strcpy( buf + 4, algcode ); -} - -static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { -/* -* Name: -* SetAttrib - -* Purpose: -* Set an attribute value for a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void SetAttrib( AstObject *this, const char *setting ) - -* Class Membership: -* FitsChan member function (over-rides the astSetAttrib protected -* method inherited from the Channel class). - -* Description: -* This function assigns an attribute value for a FitsChan, the -* attribute and its value being specified by means of a string of - -* the form: -* -* "attribute= value " -* -* Here, "attribute" specifies the attribute name and should be in -* lower case with no white space present. The value to the right -* of the "=" should be a suitable textual representation of the -* value to be assigned and this will be interpreted according to -* the attribute's data type. White space surrounding the value is -* only significant for string attributes. - -* Parameters: -* this -* Pointer to the FitsChan. -* setting -* Pointer to a null-terminated string specifying the new attribute -* value. -*/ - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - const char *class; /* Object class */ - double dval; /* Double attribute value */ - int ival; /* Integer attribute value */ - int len; /* Length of setting string */ - int nc; /* Number of characters read by astSscanf */ - int offset; /* Offset of attribute string */ - int warn; /* Offset of Warnings string */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_object; - -/* Obtain the length of the setting string. */ - len = (int) strlen( setting ); - -/* Obtain the object class. */ - class = astGetClass( this ); - -/* Card. */ -/* ----- */ - if ( nc = 0, - ( 1 == astSscanf( setting, "card= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetCard( this, ival ); - -/* Encoding. */ -/* --------- */ - } else if( nc = 0, - ( 0 == astSscanf( setting, "encoding=%n%*[^\n]%n", &ival, &nc ) ) - && ( nc >= len ) ) { - nc = ChrLen( setting + ival, status ); - if( !Ustrncmp( setting + ival, NATIVE_STRING, nc, status ) ){ - astSetEncoding( this, NATIVE_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSPC_STRING, nc, status ) ){ - astSetEncoding( this, FITSPC_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSPC_STRING2, nc, status ) ){ - astSetEncoding( this, FITSPC_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSWCS_STRING, nc, status ) ){ - astSetEncoding( this, FITSWCS_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSWCS_STRING2, nc, status ) ){ - astSetEncoding( this, FITSWCS_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSIRAF_STRING, nc, status ) ){ - astSetEncoding( this, FITSIRAF_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSIRAF_STRING2, nc, status ) ){ - astSetEncoding( this, FITSIRAF_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSAIPS_STRING, nc, status ) ){ - astSetEncoding( this, FITSAIPS_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSAIPS_STRING2, nc, status ) ){ - astSetEncoding( this, FITSAIPS_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSAIPSPP_STRING, nc, status ) ){ - astSetEncoding( this, FITSAIPSPP_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSAIPSPP_STRING2, nc, status ) ){ - astSetEncoding( this, FITSAIPSPP_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSCLASS_STRING, nc, status ) ){ - astSetEncoding( this, FITSCLASS_ENCODING ); - } else if( !Ustrncmp( setting + ival, FITSCLASS_STRING2, nc, status ) ){ - astSetEncoding( this, FITSCLASS_ENCODING ); - } else if( !Ustrncmp( setting + ival, DSS_STRING, nc, status ) ){ - astSetEncoding( this, DSS_ENCODING ); - } else { - astError( AST__BADAT, "astSet(%s): Unknown encoding system '%s' " - "requested for a %s.", status, class, setting + ival, class ); - } - -/* FitsDigits. */ -/* ----------- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "fitsdigits= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetFitsDigits( this, ival ); - -/* FitsAxisOrder. */ -/* -------------- */ - } else if ( nc = 0, - ( 0 == astSscanf( setting, "fitsaxisorder=%n%*[^\n]%n", - &offset, &nc ) ) - && ( nc >= len ) ) { - astSetFitsAxisOrder( this, setting + offset ); - -/* CDMatrix */ -/* -------- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "cdmatrix= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetCDMatrix( this, ival ); - -/* DefB1950 */ -/* -------- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "defb1950= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetDefB1950( this, ival ); - -/* TabOK */ -/* ----- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "tabok= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetTabOK( this, ival ); - -/* CarLin */ -/* ------ */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "carlin= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetCarLin( this, ival ); - -/* SipReplace */ -/* ---------- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "sipreplace= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetSipReplace( this, ival ); - -/* FitsTol. */ -/* -------- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "fitstol= %lg %n", &dval, &nc ) ) - && ( nc >= len ) ) { - astSetFitsTol( this, dval ); - -/* PolyTan */ -/* ------- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "polytan= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetPolyTan( this, ival ); - -/* SipOK */ -/* ------- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "sipok= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetSipOK( this, ival ); - -/* Iwc */ -/* --- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "iwc= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetIwc( this, ival ); - -/* Clean */ -/* ----- */ - } else if ( nc = 0, - ( 1 == astSscanf( setting, "clean= %d %n", &ival, &nc ) ) - && ( nc >= len ) ) { - astSetClean( this, ival ); - -/* Warnings. */ -/* -------- */ - } else if ( nc = 0, - ( 0 == astSscanf( setting, "warnings=%n%*[^\n]%n", &warn, &nc ) ) - && ( nc >= len ) ) { - astSetWarnings( this, setting + warn ); - -/* Define a macro to see if the setting string matches any of the - read-only attributes of this class. */ -#define MATCH(attrib) \ - ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ - ( nc >= len ) ) - -/* If the attribute was not recognised, use this macro to report an error - if a read-only attribute has been specified. */ - } else if ( MATCH( "ncard" ) || - MATCH( "cardtype" ) || - MATCH( "cardcomm" ) || - MATCH( "cardname" ) || - MATCH( "nkey" ) || - MATCH( "allwarnings" ) ){ - astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, - setting, astGetClass( this ) ); - astError( AST__NOWRT, "This is a read-only attribute." , status); - -/* If the attribute is still not recognised, pass it on to the parent - method for further interpretation. */ - } else { - (*parent_setattrib)( this_object, setting, status ); - } -} - -static void SetCard( AstFitsChan *this, int icard, int *status ){ - -/* -*+ -* Name: -* astSetCard - -* Purpose: -* Set the value of the Card attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" - -* void astSetCard( AstFitsChan *this, int icard ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function sets the value of the Card attribute for the supplied -* FitsChan. This is the index of the next card to be read from the -* FitsChan. If a value of 1 or less is supplied, the first card in -* the FitsChan will be read next. If a value greater than the number -* of cards in the FitsChan is supplied, the FitsChan will be left in an -* "end-of-file" condition, in which no further read operations can be -* performed. - -* Parameters: -* this -* Pointer to the FitsChan. -* icard -* The index of the next card to read. - -* Notes: -* - This function attempts to execute even if an error has occurred. -*- -*/ - -/* Check the supplied object. */ - if ( !this ) return; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Rewind the FitsChan. */ - astClearCard( this ); - -/* Move forward the requested number of cards. */ - MoveCard( this, icard - 1, "astSetCard", astGetClass( this ), status ); - -/* Return. */ - return; -} - -static void SetItem( double ****item, int i, int jm, char s, double val, int *status ){ -/* -* Name: -* SetItem - -* Purpose: -* Store a value for a axis keyword value in a FitStore structure. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void SetItem( double ****item, int i, int jm, char s, double val, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The supplied keyword value is stored in the specified array, -* at a position indicated by the axis and co-ordinate version. -* The array is created or extended as necessary to make room for -* the new value. Any old value is over-written. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->crval) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (i), and the pointer locates an -* array of axis keyword values. These arrays of keyword values have -* one element for every pixel axis (j) or projection parameter (m). -* i -* The zero based intermediate axis index in the range 0 to 98. Set -* this to zero for keywords (e.g. CRPIX) which are not indexed by -* intermediate axis number. -* jm -* The zero based pixel axis index (in the range 0 to 98) or parameter -* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for -* keywords (e.g. CRVAL) which are not indexed by either pixel axis or -* parameter number. -* val -* The keyword value to store. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - int el; /* Array index */ - int nel; /* Number of elements in array */ - int si; /* Integer co-ordinate version index */ - -/* Check the inherited status. */ - if( !astOK ) return; - -/* Convert the character co-ordinate version into an integer index, and - check it is within range. The primary axis description (s=' ') is - given index zero. 'A' is 1, 'B' is 2, etc. */ - if( s == ' ' ) { - si = 0; - } else if( islower(s) ){ - si = (int) ( s - 'a' ) + 1; - } else { - si = (int) ( s - 'A' ) + 1; - } - if( si < 0 || si > 26 ) { - astError( AST__INTER, "SetItem(fitschan): AST internal error; " - "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); - -/* Check the intermediate axis index is within range. */ - } else if( i < 0 || i > 98 ) { - astError( AST__INTER, "SetItem(fitschan): AST internal error; " - "intermediate axis index %d is invalid.", status, i ); - -/* Check the pixel axis or parameter index is within range. */ - } else if( jm < 0 || jm > 99 ) { - astError( AST__INTER, "SetItem(fitschan): AST internal error; " - "pixel axis or parameter index %d is invalid.", status, jm ); - -/* Otherwise proceed... */ - } else { - -/* Store the current number of coordinate versions in the supplied array */ - nel = astSizeOf( (void *) *item )/sizeof(double **); - -/* If required, extend the array located by the supplied pointer so that - it is long enough to hold the specified co-ordinate version. */ - if( nel < si + 1 ){ - *item = (double ***) astGrow( (void *) *item, si + 1, - sizeof(double **) ); - -/* Check the pointer can be used. */ - if( astOK ){ - -/* Initialise the new elements to hold NULL. Note, astGrow may add more - elements to the array than is actually needed, so use the actual current - size of the array as implied by astSize rather than the index si. */ - for( el = nel; - el < astSizeOf( (void *) *item )/sizeof(double **); - el++ ) (*item)[el] = NULL; - } - } - -/* If the above went OK... */ - if( astOK ){ - -/* Store the currrent number of intermediate axes in the supplied array */ - nel = astSizeOf( (void *) (*item)[si] )/sizeof(double *); - -/* If required, extend the array so that it is long enough to hold the - specified intermediate axis. */ - if( nel < i + 1 ){ - (*item)[si] = (double **) astGrow( (void *) (*item)[si], i + 1, - sizeof(double *) ); - -/* Check the pointer can be used. */ - if( astOK ){ - -/* Initialise the new elements to hold NULL. */ - for( el = nel; - el < astSizeOf( (void *) (*item)[si] )/sizeof(double *); - el++ ) (*item)[si][el] = NULL; - } - } - -/* If the above went OK... */ - if( astOK ){ - -/* Store the current number of pixel axis or parameter values in the array. */ - nel = astSizeOf( (void *) (*item)[si][i] )/sizeof(double); - -/* If required, extend the array so that it is long enough to hold the - specified axis. */ - if( nel < jm + 1 ){ - (*item)[si][i] = (double *) astGrow( (void *) (*item)[si][i], - jm + 1, sizeof(double) ); - -/* Check the pointer can be used. */ - if( astOK ){ - -/* Initialise the new elements to hold AST__BAD. */ - for( el = nel; - el < astSizeOf( (void *) (*item)[si][i] )/sizeof(double); - el++ ) (*item)[si][i][el] = AST__BAD; - } - } - -/* If the above went OK, store the supplied keyword value. */ - if( astOK ) (*item)[si][i][jm] = val; - } - } - } -} - -static void SetItemC( char *****item, int i, int jm, char s, const char *val, - int *status ){ -/* -* Name: -* SetItemC - -* Purpose: -* Store a character string for an axis keyword value in a FitStore -* structure. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void SetItemC( char *****item, int i, int jm, char s, const char *val, -* int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The supplied keyword string value is stored in the specified array, -* at a position indicated by the axis and co-ordinate version. -* The array is created or extended as necessary to make room for -* the new value. Any old value is over-written. - -* Parameters: -* item -* The address of the pointer within the FitsStore which locates the -* arrays of values for the required keyword (eg &(store->ctype) ). -* The array located by the supplied pointer contains a vector of -* pointers. Each of these pointers is associated with a particular -* co-ordinate version (s), and locates an array of pointers for that -* co-ordinate version. Each such array of pointers has an element -* for each intermediate axis number (i), and the pointer locates an -* array of axis keyword string pointers. These arrays of keyword -* string pointers have one element for every pixel axis (j) or -* projection parameter (m). -* i -* The zero based intermediate axis index in the range 0 to 98. Set -* this to zero for keywords (e.g. RADESYS) which are not indexed by -* intermediate axis number. -* jm -* The zero based pixel axis index (in the range 0 to 98) or parameter -* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for -* keywords (e.g. CTYPE) which are not indexed by either pixel axis or -* parameter number. -* val -* The keyword string value to store. A copy of the supplied string -* is taken. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - int el; /* Array index */ - int nel; /* Number of elements in array */ - int si; /* Integer co-ordinate version index */ - -/* Check the inherited status and the supplied pointer. */ - if( !astOK || !val ) return; - -/* Convert the character co-ordinate version into an integer index, and - check it is within range. The primary axis description (s=' ') is - given index zero. 'A' is 1, 'B' is 2, etc. */ - if( s == ' ' ) { - si = 0; - } else if( islower(s) ){ - si = (int) ( s - 'a' ) + 1; - } else { - si = (int) ( s - 'A' ) + 1; - } - if( si < 0 || si > 26 ) { - astError( AST__INTER, "SetItemC(fitschan): AST internal error; " - "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); - -/* Check the intermediate axis index is within range. */ - } else if( i < 0 || i > 98 ) { - astError( AST__INTER, "SetItemC(fitschan): AST internal error; " - "intermediate axis index %d is invalid.", status, i ); - -/* Check the pixel axis or parameter index is within range. */ - } else if( jm < 0 || jm > 99 ) { - astError( AST__INTER, "SetItemC(fitschan): AST internal error; " - "pixel axis or parameter index %d is invalid.", status, jm ); - -/* Otherwise proceed... */ - } else { - -/* Store the current number of coordinate versions in the supplied array */ - nel = astSizeOf( (void *) *item )/sizeof(char ***); - -/* If required, extend the array located by the supplied pointer so that - it is long enough to hold the specified co-ordinate version. */ - if( nel < si + 1 ){ - *item = (char ****) astGrow( (void *) *item, si + 1, - sizeof(char ***) ); - -/* Check the pointer can be used. */ - if( astOK ){ - -/* Initialise the new elements to hold NULL. Note, astGrow may add more - elements to the array than is actually needed, so use the actual current - size of the array as implied by astSize rather than the index si. */ - for( el = nel; - el < astSizeOf( (void *) *item )/sizeof(char ***); - el++ ) (*item)[el] = NULL; - } - } - -/* If the above went OK... */ - if( astOK ){ - -/* Store the currrent number of intermediate axes in the supplied array */ - nel = astSizeOf( (void *) (*item)[si] )/sizeof(char **); - -/* If required, extend the array so that it is long enough to hold the - specified intermediate axis. */ - if( nel < i + 1 ){ - (*item)[si] = (char ***) astGrow( (void *) (*item)[si], i + 1, - sizeof(char **) ); - -/* Check the pointer can be used. */ - if( astOK ){ - -/* Initialise the new elements to hold NULL. */ - for( el = nel; - el < astSizeOf( (void *) (*item)[si] )/sizeof(char **); - el++ ) (*item)[si][el] = NULL; - } - } - -/* If the above went OK... */ - if( astOK ){ - -/* Store the current number of pixel axis or parameter values in the array. */ - nel = astSizeOf( (void *) (*item)[si][i] )/sizeof(char *); - -/* If required, extend the array so that it is long enough to hold the - specified axis. */ - if( nel < jm + 1 ){ - (*item)[si][i] = (char **) astGrow( (void *) (*item)[si][i], - jm + 1, sizeof(char *) ); - -/* Check the pointer can be used. */ - if( astOK ){ - -/* Initialise the new elements to hold NULL. */ - for( el = nel; - el < astSizeOf( (void *) (*item)[si][i] )/sizeof(char *); - el++ ) (*item)[si][i][el] = NULL; - } - } - -/* If the above went OK... */ - if( astOK ){ - -/* Store a copy of the supplied string, using any pre-allocated memory. */ - (*item)[si][i][jm] = (char *) astStore( (void *) (*item)[si][i][jm], - (void *) val, - strlen( val ) + 1 ); - } - } - } - } -} - -static void SetSourceFile( AstChannel *this_channel, const char *source_file, - int *status ) { -/* -* Name: -* SetSourceFile - -* Purpose: -* Set a new value for the SourceFile attribute. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void SetSourceFile( AstChannel *this, const char *source_file, -* int *status ) - -* Class Membership: -* FitsChan member function (over-rides the astSetSourceFile -* method inherited from the Channel class). - -* Description: -* This function stores the supplied string as the new value for the -* SourceFile attribute. In addition, it also attempts to open the -* file, read FITS headers from it and append them to the end of the -* FitsChan. It then closes the SourceFile. - -* Parameters: -* this -* Pointer to the FitsChan. -* source_file -* The new attribute value. Should be the path to an existing text -* file, holding FITS headers (one per line) -* status -* Inherited status pointer. - -*/ - -/* Local Constants: */ -#define ERRBUF_LEN 80 - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - FILE *fd; /* Descriptor for source file */ - char *errstat; /* Pointer for system error message */ - char card[ AST__FITSCHAN_FITSCARDLEN + 2 ]; /* Buffer for source line */ - char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Invoke the parent astSetSourceFile method to store the supplied - string in the Channel structure. */ - (*parent_setsourcefile)( this_channel, source_file, status ); - -/* Attempt to open the file. */ - fd = NULL; - if( astOK ) { - fd = fopen( source_file, "r" ); - if( !fd ) { - if ( errno ) { -#if HAVE_STRERROR_R - strerror_r( errno, errbuf, ERRBUF_LEN ); - errstat = errbuf; -#else - errstat = strerror( errno ); -#endif - astError( AST__RDERR, "astSetSourceFile(%s): Failed to open input " - "SourceFile '%s' - %s.", status, astGetClass( this ), - source_file, errstat ); - } else { - astError( AST__RDERR, "astSetSourceFile(%s): Failed to open input " - "SourceFile '%s'.", status, astGetClass( this ), - source_file ); - } - } - } - -/* Move the FitsChan to EOF */ - astSetCard( this, INT_MAX ); - -/* Read each line from the file, remove trailing space, and append to the - FitsChan. */ - while( astOK && fgets( card, AST__FITSCHAN_FITSCARDLEN + 2, fd ) ) { - card[ astChrLen( card ) ] = 0; - astPutFits( this, card, 0 ); - } - -/* Close the source file. */ - if( fd ) fclose( fd ); - -} - -static void SetTableSource( AstFitsChan *this, - void (*tabsource)( void ), - void (*tabsource_wrap)( void (*)( void ), - AstFitsChan *, const char *, - int, int, int * ), - int *status ){ - -/* -*+ -* Name: -* astSetTableSource - -* Purpose: -* Register source and wrapper function for accessing tables in FITS files. - -* Type: -* Protected function. - -* Synopsis: -* #include "fitschan.h" -* void astSetTableSource( AstFitsChan *this, -* void (*tabsource)( void ), -* void (*tabsource_wrap)( void (*)( void ), -* AstFitsChan *, const char *, -* int, int, int * ), -* int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function registers a table source function and its wrapper. A -* wrapper function exists to adapt the API of the table source -* function to the needs of different languages. The wrapper is called -* from the FitsChan code. The wrapper then adjusts the arguments as -* required and then calls the actualy table source function. - -* Parameters: -* this -* Pointer to the FitsChan. -* tabsource -* Pointer to the table source function. The API for this function -* will depend on the language, and so is cast to void here. It -* should be cast to the required form within the wrapper function. -* tabsource_wrap -* The wrapper function. -*- -*/ - -/* Local Variables: */ - -/* Check the global error status. */ - if ( !astOK ) return; - this->tabsource = tabsource; - this->tabsource_wrap = tabsource_wrap; -} - -static void SetValue( AstFitsChan *this, const char *keyname, void *value, - int type, const char *comment, int *status ){ - -/* -* Name: -* SetValue - -* Purpose: -* Save a FITS keyword value, over-writing any existing keyword value. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void SetValue( AstFitsChan *this, char *keyname, void *value, -* int type, const char *comment, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function saves a keyword value as a card in the supplied -* FitsChan. Comment cards are always inserted in-front of the current -* card. If the keyword is not a comment card, any existing value -* for the keyword is over-written with the new value (even if it is -* marked as having been read). Otherwise, (i.e. if it is not a comment -* card, and no previous value exists) it is inserted in front -* of the current card. - -* Parameters: -* this -* A pointer to the FitsChan. -* keyname -* A pointer to a string holding the keyword name. -* value -* A pointer to a buffer holding the keyword value. For strings, -* the buffer should hold a pointer to the character string. -* type -* The FITS data type of the supplied keyword value. -* comment -* A comment to store with the keyword. -* status -* Pointer to the inherited status variable. - -* Notes: -* - Nothing is stored if a NULL pointer is supplied for "value". -* - If the keyword has a value of AST__BAD then nothing is stored, -* and an error is reported. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - FitsCard *card; /* Pointer to original current card */ - const char *class; /* Class name to include in error messages */ - const char *method; /* Method name to include in error messages */ - int newcard; /* Has the original current card been deleted? */ - int old_ignore_used; /* Original setting of external ignore_used variable */ - int stored; /* Has the keyword been stored? */ - -/* Check the status and supplied value pointer. */ - if( !astOK || !value ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Set up the method and class names for inclusion in error mesages. */ - method = "astWrite"; - class = astGetClass( this ); - -/* Comment card are always inserted in-front of the current card. */ - if ( type == AST__COMMENT ) { - SetFits( this, keyname, value, type, comment, 0, status ); - -/* Otherwise... */ - } else { - -/* Report an error if a bad value is stored for a keyword. */ - if( type == AST__FLOAT ){ - if( *( (double *) value ) == AST__BAD && astOK ) { - astError( AST__BDFTS, "%s(%s): The required FITS keyword " - "\"%s\" is indeterminate.", status, method, class, keyname ); - } - } - -/* Save a pointer to the current card. */ - card = (FitsCard *) this->card; - -/* Indicate that we should not skip over cards marked as having been - read. */ - old_ignore_used = ignore_used; - ignore_used = 0; - -/* Indicate that we have not yet stored the keyword value. */ - stored = 0; - -/* Attempt to find a card refering to the supplied keyword. If one is - found, it becomes the current card. */ - if( SearchCard( this, keyname, "astWrite", astGetClass( this ), status ) ){ - -/* If the card which was current on entry to this function will be - over-written, we will need to take account of this when re-instating the - original current card. Make a note of this. */ - newcard = ( card == (FitsCard *) this->card ); - -/* Replace the current card with a card holding the supplied information. */ - SetFits( this, keyname, value, type, comment, 1, status ); - stored = 1; - -/* If we have just replaced the original current card, back up a card - so that the replacement card becomes the current card. */ - if( newcard ) { - MoveCard( this, -1, "astWrite", astGetClass( this ), status ); - -/* Otherwise, re-instate the original current card. */ - } else { - this->card = (void *) card; - } - } - -/* If the keyword has not yet been stored (i.e. if it did not exist in the - FitsChan), re-instate the original current card and insert the new card - before the original current card, leaving the current card unchanged. */ - if( !stored ) { - this->card = (void *) card; - SetFits( this, keyname, value, type, comment, 0, status ); - } - -/* Re-instate the original flag indicating if cards marked as having been - read should be skipped over. */ - ignore_used = old_ignore_used; - } -} - -static void Shpc1( double xmin, double xmax, int n, double *d, double *w, - int *status ){ -/* -* Name: -* Shpc1 - -* Purpose: -* Modifies a one-dimensional polynomial to scale the polynomial argument. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void Shpc1( double xmin, double xmax, int n, double *d, double *w, -* int *status ) - -* Description: -* Given the coefficients of a one-dimensional polynomial P(u) defined on a -* unit interval (i.e. -1 <= u <= +1 ), find the coefficients of another -* one-dimensional polynomial Q(x) where: -* -* Q(x) = P(u) -* u = ( 2*x - ( xmax + xmin ) ) / ( xmax - xmin ) -* -* That is, u is a scaled version of x, such that the unit interval in u -* maps onto (xmin:xmax) in x. - -* Parameters: -* xmin -* X value corresponding to u = -1 -* xmax -* X value corresponding to u = +1 -* n -* One more than the maximum power of u within P. -* d -* An array of n elements supplied holding the coefficients of P such -* that the coefficient of (u^i) is held in element (i). -* w -* An array of n elements returned holding the coefficients of Q such -* that the coefficient of (x^i) is held in element (i). -* status -* Pointer to the inherited status variable. - -* Notes: -* - Vaguely inspired by the Numerical Recipes routine "pcshft". But the -* original had bugs, so I wrote this new version from first principles. - -*/ - -/* Local Variables: */ - double b; - double a; - int j; - int i; - -/* Check inherited status */ - if( !astOK ) return; - -/* Get the scale and shift terms so that u = a*x + b */ - a = 2.0/( xmax - xmin ); - b = ( xmin + xmax )/( xmin - xmax ); - -/* Initialise the returned coeffs */ - for( i = 0; i < n; i++ ) w[ i ] = 0.0; - -/* The supplied Polynomial is - - P(u) = d0 + d1*u + d2*u^2 + ... - - = d0 + u*( d1 + u*( d2 + ... u*( d{n-1} ) ) ) . . . . . (1) - - = d0 + (a*x+b)*( d1 + (a*x+b)*( d2 + ... (a*x+b)*( d[n-1] ) ) ) - - The inner-most parenthesised expression is a polynomial of order zero - (a constant - d[n-1]). Store the coefficients of this zeroth order - polynomial in the returned array. The "w" array is used to hold the - coefficients of Q, i.e. coefficients of powers of "x", not "u", but - since the inner-most polynomial is a constant, it makes no difference - (x^0 == u^0 == 1). */ - w[ 0 ] = d[ n - 1 ]; - -/* Now loop through each remaining level of parenthetic nesting in (1). At - each level, the parenthesised expression represents a polynomial of order - "i". At the end of each pass though this loop, the returned array "w" - holds the coefficients of this "i"th order polynomial. So on the last - loop, i = n-1, "w" holds the required coefficients of Q. */ - for( i = 1; i < n; i++ ) { - -/* If "R" is the polynomial at the "i-1"th level of nesting (the - coefficiemts of which are currently held in "w"), and "S" is the - polynomial at the "i"th level of nesting, we can see from (1) that: - - S = d[ n - 1 - i ] + u*R - - Substituting for "u", this becomes - - S = d[ n - 1 - i ] + ( a*x + b )*R - = d[ n - 1 - i ] + a*R*x + b*R - - Looking at each of these three terms in reverse order: - - 1) The "b*R" term is implemented by simply scaling the current contents - of the "w" array by "b"; in the "a*R*x" term. - - 2) In "a*R*x", the effect of multiplying by "x" is to move the existing - coefficients in "w" up one element. We then multiply the shifted - coefficients by "a" and add them onto the coefficients produced at - step 1) above. - - We know that "w" still contains the initial zeros at indices higher than - "i" so we only need to scale the bottom "i" elements. We do not do the - zeroth term in this loop since there is no lower term to shift up into - it. */ - - for( j = i; j > 0; j-- ){ - w[ j ] = b*w[ j ] + a*w[ j - 1 ]; - } - -/* Now do the zeroth term. Scale the existing zeroth term by "b" as - required by step 1) and add on the first term, the constant - "d[ n - 1 - i ]". Step 2) is a no-op, since in effect the value of - "w[-1]" is zero. */ - w[ 0 ] = d[ n - i - 1 ] + b*w[ 0 ]; - } - -} - -static void ShowFits( AstFitsChan *this, int *status ){ - -/* -*++ -* Name: -c astShowFits -f AST_SHOWFITS - -* Purpose: -* Display the contents of a FitsChan on standard output. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" -c void astShowFits( AstFitsChan *this ) -f CALL AST_SHOWFITS( THIS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* formats and displays all the cards in a FitsChan on standard output. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. - -*-- -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - char card[ AST__FITSCHAN_FITSCARDLEN + 1]; /* Buffer for header card */ - int icard; /* Current card index on entry */ - int old_ignore_used; /* Original value of external variable ignore_used */ - -/* Check the global status. */ - if( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Store the current card index. */ - icard = astGetCard( this ); - -/* Indicate that cards which have been read into an AST object should skipped - over by the functions which navigate the linked list of cards. */ - old_ignore_used = ignore_used; - ignore_used = 1; - -/* Ensure that the first card in the FitsChan will be the next one to be - read. */ - astSetCard( this, 1 ); - -/* Loop round obtaining and writing out each card, until all cards have been - processed. */ - while( !astFitsEof( this ) && astOK ){ - -/* Get the current card, and display it. The call to astFindFits increments - the current card. */ - if( astFindFits( this, "%f", card, 1 ) ) printf( "%s\n", card ); - } - -/* Re-instate the original flag indicating if cards marked as having been - read should be skipped over. */ - ignore_used = old_ignore_used; - -/* Set the current card index back to what it was on entry. */ - astSetCard( this, icard ); - -} - -static int Similar( const char *str1, const char *str2, int *status ){ -/* -* Name: -* Similar - -* Purpose: -* Are two string effectively the same to human readers? - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void Similar( const char *str1, const char *str2, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function returns a non-zero value if the two supplied strings -* are equivalent to a human reader. This is assumed to be the case if -* the strings are equal apart from leading and trailing white space, -* multiple embedded space, and case. - -* Parameters: -* str1 -* The first string -* str2 -* The second string -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Non-zero if the two supplied strings are equivalent, and zero -* otherwise. -*/ - -/* Local Variables: */ - const char *ea; /* Pointer to end of string a */ - const char *eb; /* Pointer to end of string b */ - const char *a; /* Pointer to next character in string a */ - const char *b; /* Pointer to next character in string b */ - int result; /* Are the two strings equivalent? */ - int ss; /* Skip subsequent spaces? */ - -/* Initialise */ - result = 0; - -/* Check the status and supplied value pointer. */ - if( !astOK ) return result; - -/* Initialise pointers into the two strings. */ - a = str1; - b = str2; - -/* Get a pointer to the character following the last non-blank character in - each string. */ - ea = a + ChrLen( a, status ) - 1; - eb = b + ChrLen( b, status ) - 1; - -/* Set a flag indicating that spaces before the next non-blank character - should be ignored. */ - ss = 1; - -/* Compare the strings. */ - while( 1 ){ - -/* Move on to the next significant character in both strings. */ - while( a < ea && *a == ' ' && ss ) a++; - while( b < eb && *b == ' ' && ss ) b++; - -/* If one string has been exhausted but the other has not, the strings - are not equivalent. */ - if( ( a < ea && b == eb ) || ( a == ea && b < eb ) ) { - break; - -/* If both strings have been exhausted simultaneously, the strings - are equivalent. */ - } else if( b == eb && a == ea ) { - result = 1; - break; - -/* If neither string has been exhausted, compare the current character - for equality, ignoring case. Break if they are different. */ - } else if( tolower( *a ) != tolower( *b ) ){ - break; - -/* If the two characters are both spaces, indicate that subsequent spaces - should be skipped. */ - } else if( *a == ' ' ) { - ss = 1; - -/* If the two characters are not spaces, indicate that subsequent spaces - should not be skipped. */ - } else { - ss = 0; - } - -/* Move on to the next characters. */ - a++; - b++; - } - -/* Return the result. */ - return result; -} - -static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) { -/* -* Name: -* SinkWrap - -* Purpose: -* Wrapper function to invoke a C FitsChan sink function. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function invokes the sink function whose pointer is -* supplied in order to write an output line to an external data -* store. - -* Parameters: -* sink -* Pointer to a sink function, whose single parameter is a -* pointer to a const, null-terminated string containing the -* text to be written, and which returns void. This is the form -* of FitsChan sink function employed by the C language interface -* to the AST library. -* status -* Pointer to the inherited status variable. -*/ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Invoke the sink function. */ - ( *sink )( line ); -} - -static AstMapping *SIPIntWorld( AstMapping *map, int lonax, int latax, - char s, FitsStore *store, double *dim, - int inaxes[2], double crpix[2], double cd[4], - const char *method, const char *class, - int *status ){ -/* -* Name: -* SIPIntWorld - -* Purpose: -* Create FITS header values which map grid into intermediate world -* coords for celestial axes that include SIP distortion. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *SIPIntWorld( AstMapping *map, int lonax, int latax, -* char s, FitsStore *store, double *dim, -* int inaxes[2], double crpix[2], double cd[4], -* const char *method, const char *class, -* int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function finds and returns values for the CRPIX and CDi_j -* keywords for sky axes that can be described using the SIP -* distortion scheme. These values are determined by examining the -* supplied pixel->IWCS Mapping. Values for SIP headers are also stored -* in the supplied FitsSTore. -* -* The celestial axes are first identified and the supplied Mapping -* split to create a (2-in,2-out) Mapping that describes them. This -* Mapping is then searched for a PolyMap. If found, the Mapping prior -* to the PolyMap is checked to ensure it is a simple shift of origin. -* The Mapping following the PolyMap is checked to ensure it is a -* linear transformation with no shift of origin. The PolyMap itself -* is checked to see if it conforms to the requirements of the SIP -* conventions. If any of these conditions are not met, NULL is -* returned as the function value. Otherwise, CRPIX values are created -* from the Mapping prior to the PolyMap, and CDi_j values from the -* Mapping following the PolyMap. The keywords describing the SIP -* distortion itself (the PolyMap) are stored in the supplied FitsStore. -* A Mapping is retuned that is identical to the supplied Mapping but -* without the PolyMap. - -* Parameters: -* map -* A pointer to a Mapping which transforms grid coordinates into -* intermediate world coordinates. -* lonax -* The zero-based index of the output of "map" corresponding to -* celestial longitude. -* latax -* The zero-based index of the output of "map" corresponding to -* celestial latitude. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* store -* A pointer to the FitsStore into which the calculated SIP headers -* are stored. -* dim -* An array holding the image dimensions in pixels. AST__BAD can be -* supplied for any unknwon dimensions. -* inaxes -* Returned holding the indices of the two Mapping inputs that generate -* the returned "crpix" and "cd" values. -* crpix -* If SIP headers are stored successfully in the FitsStore, then -* this array is returned holding the CRPIX values. The first -* element refers to the Mapping input given by the first element -* of "inaxes". The second element refers to the Mapping input given -* by the second element of "inaxes". -* cd -* If SIP headers are stored successfully in the FitsStore, then -* this array is returned holding the CD values in the order -* (CDlonax_j1,CDlonax_j2,CDlatax_j1,CDlatax_j2). Where "lonax" and -* "latax" are the indices of the lon and lat Mapping outputs -* (note, these may be different to the corresponding FITS "i" axis -* indices), and "j1" and "j2" are the indices of the Mapping inputs -* returned in "inaxes" (i.e. j1 = inaxes[0] and j2 = inaxes[1]). -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A Mapping is returned if SIP headers have been stored in the -* FitsStore successfully. NULL is returned otherwise. The returned -* Mapping is a copy of the supplied mapping "map", but without the -* PolyMap. - -* Notes: -* - NULL is returned if an error occurs. -*/ - -/* Local Variables: */ - AstMapping **map_list; - AstMapping *map_upper; - AstMapping *map_lower; - AstMapping *result; - AstMapping *smap; - AstMapping *tmap; - AstMapping *tmap2; - AstMapping *tmap1; - AstPolyMap *polymap; - AstPermMap *pm; - const char *cval; - char buf[30]; - double ****item; - double *coeffs; - double *pc; - double *scales; - double *shifts; - double fit[ 6 ]; - double iwcxin; - double iwcyin; - double lbnd[ 2 ]; - double ubnd[ 2 ]; - double val; - int *inax1; - int *inax2; - int *inperm1; - int *inperm2; - int *invert_list; - int *outperm1; - int *outperm2; - int *outrem; - int fwd; - int i; - int icoeff; - int iin; - int imap; - int imap_pm; - int iout; - int ioutrem; - int jm; - int ncoeff; - int nin; - int nmap; - int nout; - int noutrem; - int ok; - int old_invert; - int outax[ 2 ]; - int aimax; - int ajmmax; - int bimax; - int bjmmax; - -/* Initialise */ - result = NULL; - -/* Check the inherited status. */ - if( !astOK ) return result; - -/* Get the number of inputs and outputs for the Mapping. */ - nin = astGetNin( map ); - nout = astGetNin( map ); - -/* Check both transformations are defined in the supplied Mapping. */ - if( astGetTranForward( map ) && astGetTranInverse( map ) ) { - -/* Attempt to split the supplied Mapping to generate a (2-input,2-output) - Mapping that goes from grid coords to celestial longitude and latitude. - Since we want to specify the retained output, we need to invert the - Mapping, because astMapSplit only allows us to specify the retained - inputs. */ - astInvert( map ); - outax[ 0 ] = lonax; - outax[ 1 ] = latax; - inax1 = astMapSplit( map, 2, outax, &tmap1 ); - astInvert( map ); - -/* Check the Mapping could be split, and that the mapping that generates - lonax/latax has exactly two inputs (use the NBout attribute since - "tmap1" is inverted). Then invert "tmap1" so that it is in the same - direction as the supplied mapping. */ - if( inax1 && tmap1 && astGetNout( tmap1 ) == 2 ) { - astInvert( tmap1 ); - inaxes[ 0 ] = inax1[ 0 ]; - inaxes[ 1 ] = inax1[ 1 ]; - -/* Search this list of Mappings for a PolyMap. First simplify it, then - expand it as a list of Mappings in series. Then look through the list - for a PolyMap. */ - polymap = NULL; - smap = astSimplify( tmap1 ); - nmap = 0; - map_list = NULL; - invert_list = NULL; - (void) astMapList( smap, 1, astGetInvert(smap), &nmap, &map_list, - &invert_list ); - for( imap = 0; imap < nmap; imap++ ) { - if( astIsAPolyMap( map_list[ imap ] ) ) { - imap_pm = imap; - polymap = astCopy( map_list[ imap ] ); - astSetInvert( polymap, invert_list[ imap ] ); - break; - } - } - -/* If a PolyMap is found, check it conforms to the requirements of the - SIP convention. */ - if( polymap ){ - if( astGetNin( polymap ) == 2 && astGetNout( polymap ) == 2 ){ - -/* Check that each Mapping before the PolyMap is a shift of origin, and - accumulate them into a single CmpMap. */ - map_lower = NULL; - ok = 1; - for( imap = 0; ok && imap < imap_pm; imap++ ) { - old_invert = astGetInvert( map_list[ imap ] ); - astSetInvert( map_list[ imap ], invert_list[ imap ] ); - - if( astGetNin( map_list[ imap ] ) != 2 ){ - ok = 0; - - } else if( astIsAWinMap( map_list[ imap ] ) ) { - astWinTerms( map_list[ imap ], &shifts, &scales ); - if( scales[ 0 ] != 1.0 || scales[ 1 ] != 1.0 ) ok = 0; - - } else if( !astIsAShiftMap( map_list[ imap ] ) && - !astIsAUnitMap( map_list[ imap ] ) ) { - ok = 0; - } - - if( map_lower ) { - tmap = (AstMapping *) astCmpMap( map_lower, map_list[ imap ], 1, " ", status ); - (void) astAnnul( map_lower ); - map_lower = tmap; - } else { - map_lower = astCopy( map_list[ imap ] ); - } - - astSetInvert( map_list[ imap ], old_invert ); - } - -/* Check that each Mapping after the PolyMap is a scaling or matrix, - and accumulate them into a single CmpMap. */ - map_upper = NULL; - for( imap = imap_pm + 1; ok && imap < nmap; imap++ ) { - old_invert = astGetInvert( map_list[ imap ] ); - astSetInvert( map_list[ imap ], invert_list[ imap ] ); - - if( astGetNin( map_list[ imap ] ) != 2 ){ - ok = 0; - - } else if( astIsAWinMap( map_list[ imap ] ) ) { - astWinTerms( map_list[ imap ], &shifts, &scales ); - if( shifts[ 0 ] != 0.0 || shifts[ 1 ] != 0.0 ) ok = 0; - - } else if( !astIsAMatrixMap( map_list[ imap ] ) && - !astIsAZoomMap( map_list[ imap ] ) && - !astIsAUnitMap( map_list[ imap ] ) ) { - ok = 0; - } - - if( map_upper ) { - tmap = (AstMapping *) astCmpMap( map_upper, map_list[ imap ], 1, " ", status ); - (void) astAnnul( map_upper ); - map_upper = tmap; - } else { - map_upper = astCopy( map_list[ imap ] ); - } - - astSetInvert( map_list[ imap ], old_invert ); - } - -/* Split the supplied Mapping to generate the Mapping that gives - any remaining non-celestial output axes. We only need to do this if - the supplied Mapping has any surplus inputs or outputs. */ - inax2 = NULL; - tmap2 = NULL; - outrem = NULL; - - if( nout > 2 ) { - noutrem = nout - 2; - outrem = astMalloc( noutrem*sizeof(int) ); - if( astOK ) { - ioutrem = 0; - for( iout = 0; iout < nout; iout++ ) { - if( iout != lonax && iout != latax ) outrem[ ioutrem++ ] = iout; - } - - astInvert( map ); - inax2 = astMapSplit( map, noutrem, outrem, &tmap2 ); - astInvert( map ); - if( tmap2 ) { - astInvert( tmap2 ); - } else { - ok = 0; - } - } - - } else if( nout != 2 || nin != 2 ) { - ok = 0; - } - -/* If the above tests were passed, transform the origin of IWC (the total map - output space) into grid coords. This gives CRPIX. */ - if( ok ) { - iwcxin = 0.0; - iwcyin = 0.0; - astTran2( smap, 1, &iwcxin, &iwcyin, 0, crpix, crpix + 1 ); - -/* Determine the CD matrix. We already know the upper Mapping is linear - because we have checked that it contains only linear atomic mappings. - So we can use fixed bounds for the fitting area safely. */ - lbnd[ 0 ] = -1.0; - lbnd[ 1 ] = -1.0; - ubnd[ 0 ] = 1.0; - ubnd[ 1 ] = 1.0; - if( !astLinearApprox( map_upper, lbnd, ubnd, 0.01, fit ) ) { - astError( AST__INTER, "%s(%s): SipIntWorld: Mapping " - "following PolyMap is not linear (internal " - "AST programming error).", status, method, - class ); - } - -/* Store the matrix elements in the required order. */ - cd[ 0 ] = fit[ 2 ]; - cd[ 1 ] = fit[ 3 ]; - cd[ 2 ] = fit[ 4 ]; - cd[ 3 ] = fit[ 5 ]; - -/* Store SIP headers describing first the forward then the inverse - transformation of the PolyMap in the FitsStore. Note, the axis indices - returned by astPolyCoeffs are 1-based. */ - for( fwd = 1; fwd >= 0; fwd-- ) { - if( ( fwd && astGetTranForward( polymap ) ) || - ( !fwd && astGetTranInverse( polymap ) ) ) { - astPolyCoeffs( polymap, fwd, 0, NULL, &ncoeff ); - coeffs = astMalloc( 4*ncoeff*sizeof(*coeffs) ); - if( astOK ) { - astPolyCoeffs( polymap, fwd, 4*ncoeff, coeffs, &ncoeff ); - -/* Find the maximum used power on each input axis. */ - aimax = 0; - ajmmax = 0; - bimax = 0; - bjmmax = 0; - pc = coeffs; - for( icoeff = 0; icoeff < ncoeff; icoeff++ ) { - if( inaxes[ 0 ] < inaxes [ 1 ] ) { - i = (int) ( pc[ 2 ] + 0.5 ); - jm = (int) ( pc[ 3 ] + 0.5 ); - if( pc[ 1 ] == 1 ) { - if( i > aimax ) aimax = i; - if( jm > ajmmax ) ajmmax = jm; - } else { - if( i > bimax ) bimax = i; - if( jm > bjmmax ) bjmmax = jm; - } - } else { - i = (int) ( pc[ 3 ] + 0.5 ); - jm = (int) ( pc[ 2 ] + 0.5 ); - if( pc[ 1 ] == 1 ) { - if( i > bimax ) bimax = i; - if( jm > bjmmax ) bjmmax = jm; - } else { - if( i > aimax ) aimax = i; - if( jm > ajmmax ) ajmmax = jm; - } - } - pc += 4; - } - -/* Initialise the arrays with bad values so that unused powers are not - included in the header. */ - - for( i = 0; i <= aimax; i++ ){ - for( jm = 0; jm <= ajmmax; jm++ ){ - SetItem( fwd? &(store->asip) : &(store->apsip), - i, jm, s, AST__BAD, status ); - } - } - - for( i = 0; i <= bimax; i++ ){ - for( jm = 0; jm <= bjmmax; jm++ ){ - SetItem( fwd? &(store->bsip) : &(store->bpsip), - i, jm, s, AST__BAD, status ); - } - } - -/* Over-write the bad values with real values for the powers that are - actually used. Reduce the coefficients of the linear terms by 1.0 - since the SIP distortion is an additive correction, rather than a direct - transformation. */ - pc = coeffs; - for( icoeff = 0; icoeff < ncoeff; icoeff++ ) { - if( inaxes[ 0 ] < inaxes [ 1 ] ) { - if( pc[ 1 ] == 1 ) { - item = fwd ? &(store->asip) : &(store->apsip); - } else { - item = fwd ? &(store->bsip) : &(store->bpsip); - } - i = (int) ( pc[ 2 ] + 0.5 ); - jm = (int) ( pc[ 3 ] + 0.5 ); - } else { - if( pc[ 1 ] == 1 ) { - item = fwd ? &(store->bsip) : &(store->bpsip); - } else { - item = fwd ? &(store->asip) : &(store->apsip); - } - i = (int) ( pc[ 3 ] + 0.5 ); - jm = (int) ( pc[ 2 ] + 0.5 ); - } - - val = pc[ 0 ]; - if( ( pc[ 1 ] == 1 && i == 1 && jm == 0 ) || - ( pc[ 1 ] == 2 && i == 0 && jm == 1 ) ){ - val -= 1.0; - } - if( val != 0.0 && val != AST__BAD ) { - SetItem( item, i, jm, s, val, status ); - } - pc += 4; - } - } - coeffs = astFree( coeffs ); - } - } - -/* Change the CTYPE value to indicate SIP distortion is in use. */ - cval = GetItemC( &(store->ctype), latax, 0, s, NULL, method, - class, status ); - if( cval ){ - strcpy( buf, cval ); - strcpy( buf + 8, "-SIP" ); - SetItemC( &(store->ctype), latax, 0, s, buf, status ); - } - - cval = GetItemC( &(store->ctype), lonax, 0, s, NULL, method, - class, status ); - if( cval ){ - strcpy( buf, cval ); - strcpy( buf + 8, "-SIP" ); - SetItemC( &(store->ctype), lonax, 0, s, buf, status ); - } - -/* Construct the returned Mapping. This is equivalent to the supplied - Mapping, but without the PolyMap. Use PermMaps at beginning and end to - take account of any axis permutations introduced by the operation of - astMapSplit. First put the 2D Mapping preceding the PolyMap in series - with the 2D Mapping following the PolyMap. */ - result = (AstMapping *) astCmpMap( map_lower, map_upper, 1, " ", status ); - -/* Now put the above Mapping in parallel with the mMapping that - transforms any additional axes. */ - if( tmap2 ) { - tmap = (AstMapping *) astCmpMap( result, tmap2, 0, " ", status ); - (void) astAnnul( result ); - result = tmap; - } - -/* Create a PermMap that permutes the outputs of the above Mapping back - into their original order. */ - inperm1 = astMalloc( nout*sizeof(int) ); - outperm1 = astMalloc( nout*sizeof(int) ); - inperm2 = astMalloc( nin*sizeof(int) ); - outperm2 = astMalloc( nin*sizeof(int) ); - if( astOK ) { - inperm1[ 0 ] = lonax; - inperm1[ 1 ] = latax; - outperm1[ lonax ] = 0; - outperm1[ latax ] = 1; - if( tmap2 ) { - for( iout = 0; iout < noutrem; iout++ ) { - inperm1[ iout + 2 ] = outrem[ iout ]; - outperm1[ outrem[ iout ] ] = iout + 2; - } - } - pm = astPermMap( nout, inperm1, nout, outperm1, - NULL, " ", status ); - -/* Put this PermMap in series with (following) the main Mapping created - above. */ - tmap = (AstMapping *) astCmpMap( result, pm, 1, " ", status ); - (void) astAnnul( result ); - pm = astAnnul( pm ); - result = tmap; - -/* Create a PermMap that permutes the inputs of the above Mapping back - into their original order. */ - outperm2[ 0 ] = inax1[ 0 ]; - outperm2[ 1 ] = inax1[ 1 ]; - inperm2[ inax1[ 0 ] ] = 0; - inperm2[ inax1[ 1 ] ] = 1; - - if( tmap2 ) { - for( iin = 0; iin < nin - 2; iin++ ) { - outperm2[ iin + 2 ] = inax2[ iin ]; - inperm2[ inax2[ iin ] ] = iin + 2; - } - } - - pm = astPermMap( nin, inperm2, nin, outperm2, - NULL, " ", status ); - -/* Put this PermMap in series with (preceding) the main Mapping created - above. */ - tmap = (AstMapping *) astCmpMap( pm, result, 1, " ", status ); - (void) astAnnul( result ); - pm = astAnnul( pm ); - result = tmap; - } - -/* Free resources. */ - inperm1 = astFree( inperm1 ); - inperm2 = astFree( inperm2 ); - outperm1 = astFree( outperm1 ); - outperm2 = astFree( outperm2 ); - } - - inax2 = astFree( inax2 ); - outrem = astFree( outrem ); - if( tmap2 ) tmap2 = astAnnul( tmap2 ); - if( map_lower ) map_lower = astAnnul( map_lower ); - if( map_upper ) map_upper = astAnnul( map_upper ); - } - - polymap = astAnnul( polymap ); - } - - for( imap = 0; imap < nmap; imap++ ) { - map_list[ imap ] = astAnnul( map_list[ imap ] ); - } - - invert_list = astFree( invert_list ); - map_list = astFree( map_list ); - inax1 = astFree( inax1 ); - smap = astAnnul( smap ); - } - if( tmap1 ) tmap1 = astAnnul( tmap1 ); - } - -/* Return the Mapping. */ - return result; -} - -static AstMapping *SIPMapping( AstFitsChan *this, double *dim, FitsStore *store, - char s, int naxes, const char *method, - const char *class, int *status ){ -/* -* Name: -* SIPMapping - -* Purpose: -* Create a Mapping descriping "-SIP" (Spitzer) distortion. - -* Type: -* Private function. - -* Synopsis: -* AstMapping *SIPMapping( AstFitsChan *this, double *dim, FitsStore *store, -* char s, int naxes, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function uses the values in the supplied FitsStore to create a -* Mapping which implements the "-SIP" distortion code. This is the - -* code used by the Spitzer project and is described in: -* -* http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf -* -* SIP distortion can only be applied to axes 0 and 1. Other axes are -* passed unchanged by the returned Mapping. - -* Parameters: -* this -* The FitsChan. -* dim -* The dimensions of the array in pixels. AST__BAD is stored for -* each value if dimensions are not known. -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* naxes -* The number of intermediate world coordinate axes (WCSAXES). -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the Mapping. -*/ - -/* Local Variables: */ - AstMapping *ret; /* Pointer to the returned Mapping */ - AstPolyMap *pmap; /* PolyMap describing the distortion */ - AstPolyMap *pmap2; /* New PolyMap describing the distortion */ - double ****item; /* Address of FitsStore item to use */ - double *c; /* Pointer to start of coefficient description */ - double *coeff_f; /* Array of coeffs. for forward transformation */ - double *coeff_i; /* Array of coeffs. for inverse transformation */ - double cof; /* Coefficient value */ - double lbnd[ 2 ]; /* Lower bounds of fitted region */ - double ubnd[ 2 ]; /* Upper bounds of fitted region */ - int def; /* Is transformation defined? */ - int iin; /* Input (u or v) index */ - int iout; /* Output (U or V) index */ - int ncoeff_f; /* No. of coeffs. for forward transformation */ - int ncoeff_i; /* No. of coeffs. for inverse transformation */ - int p; /* Power of u or U */ - int pmax; /* Max power of u or U */ - int q; /* Power of v or V */ - int qmax; /* Max power of v or V */ - -/* Initialise the pointer to the returned Mapping. */ - ret = NULL; - -/* Check the global status. */ - if ( !astOK ) return ret; - -/* Store coefficients of the forward transformation: - ================================================ */ - -/* Indicate that we have as yet no coefficients for the forward polynomials. */ - ncoeff_f = 0; - -/* Indicate that we do not yet have any evidence that the forward - transformation is defined. */ - def = 0; - -/* Allocate workspace to hold descriptions of (initially) 20 coefficients used - within the forward polynomials. */ - coeff_f = astMalloc( sizeof( double )*20 ); - -/* Store the coefficients of the polynomial which produces each output - axis (U or V) in turn. */ - for( iout = 0; iout < 2; iout++ ){ - -/* Get a pointer to the FitsStore item holding the values defining this - output. */ - item = ( iout == 0 ) ? &(store->asip) : &(store->bsip); - -/* Get the largest powers used of u and v. */ - pmax = GetMaxI( item, s, status ); - qmax = GetMaxJM( item, s, status ); - -/* Loop round all combination of powers. */ - for( p = 0; p <= pmax; p++ ){ - for( q = 0; q <= qmax; q++ ){ - -/* Get the polynomial coefficient for this combination of powers. */ - cof = GetItem( item, p, q, s, NULL, method, class, status ); - -/* If there is no coefficient for this combination of powers, use a value - of zero. Otherwise indicate we have found at least one coefficient. */ - if( cof == AST__BAD ) { - cof = 0.0; - } else { - def = 1; - } - -/* The distortion polynomial gives a correction to be added on to the - input value. On the other hand, the returned Mapping is a direct - transformation from input to output. Therefore increment the coefficient - value by 1 for the term which corresponds to the current output axis. */ - if( p == ( 1 - iout ) && q == iout ) cof += 1.0; - -/* If the coefficient is not zero, store it in the array of coefficient - descriptions. */ - if( cof != 0.0 ) { - -/* Increment the number of coefficients for the forward polynomials. */ - ncoeff_f++; - -/* Ensure the "coeff_f" array is large enough to hold the new coefficient. */ - coeff_f = astGrow( coeff_f, sizeof( double )*4, ncoeff_f ); - if( astOK ) { - -/* Store it. Each coefficient is described by 4 values (since we have 2 - inputs to the Mapping). The first is the coefficient value, the second - is the (1-based) index of the output to which the coefficient relates. - The next is the power of input 0, and the last one is the power of input 1. */ - c = coeff_f + 4*( ncoeff_f - 1 ); - c[ 0 ] = cof; - c[ 1 ] = iout + 1; - c[ 2 ] = p; - c[ 3 ] = q; - } - } - } - } - } - -/* If no coefficients were supplied in the FitsStore, the forward - transformation is undefined. */ - if( !def ) ncoeff_f = 0; - -/* Store coefficients of the inverse transformation: - ================================================ */ - -/* Indicate that we have as yet no coefficients for the inverse polynomials. */ - ncoeff_i = 0; - -/* Indicate that we do not yet have any evidence that the forward - transformation is defined. */ - def = 0; - -/* Allocate workspace to hold descriptions of (initially) 20 coefficients used - within the inverse polynomials. */ - coeff_i = astMalloc( sizeof( double )*20 ); - -/* Store the coefficients of the polynomial which produces each input - axis (u or v) in turn. */ - for( iin = 0; iin < 2; iin++ ){ - -/* Get a pointer to the FitsStore item holding the values defining this - output. */ - item = ( iin == 0 ) ? &(store->apsip) : &(store->bpsip); - -/* Get the largest powers used of U and V. */ - pmax = GetMaxI( item, s, status ); - qmax = GetMaxJM( item, s, status ); - -/* Loop round all combination of powers. */ - for( p = 0; p <= pmax; p++ ){ - for( q = 0; q <= qmax; q++ ){ - -/* Get the polynomial coefficient for this combination of powers. */ - cof = GetItem( item, p, q, s, NULL, method, class, status ); - -/* If there is no coefficient for this combination of powers, use a value - of zero. Otherwise indicate we have found at least one coefficient. */ - if( cof == AST__BAD ) { - cof = 0.0; - } else { - def = 1; - } - -/* The distortion polynomial gives a correction to be added on to the - output value. On the other hand, the returned Mapping is a direct - transformation from output to input. Therefore increment the coefficient - value by 1 for the term which corresponds to the current input axis. */ - if( p == ( 1 - iin ) && q == iin ) cof += 1.0; - -/* If the coefficient is not zero, store it in the array of coefficient - descriptions. */ - if( cof != 0.0 ) { - -/* Increment the number of coefficients for the inverse polynomials. */ - ncoeff_i++; - -/* Ensure the "coeff_i" array is large enough to hold the new coefficient. */ - coeff_i = astGrow( coeff_i, sizeof( double )*4, ncoeff_i ); - if( astOK ) { - -/* Store it. Each coefficient is described by 4 values (since we have 2 - outputs to the Mapping). The first is the coefficient value, the second - is the (1-based) index of the input to which the coefficient relates. The - next is the power of output 0, and the last one is the power of output 1. */ - c = coeff_i + 4*( ncoeff_i - 1 ); - c[ 0 ] = cof; - c[ 1 ] = iin + 1; - c[ 2 ] = p; - c[ 3 ] = q; - } - } - } - } - } - -/* If no coefficients were supplied in the FitsStore, the forward - transformation is undefined. */ - if( !def ) ncoeff_i = 0; - -/* Create the returned Mapping: - ============================ */ - -/* If neither transformation is defined, create a UnitMap. */ - if( ncoeff_f == 0 && ncoeff_i == 0 ){ - ret = (AstMapping *) astUnitMap( naxes, "", status ); - -/* Otherwise, create a PolyMap to describe axes 0 and 1. */ - } else { - pmap = astPolyMap( 2, 2, ncoeff_f, coeff_f, ncoeff_i, coeff_i, "", status ); - -/* The inverse transformations supplied within SIP headers are often - inaccurate. So replace any existing inverse by sampling the supplied - transformation, and fitting a polynomial to the sampled positions. If - the fit fails to reach 0.01 pixel accuracy, forget it and rely on the - (slower) iterative inverse provided by the PolyMap class. Do the fit - over an area three times the size of the image to provide accurate - values outside the image. Only do this if it has not been disabled - using attribute SipReplace. */ - if( astGetSipReplace( this ) ) { - lbnd[ 0 ] = ( dim[ 0 ] != AST__BAD ) ? -dim[ 0 ] : -1000.0; - lbnd[ 1 ] = ( dim[ 1 ] != AST__BAD ) ? -dim[ 1 ] : -1000.0; - ubnd[ 0 ] = ( dim[ 0 ] != AST__BAD ) ? 2*dim[ 0 ] : 2000.0; - ubnd[ 1 ] = ( dim[ 1 ] != AST__BAD ) ? 2*dim[ 1 ] : 2000.0; - pmap2 = astPolyTran( pmap, (ncoeff_f == 0), 0.0001, 0.01, 7, lbnd, - ubnd ); - if( pmap2 ) { - (void) astAnnul( pmap ); - pmap = pmap2; - } else { - astSet( pmap, "IterInverse=1,NiterInverse=6,TolInverse=1.0E-8", - status ); - } - } - -/* Add the above Mapping in parallel with a UnitMap which passes any - other axes unchanged. */ - ret = AddUnitMaps( (AstMapping *) pmap, 0, naxes, status ); - pmap = astAnnul( pmap ); - } - -/* Free resources. */ - coeff_f = astFree( coeff_f ); - coeff_i = astFree( coeff_i ); - -/* Return the result. */ - return ret; -} - -static void SkyPole( AstWcsMap *map2, AstMapping *map3, int ilon, int ilat, - int *wperm, char s, FitsStore *store, const char *method, - const char *class, int *status ){ -/* -* Name: -* SkyPole - -* Purpose: -* Put values for FITS keywords LONPOLE and LATPOLE into a FitsStore. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void SkyPole( AstWcsMap *map2, AstMapping *map3, int ilon, int ilat, -* int *wperm, char s, FitsStore *store, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function calculates values for the LONPOLE and LATPOLE FITS -* keywords and stores them in the supplied FitsStore. LONPOLE and -* LATPOLE are the longitude and latitude of the celestial north pole -* in native spherical coordinates. - -* Parameters: -* map2 -* Pointer to the Mapping from Intermediate World Coordinates to Native -* Spherical Coordinates. -* map3 -* Pointer to the Mapping from Native Spherical Coordinates to celestial -* coordinates. -* ilon -* Zero-based index of longitude output from "map3". -* ilat -* Zero-based index of latitude output from "map3". -* wperm -* Pointer to an array of integers with one element for each axis of -* the current Frame. Each element holds the zero-based -* index of the FITS-WCS axis (i.e. the value of "i" in the keyword -* names "CTYPEi", "CRVALi", etc) which describes the Frame axis. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* store -* The FitsStore in which to store the FITS WCS keyword values. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - AstPointSet *pset1; /* PointSet holding intermediate wcs coords */ - AstPointSet *pset2; /* PointSet holding final WCS coords */ - double **ptr1; /* Pointer to coordinate data */ - double **ptr2; /* Pointer to coordinate data */ - double alpha0; /* Long. of fiducial point in standard system */ - double alphap; /* Celestial longitude of native north pole */ - double deflonpole; /* Default value for lonpole */ - double delta0; /* Lat. of fiducial point in standard system */ - double latpole; /* Native latitude of celestial north pole */ - double lonpole; /* Native longitude of celestial north pole */ - double phi0; /* Native longitude at fiducial point */ - double theta0; /* Native latitude at fiducial point */ - int axlat; /* Index of latitude output from "map2" */ - int axlon; /* Index of longitude output from "map2" */ - int fits_ilat; /* FITS WCS axis index for latitude axis */ - int fits_ilon; /* FITS WCS axis index for longitude axis */ - int iax; /* Axis index */ - int nax; /* Number of IWC axes */ - int nax2; /* Number of WCS axes */ - -/* Check the inherited status. */ - if( !astOK ) return; - -/* Store the indices of the native longitude and latitude outputs of the - WcsMap. */ - axlon = astGetWcsAxis( map2, 0 ); - axlat = astGetWcsAxis( map2, 1 ); - -/* Store the indices of the FITS WCS axes for longitude and latitude */ - fits_ilon = wperm[ ilon ]; - fits_ilat = wperm[ ilat ]; - -/* To find the longitude and latitude of the celestial north pole in native - spherical coordinates, we will transform the coords of the celestial north - pole into spherical cords using the inverse of "map2", and if the resulting - native spherical coords differ from the default values of LONPOLE and - LATPOLE, we store them in the FitsStore. However, for zenithal projections, - any value can be used simply by introducing an extra rotation into the - (X,Y) projection plane. If values have been set in the WcsMap (as - projection parameters PVi_3 and PVi_4 for longitude axis "i") uses - them. Otherwise, set the values bad to indicate that the default values - should be used. Note, these projection parameters are used for other - purposes in a TPN projection. */ - lonpole = AST__BAD; - latpole = AST__BAD; - if( astIsZenithal( map2 ) ) { - if( astGetWcsType( map2 ) != AST__TPN ) { - lonpole = astTestPV( map2, axlon, 3 ) ? astGetPV( map2, axlon, 3 ) - : AST__BAD; - latpole = astTestPV( map2, axlon, 4 ) ? astGetPV( map2, axlon, 4 ) - : AST__BAD; - } - -/* For non-zenithal projections, do the full calculation. */ - } else { - -/* Allocate resources. */ - nax = astGetNin( map2 ); - pset1 = astPointSet( 1, nax, "", status ); - ptr1 = astGetPoints( pset1 ); - nax2 = astGetNout( map3 ); - pset2 = astPointSet( 1, nax2, "", status ); - ptr2 = astGetPoints( pset2 ); - if( astOK ) { - -/* Calculate the longitude and latitude of the celestial north pole - in native spherical coordinates (using the inverse of map3). These - values correspond to the LONPOLE and LATPOLE keywords. */ - for( iax = 0; iax < nax2; iax++ ) ptr2[ iax ][ 0 ] = 0.0; - ptr2[ ilat ][ 0 ] = AST__DPIBY2; - (void) astTransform( map3, pset2, 0, pset1 ); - -/* Retrieve the latitude and longitude (in the standard system) of the - fiducial point (i.e. CRVAL), in radians. */ - delta0 = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status ); - if( delta0 == AST__BAD ) delta0 = 0.0; - delta0 *= AST__DD2R; - alpha0 = GetItem( &(store->crval), fits_ilon, 0, s, NULL, method, class, status ); - if( alpha0 == AST__BAD ) alpha0 = 0.0; - alpha0 *= AST__DD2R; - -/* The default value of the LATPOLE is defined by equation 8 of FITS-WCS - paper II (taking the +ve signs). Find this value. */ - if( WcsNatPole( NULL, map2, alpha0, delta0, 999.0, ptr1[ axlon ], - &alphap, &latpole, status ) ){ - -/* If the default value is defined, compare it to the latitude of the - north pole found above. If they are equal use a bad value instead to - prevent an explicit keyword from being added to the FitsChan. */ - if( EQUALANG( ptr1[ axlat ][ 0 ], latpole ) ) { - latpole = AST__BAD; - } else { - latpole = ptr1[ axlat ][ 0 ]; - } - -/* If the default value is not defined, always store an explicit LATPOLE - value. */ - } else { - latpole = ptr1[ axlat ][ 0 ]; - } - -/* The default LONPOLE value is zero if the celestial latitude at the - fiducial point is greater than or equal to the native latitude at the - fiducial point. Otherwise, the default is (+ or -) 180 degrees. If LONPOLE - takes the default value, replace it with AST__BAD to prevent an explicit - keyword being stored in the FitsChan. */ - GetFiducialNSC( map2, &phi0, &theta0, status ); - lonpole = palDranrm( ptr1[ axlon ][ 0 ] ); - if( delta0 >= theta0 ){ - deflonpole = 0.0; - } else { - deflonpole = AST__DPI; - } - if( EQUALANG( lonpole, deflonpole ) ) lonpole = AST__BAD; - } - -/* Convert from radians to degrees. */ - if( lonpole != AST__BAD ) lonpole *= AST__DR2D; - if( latpole != AST__BAD ) latpole *= AST__DR2D; - -/* Free resources. */ - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - } - -/* Store these values. */ - SetItem( &(store->lonpole), 0, 0, s, lonpole, status ); - SetItem( &(store->latpole), 0, 0, s, latpole, status ); - -/* FITS-WCS paper 2 recommends putting a copy of LONPOLE and LATPOLE in - projection parameters 3 and 4 associated with the longitude axis. Only do - this if the projection is not TPN (since this projection uses these - parameters for other purposes). */ - if( astGetWcsType( map2 ) != AST__TPN ) { - SetItem( &(store->pv), fits_ilon, 3, s, lonpole, status ); - SetItem( &(store->pv), fits_ilon, 4, s, latpole, status ); - } -} - -static int SkySys( AstFitsChan *this, AstSkyFrame *skyfrm, int wcstype, - int wcsproj, FitsStore *store, int axlon, int axlat, char s, - int isoff, const char *method, const char *class, int *status ){ -/* -* Name: -* SkySys - -* Purpose: -* Return FITS-WCS values describing a sky coordinate system. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int SkySys( AstFitsChan *this, AstSkyFrame *skyfrm, int wcstype, -* int wcsproj, FitsStore *store, int axlon, int axlat, char s, -* int isoff, const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function sets values for the following FITS-WCS keywords -* within the supplied FitsStore structure: CTYPE, CNAME, RADESYS, EQUINOX, -* MJDOBS, CUNIT, OBSGEO-X/Y/Z. The values are derived from the supplied -* SkyFrame and WcsMap. - -* Parameters: -* this -* Pointer to the FitsChan. -* skyfrm -* A pointer to the SkyFrame to be described. -* wcstype -* The type of WCS: 0 = TAB, 1 = WcsMap projection. -* wcsproj -* An identifier for the type of WCS projection to use. Should be -* one of the values defined by the WcsMap class. Only used if "wcstype" -* is 1. -* store -* A pointer to the FitsStore structure in which to store the -* results. -* axlon -* The index of the FITS WCS longitude axis (i.e. the value of "i" -* in "CTYPEi"). -* axlat -* The index of the FITS WCS latitude axis (i.e. the value of "i" -* in "CTYPEi"). -* s -* Co-ordinate version character. -* isoff -* If greater than zero, the description to add to the FitsStore -* should describe offset coordinates. If less than zero, the -* description to add to the FitsStore should describe absolute -* coordinates but should include the SkyRefIs, SkyRef and SkyRefP -* attributes. If zero, ignore all offset coordinate info. The -* absolute value indicates the nature of the reference point: -* 1 == "pole", 2 == "origin", otherwise "ignored". -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Are the keywords values in the FitsStore usable? -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - char *label; /* Pointer to axis label string */ - char attr[20]; /* Buffer for AST attribute name */ - char com[80]; /* Buffer for keyword comment */ - char lattype[MXCTYPELEN];/* Latitude axis CTYPE value */ - char lontype[MXCTYPELEN];/* Longitude axis CTYPE value */ - const char *latsym; /* SkyFrame latitude axis symbol */ - const char *lonsym; /* SkyFrame longitude axis symbol */ - const char *prj_name; /* Pointer to projection name string */ - const char *skyref; /* Formatted SkyRef position */ - const char *skyrefis; /* SkyRefIs value */ - const char *sys; /* Celestal coordinate system */ - const char *timesys; /* Timescale specified in FitsChan */ - double ep; /* Epoch of observation in required timescale (MJD) */ - double ep_tdb; /* Epoch of observation in TDB timescale (MJD) */ - double ep_utc; /* Epoch of observation in UTC timescale (MJD) */ - double eq; /* Epoch of reference equinox (MJD) */ - double geolat; /* Geodetic latitude of observer (radians) */ - double geolon; /* Geodetic longitude of observer (radians) */ - double h; /* Geodetic altitude of observer (metres) */ - double skyref_lat; /* SkyRef latitude value (rads) */ - double skyrefp_lat; /* SkyRefP latitude value (rads) */ - double skyref_lon; /* SkyRef longitude value (rads) */ - double skyrefp_lon; /* SkyRefP longitude value (rads) */ - double xyz[3]; /* Geocentric position vector (in m) */ - int defdate; /* Can the date keywords be defaulted? */ - int i; /* Character count */ - int isys; /* Celestial coordinate system */ - int latax; /* Index of latitude axis in SkyFrame */ - int lonax; /* Index of longitude axis in SkyFrame */ - int ok; /* Do axis symbols conform to FITS-WCS CTYPE form? */ - int old_ignore_used; /* Original setting of external ignore_used variable */ - int ret; /* Returned flag */ - -/* Check the status. */ - if( !astOK ) return 0; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Check we have a SkyFrame. */ - if( !IsASkyFrame( skyfrm ) ) return 0; - -/* Initialise */ - ret = 1; - -/* Get the equinox, epoch of observation, and system of the SkyFrame. The epoch - is in TDB. It is assumed the Equinox is in UTC. */ - eq = astGetEquinox( skyfrm ); - sys = astGetC( skyfrm, "system" ); - ep_tdb = astTestEpoch( skyfrm ) ? astGetEpoch( skyfrm ) : AST__BAD; - -/* Convert the epoch to UTC. */ - ep_utc = TDBConv( ep_tdb, AST__UTC, 1, method, class, status ); - -/* See if the FitsChan contains a value for the TIMESYS keyword (include - previously used cards in the search). If so, and if it is not UTC, convert - the epoch to the specified time scale, and store a TIMESYS value in the - FitsStore. */ - old_ignore_used = ignore_used; - ignore_used = 0; - if( GetValue( this, "TIMESYS", AST__STRING, (void *) ×ys, 0, 0, method, - class, status ) && strcmp( timesys, "UTC" ) ) { - ep = TDBConv( ep_tdb, TimeSysToAst( this, timesys, method, class, - status ), - 1, method, class, status ); - SetItemC( &(store->timesys), 0, 0, s, timesys, status ); - -/* If no TIMESYS keyword was found in the FitsChan, or the timesys was - UTC, we use the UTC epoch value found above. In this case no TIMESYS value - need be stored in the FitsSTore since UTC is the default for TIMESYS. */ - } else { - ep = ep_utc; - } - -/* Reinstate the original value for the flag that indicates whether keywords - in the FitsChan that have been used previously should be ignored. */ - ignore_used = old_ignore_used; - -/* The MJD-OBS and DATE-OBS keywords default to the epoch of the - reference equinox if not supplied. Therefore MJD-OBS and DATE-OBS do - not need to be stored in the FitsChan if the epoch of observation is - the same as the epoch of the reference equinox. This can avoid - producing FITS headers which say unlikely things like - DATE-OBS = "01/01/50". Set a flag indicating if MJD-OBS and DATE-OBS - can be defaulted. */ - defdate = astEQUAL( ep_utc, eq ); - -/* Convert the equinox to a Julian or Besselian epoch. Also get the - reference frame and standard system. */ - if( !Ustrcmp( sys, "FK4", status ) ){ - eq = palEpb( eq ); - isys = RADEC; - SetItemC( &(store->radesys), 0, 0, s, "FK4", status ); - } else if( !Ustrcmp( sys, "FK4_NO_E", status ) || !Ustrcmp( sys, "FK4-NO-E", status ) ){ - eq = palEpb( eq ); - isys = RADEC; - SetItemC( &(store->radesys), 0, 0, s, "FK4-NO-E", status ); - } else if( !Ustrcmp( sys, "FK5", status ) ){ - eq = palEpj( eq ); - isys = RADEC; - SetItemC( &(store->radesys), 0, 0, s, "FK5", status ); - } else if( !Ustrcmp( sys, "ICRS", status ) ){ - eq = AST__BAD; - isys = RADEC; - SetItemC( &(store->radesys), 0, 0, s, "ICRS", status ); - } else if( !Ustrcmp( sys, "GAPPT", status ) || - !Ustrcmp( sys, "Apparent", status ) || - !Ustrcmp( sys, "Geocentric", status ) ){ - eq = AST__BAD; - isys = RADEC; - SetItemC( &(store->radesys), 0, 0, s, "GAPPT", status ); - } else if( !Ustrcmp( sys, "Helioecliptic", status ) ){ - eq = AST__BAD; - isys = HECLIP; - } else if( !Ustrcmp( sys, "Galactic", status ) ){ - eq = AST__BAD; - isys = GALAC; - } else if( !Ustrcmp( sys, "Supergalactic", status ) ){ - eq = AST__BAD; - isys = SUPER; - } else if( !Ustrcmp( sys, "AzEl", status ) ){ - eq = AST__BAD; - isys = AZEL; - } else { - eq = AST__BAD; - isys = NOCEL; - } - -/* Store these values. Only store the date if it does not take its - default value. */ - SetItem( &(store->equinox), 0, 0, s, eq, status ); - if( !defdate ) SetItem( &(store->mjdobs), 0, 0, ' ', ep, status ); - -/* Only proceed if we have usable values */ - if( astOK ) { - -/* Get the indices of the latitude and longitude axes within the - SkyFrame. */ - latax = astGetLatAxis( skyfrm ); - lonax = 1 - latax; - -/* The first 4 characters in CTYPE are determined by the celestial coordinate - system and the second 4 by the projection type. If we are describing - offset coordinates, then use "OFLN" and "OFLT. Otherwise use the - standard FITS-WCS name of the system. */ - if( isoff > 0 ){ - strcpy( lontype, "OFLN" ); - strcpy( lattype, "OFLT" ); - } else if( isys == RADEC ){ - strcpy( lontype, "RA--" ); - strcpy( lattype, "DEC-" ); - } else if( isys == ECLIP ){ - strcpy( lontype, "ELON" ); - strcpy( lattype, "ELAT" ); - } else if( isys == HECLIP ){ - strcpy( lontype, "HLON" ); - strcpy( lattype, "HLAT" ); - } else if( isys == GALAC ){ - strcpy( lontype, "GLON" ); - strcpy( lattype, "GLAT" ); - } else if( isys == SUPER ){ - strcpy( lontype, "SLON" ); - strcpy( lattype, "SLAT" ); - } else if( isys == AZEL ){ - strcpy( lontype, "AZ--" ); - strcpy( lattype, "EL--" ); - -/* For unknown systems, use the axis symbols within CTYPE if they conform - to the requirement of FITS-WCS (i.e. "xxLN/xxLT" or "xLON/xLAT") or use - "UNLN/UNLT" otherwise. */ - } else { - latsym = astGetSymbol( skyfrm, latax ); - lonsym = astGetSymbol( skyfrm, lonax ); - if( astOK ) { - - ok = 0; - if( strlen( latsym ) == 4 && strlen( lonsym ) == 4 ) { - if( !strcmp( latsym + 2, "LT" ) && - !strcmp( lonsym + 2, "LN" ) && - !strncmp( latsym, lonsym, 2 ) ) { - ok = 1; - } else if( !strcmp( latsym + 1, "LAT" ) && - !strcmp( lonsym + 1, "LON" ) && - !strncmp( latsym, lonsym, 1 ) ) { - ok = 1; - } - } - - if( !ok ) { - latsym = "UNLT"; - lonsym = "UNLN"; - } - - strncpy( lontype, lonsym, 4 ); - for( i = strlen( lonsym ); i < 4; i++ ) { - lontype[ i ] = '-'; - } - strncpy( lattype, latsym, 4 ); - for( i = strlen( latsym ); i < 4; i++ ) { - lattype[ i ] = '-'; - } - } - } - -/* Store the projection strings. */ - prj_name = ( wcstype == 0 ) ? "-TAB" : astWcsPrjName( wcsproj ); - if( astOK ) { - strcpy( lontype + 4, prj_name ); - strcpy( lattype + 4, prj_name ); - } - -/* Store the total CTYPE strings */ - SetItemC( &(store->ctype), axlon, 0, s, lontype, status ); - SetItemC( &(store->ctype), axlat, 0, s, lattype, status ); - -/* Store offset coord information. */ - if( isoff ) { - -/* If the description is for offset coords store suitable comments for - the CTYPE keywords. */ - if( isoff > 0 ) { - skyref = astGetC( skyfrm, "SkyRef" ); - - sprintf( attr, "Symbol(%d)", axlon + 1 ); - sprintf( com, "%s offset from %s",astGetC( skyfrm, attr )+1, skyref ); - SetItemC( &(store->ctype_com), axlon, 0, s, com, status ); - - sprintf( attr, "Symbol(%d)", axlat + 1 ); - sprintf( com, "%s offset from %s",astGetC( skyfrm, attr )+1, skyref ); - SetItemC( &(store->ctype_com), axlat, 0, s, com, status ); - -/* If the description is for absolute coords store the SkyFrame attribute - values in AST-specific keywords. */ - } else { - sprintf( attr, "SkyRef(%d)", axlon + 1 ); - skyref_lon = astGetD( skyfrm, attr ); - sprintf( attr, "SkyRef(%d)", axlat + 1 ); - skyref_lat = astGetD( skyfrm, attr ); - - sprintf( attr, "SkyRefP(%d)", axlon + 1 ); - skyrefp_lon = astGetD( skyfrm, attr ); - sprintf( attr, "SkyRefP(%d)", axlat + 1 ); - skyrefp_lat = astGetD( skyfrm, attr ); - - skyrefis = (isoff < -2) ? "IGNORED" : - ( (isoff < -1) ? "ORIGIN" : "POLE" ); - - SetItemC( &(store->skyrefis), 0, 0, s, skyrefis, status ); - if( astTest( skyfrm, "SkyRef(1)" ) ) { - SetItem( &(store->skyref), axlon, 0, s, skyref_lon, status ); - SetItem( &(store->skyref), axlat, 0, s, skyref_lat, status ); - } - if( astTest( skyfrm, "SkyRefP(1)" ) ) { - SetItem( &(store->skyrefp), axlon, 0, s, skyrefp_lon, status ); - SetItem( &(store->skyrefp), axlat, 0, s, skyrefp_lat, status ); - } - } - } - -/* If the Label attribute has been set for an axis, use it as the CTYPE - comment and CNAME value. */ - if( astTestLabel( skyfrm, latax ) ) { - label = (char *) astGetLabel( skyfrm, latax ); - SetItemC( &(store->ctype_com), axlat, 0, s, label, status ); - SetItemC( &(store->cname), axlat, 0, s, label, status ); - } - if( astTestLabel( skyfrm, lonax ) ) { - label = (char *) astGetLabel( skyfrm, lonax ); - SetItemC( &(store->ctype_com), axlon, 0, s, label, status ); - SetItemC( &(store->cname), axlon, 0, s, label, status ); - } - -/* Nullify any CUNITS strings for the longitude and latitude axes (they - always take the default value of degrees). */ - SetItemC( &(store->cunit), axlat, 0, s, NULL, status ); - SetItemC( &(store->cunit), axlon, 0, s, NULL, status ); - } - -/* Store the Domain name as the WCSNAME keyword (if set). */ - if( astTestDomain( skyfrm ) ) { - SetItemC( &(store->wcsname), 0, 0, s, (char *) astGetDomain( skyfrm ), status ); - } - -/* Store the observer's position if set (needed for definition of AzEl - systems). */ - if( astTestObsLon( skyfrm ) && astTestObsLat( skyfrm ) && s == ' ' ) { - geolon = astGetObsLon( skyfrm ); - geolat = astGetObsLat( skyfrm ); - h = astGetObsAlt( skyfrm ); - if( geolat != AST__BAD && geolon != AST__BAD && h != AST__BAD ) { - eraGd2gc( 1, geolon, geolat, h, xyz ); - SetItem( &(store->obsgeox), 0, 0, ' ', xyz[0], status ); - SetItem( &(store->obsgeoy), 0, 0, ' ', xyz[1], status ); - SetItem( &(store->obsgeoz), 0, 0, ' ', xyz[2], status ); - } - } - if( !astOK ) ret = 0; - return ret; -} - -static char *SourceWrap( const char *(* source)( void ), int *status ) { -/* -* Name: -* SourceWrap - -* Purpose: -* Wrapper function to invoke a C FitsChan source function. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* char *SourceWrap( const char *, int *status(* source)( void ) ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function invokes the source function whose pointer is -* supplied in order to read the next input line from an external -* data store. It then returns a pointer to a dynamic string -* containing a copy of the text that was read. - -* Parameters: -* source -* Pointer to a source function, with no parameters, that -* returns a pointer to a const, null-terminated string -* containing the text that it read. This is the form of FitsChan -* source function employed by the C language interface to the -* AST library. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a dynamically allocated, null terminated string -* containing a copy of the text that was read. This string must be -* freed by the caller (using astFree) when no longer required. -* -* A NULL pointer will be returned if there is no more input text -* to read. - -* 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: */ - char *result; /* Pointer value to return */ - const char *line; /* Pointer to input line */ - -/* Initialise. */ - result = NULL; - -/* Check the global error status. */ - if ( !astOK ) return result; - -/* Invoke the source function to read the next input line and return a - pointer to the resulting string. */ - line = ( *source )(); - -/* If a string was obtained, make a dynamic copy of it and save the - resulting pointer. */ - if ( line ) result = astString( line, (int) strlen( line ) ); - -/* Return the result. */ - return result; -} - -static AstMapping *SpectralAxes( AstFitsChan *this, AstFrameSet *fs, - double *dim, int *wperm, - char s, FitsStore *store, double *crvals, - int *axis_done, const char *method, - const char *class, int *status ){ - -/* -* Name: -* SpectralAxes - -* Purpose: -* Add values to a FitsStore describing spectral axes in a Frame. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* AstMapping *SpectralAxes( AstFitsChan *this, AstFrameSet *fs, -* double *dim, int *wperm, -* char s, FitsStore *store, double *crvals, -* int *axis_done, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The current Frame of the supplied FrameSet is searched for spectral -* axes. If any are found, FITS WCS keyword values describing the axis -* are added to the supplied FitsStore, if possible (the conventions -* of FITS-WCS paper III are used). Note, this function does not store -* values for keywords which define the transformation from pixel -* coords to Intermediate World Coords (CRPIX, PC and CDELT), but a -* Mapping is returned which embodies these values. This Mapping is -* from the current Frame in the FrameSet (WCS coords) to a Frame -* representing IWC. The IWC Frame has the same number of axes as the -* WCS Frame which may be greater than the number of base Frame (i.e. -* pixel) axes. -* -* If a spectral axis is found, the RafRA and RefDec attributes of the -* SpecFrame describing the axis are ignored: it is assumed that the -* WCS Frame also contains a pair of celestial axes which will result -* in appropriate celestial reference values being stored in the -* FitsStore (this asumption should be enforced by calling function -* MakeFitsFrameSet prior to calling this function). - -* Parameters: -* this -* Pointer to the FitsChan. -* fs -* Pointer to the FrameSet. The base Frame should represent FITS pixel -* coordinates, and the current Frame should represent FITS WCS -* coordinates. The number of base Frame axes should not exceed the -* number of current Frame axes. The spectral Unit in the returned -* FrameSet will always be linearly related to the default Units for -* the spectral System in use by the axis. If this requires a -* change to the existing spectral Unit, the integrity of the -* FrameSet will be maintained by suitable adjustments to the Mappings -* within the FrameSet. -* dim -* An array holding the image dimensions in pixels. AST__BAD can be -* supplied for any unknwon dimensions. -* wperm -* Pointer to an array of integers with one element for each axis of -* the current Frame. Each element holds the zero-based -* index of the FITS-WCS axis (i.e. one les than the value of "i" in -* the keyword names "CTYPEi", "CRVALi", etc) which describes the -* Frame axis. -* s -* The co-ordinate version character. A space means the primary -* axis descriptions. Otherwise the supplied character should be -* an upper case alphabetical character ('A' to 'Z'). -* store -* The FitsStore in which to store the FITS WCS keyword values. -* crvals -* Pointer to an array holding the default CRVAL value for each -* axis in the WCS Frame. -* axis_done -* An array of flags, one for each Frame axis, which indicate if a -* description of the corresponding axis has yet been stored in the -* FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* If a spectral axis was found which can be described using the -* conventions of FITS-WCS paper III, then a Mapping from the current Frame -* of the supplied FrameSet, to the IWC Frame is returned. Otherwise, -* a UnitMap is returned. Note, the Mapping only defines the IWC -* transformation for spectral axes. Any non-spectral axes are passed -* unchanged by the returned Mapping. -*/ - -/* Local Variables: */ - AstFitsTable *table; /* Pointer to structure holding -TAB table info */ - AstFrame *pframe; /* Primary Frame containing current WCS axis*/ - AstFrame *tfrm1; /* A temporary Frame */ - AstFrame *tfrm; /* A temporary Frame */ - AstFrame *wcsfrm; /* WCS Frame within FrameSet */ - AstFrameSet *tfs; /* A temporary FrameSet */ - AstGrismMap *gmap; /* GrismMap defining the spectral axis */ - AstMapping *axmap; /* Mapping from WCS to IWC */ - AstMapping *map; /* Pixel -> WCS mapping */ - AstMapping *ret; /* Returned Mapping */ - AstMapping *tmap0; /* A temporary Mapping */ - AstMapping *tmap1; /* A temporary Mapping */ - AstMapping *tmap2; /* A temporary Mapping */ - AstMapping *tmap3; /* A temporary Mapping */ - AstMapping *tmap4; /* A temporary Mapping */ - AstMapping *tmap5; /* A temporary Mapping */ - AstMapping *tmap6; /* A temporary Mapping */ - AstPermMap *pm; /* PermMap pointer */ - AstSpecFrame *specfrm; /* The SpecFrame defining current WCS axis */ - char *cname; /* Pointer to CNAME value */ - char ctype[ MXCTYPELEN ]; /* The value for the FITS CTYPE keyword */ - char lin_unit[ 20 ]; /* Linear spectral Units being used */ - char orig_system[ 40 ]; /* Value of System attribute for current WCS axis */ - char system_attr[ 10 ]; /* Name of System attribute for current WCS axis */ - char unit_attr[ 10 ]; /* Name of Unit attribute for current WCS axis */ - const char *cval; /* Pointer to temporary character string */ - const char *x_sys[ 4 ]; /* Basic spectral systems */ - double *lbnd_p; /* Pointer to array of lower pixel bounds */ - double *ubnd_p; /* Pointer to array of upper pixel bounds */ - double crval; /* The value for the FITS CRVAL keyword */ - double dgbyds; /* Rate of change of grism parameter wrt "S" at ref. point */ - double dsbydx; /* Rate of change of "S" wrt "X" at ref. point */ - double geolat; /* Geodetic latitude of observer (radians) */ - double geolon; /* Geodetic longitude of observer (radians) */ - double gval; /* Value of grism parameter at reference point */ - double h; /* Geodetic altitude of observer (metres) */ - double imagfreq; /* Image sideband equivalent to the rest frequency (Hz) */ - double lbnd_s; /* Lower bound on spectral axis */ - double pv; /* Value of projection parameter */ - double restfreq; /* Rest frequency (Hz) */ - double ubnd_s; /* Upper bound on spectral axis */ - double vsource; /* Rel.vel. of source (m/s) */ - double xval; /* Value of "X" system at reference point */ - double xyz[3]; /* Geocentric position vector (in m) */ - double zsource; /* Redshift of source */ - int *inperm; /* Pointer to permutation array for input axes */ - int *outperm; /* Pointer to permutation array for output axes */ - int extver; /* Table version number for -TAB headers */ - int fits_i; /* FITS WCS axis index for current WCS axis */ - int iax; /* Axis index */ - int icolindex; /* Index of table column holding index vector */ - int icolmain; /* Index of table column holding main coord array */ - int interp; /* INterpolation method for look-up tables */ - int ix; /* System index */ - int j; /* Loop count */ - int npix; /* Number of pixel axes */ - int nwcs; /* Number of WCS axes */ - int paxis; /* Axis index within primary Frame */ - int sourcevrf; /* Rest Frame in which SourceVel is accesed */ - -/* Initialise */ - ret = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* Every supported spectral system is linearly related to one of the - following four systems. */ - x_sys[ 0 ] = "FREQ"; - x_sys[ 1 ] = "WAVE"; - x_sys[ 2 ] = "AWAV"; - x_sys[ 3 ] = "VELO"; - -/* Get a pointer to the WCS Frame. */ - wcsfrm = astGetFrame( fs, AST__CURRENT ); - -/* Store the number of pixel and WCS axes. */ - npix = astGetNin( fs ); - nwcs = astGetNout( fs ); - -/* Store the upper and lower pixel bounds. */ - lbnd_p = astMalloc( sizeof( double )*(size_t) npix ); - ubnd_p = astMalloc( sizeof( double )*(size_t) npix ); - if( astOK ) { - for( iax = 0; iax < npix; iax++ ) { - lbnd_p[ iax ] = 1.0; - ubnd_p[ iax ] = ( dim[ iax ] != AST__BAD ) ? dim[ iax ] : 500; - } - } - -/* Check each axis in the WCS Frame to see if it is a spectral axis. */ - axmap = NULL; - for( iax = 0; iax < nwcs; iax++ ) { - -/* Obtain a pointer to the primary Frame containing the current WCS axis. */ - astPrimaryFrame( wcsfrm, iax, &pframe, &paxis ); - -/* If the current axis belongs to a SpecFrame, we have found a spectral - axis. */ - if( astIsASpecFrame( pframe ) ) { - specfrm = (AstSpecFrame *) pframe; - -/* Note the (zero-based) FITS WCS axis index to be used for the current - Frame axis. */ - fits_i = wperm[ iax ]; - -/* Note the name and original value of the System attribute for the spectral - axis within the FrameSet current Frame. */ - sprintf( system_attr, "System(%d)", iax + 1 ); - cval = astGetC( wcsfrm, system_attr ); - if( cval ) strcpy( orig_system, cval ); - -/* Note the name of the Unit attribute for the spectral axis within the - FrameSet current Frame. */ - sprintf( unit_attr, "Unit(%d)", iax + 1 ); - -/* Get a pointer to the Mapping from FITS pixel coordinates to SpecFrame. */ - map = astGetMapping( fs, AST__BASE, AST__CURRENT ); - -/* Find the bounds of the Spectral axis over the volume of the pixel grid. */ - astMapBox( map, lbnd_p, ubnd_p, 1, iax, &lbnd_s, &ubnd_s, - NULL, NULL ); - -/* The Unit attribute of a SpecFrame can be set to arbitrary non-linear - functions of standard linear spectral units. FITS-WCS paper III requires - CRVAL etc to be given in linear units. So first we ensure that we have a - SpecFrame with linear Units. Create a copy of the SpecFrame and clear - its Unit attribute (this ensures the copy has the default linear units). - Then find a Mapping from the original spectral units to the default - linear units. If the conversion is possible, see if the Mapping - between the units is linear. If it is, then the original Unit attribute - of the SpecFrame is OK (i.e. the units are linear). If not, clear - the Unit attribute of the spectral axis in the FrameSet so that it - uses the default linear units (retaining the original value so that it - can be re-instated later). Using the clear method on the FrameSet - pointer rather than the SpecFrame pointer causes the SpecFrame to be - re-mapped within the FrameSet to maintain its correct relationship with - the other Frames in the FrameSet. Also update the pixel->spectrum - Mapping to take account of the change in units and re-calculate the new - bounds on the spectral axis. Also update any supplied CRVAL value for - the spectral axis. */ - tfrm = astCopy( specfrm ); - astClearUnit( tfrm, 0 ); - tfs = astConvert( specfrm, tfrm, "" ); - tfrm = astAnnul( tfrm ); - if( tfs ) { - crval = crvals ? crvals[ iax ] : AST__BAD; - tmap1 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); - tfs = astAnnul( tfs ); - if( !IsMapLinear( tmap1, &lbnd_s, &ubnd_s, 0, status ) ) { - astClear( fs, unit_attr ); - (void) astAnnul( map ); - map = astGetMapping( fs, AST__BASE, AST__CURRENT ); - astMapBox( map, lbnd_p, ubnd_p, 1, iax, &lbnd_s, &ubnd_s, - NULL, NULL ); - astTran1( tmap1, 1, &crval, 1, &crval ); - } - tmap1 = astAnnul( tmap1 ); - -/* Note the linear spectral Unit currently in use. */ - cval = astGetUnit( specfrm, 0 ); - if( cval ) strcpy( lin_unit, cval ); - -/* For some of the algorithms, the reference value CRVAL is arbitrary. - For these algorithms we choose to use the supplied default CRVAL value. - If no default CRVAL value was suppllied, we use the mid spectral value - if the size of the spectral axis was given, or the lower bound (i.e. - pixel 1) if the size of the spectral axis was not given. */ - if( crval == AST__BAD ) { - if( dim[ iax ] != AST__BAD ) { - crval = 0.5*( lbnd_s + ubnd_s ); - } else { - crval = lbnd_s; - } - } - -/* Modify this crval value so that it correpsonds to an integer pixel - coordinate. */ - crval = NearestPix( map, crval, iax, status ); - -/* We now check to see if the Mapping from pixel coords -> linear spectral - coords corresponds to one of the algorithms supported in FITS-WCS paper - III. First check for the "linear" algorithm in which the linear spectral - coordinate given by the SpecFrame is related linearly to the pixel - coords. */ - ctype[ 0 ] = 0; - if( IsMapLinear( map, lbnd_p, ubnd_p, iax, status ) ) { - -/* The CTYPE value is just the spectral system. */ - strcpy( ctype, orig_system ); - -/* Create the Mapping which defines the spectral IWC axis. This is - initially the Mapping from WCS to IWCS - it subtracts the CRVAL value - from the spectral WCS value to get the spectral IWC value (other - non-spectral axes are left unchanged by this Mapping). This results - in the spectral IWC axis having the same axis index as the spectral - WCS axis. */ - crval = -crval; - tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status ); - crval = -crval; - axmap = AddUnitMaps( tmap0, iax, nwcs, status ); - tmap0 = astAnnul( tmap0 ); - } - -/* If the "linear" algorithm above is inappropriate, see if the "non-linear" - algorithm defined in FITS-WCS paper III can be used, in which pixel - coords are linearly related to some spectral system (called "X") other - than the one represented by the supplied SpecFrame (called "S"). */ - if( !ctype[ 0 ] ) { - -/* Loop round each of the 4 allowed X systems. All other spectral systems - are linearly related to one of these 4 systems and so do not need to be - tested. */ - for( ix = 0; ix < 4 && !ctype[ 0 ]; ix++ ) { - -/* Set the system of the spectral WCS axis to the new trial X system. Clear - the Unit attribute to ensure we are using the default linear units. - Using the FrameSet pointer "fs" ensures that the Mappings within the - FrameSet are modified to maintain the correct inter-Frame relationships. */ - astSetC( fs, system_attr, x_sys[ ix ] ); - astClear( fs, unit_attr ); - -/* Now we check to see if the current X system is linearly related to - pixel coordinates. */ - tmap3 = astGetMapping( fs, AST__BASE, AST__CURRENT ); - if( IsMapLinear( tmap3, lbnd_p, ubnd_p, iax, status ) ) { - -/* CTYPE: First 4 characters specify the "S" system. */ - strcpy( ctype, orig_system ); - -/* The non-linear algorithm code to be appended to the "S" system is of the - form "-X2P" ("P" is the system which is linearly related to "S"). */ - if( !strcmp( x_sys[ ix ], "FREQ" ) ) { - strcpy( ctype + 4, "-F2" ); - } else if( !strcmp( x_sys[ ix ], "WAVE" ) ) { - strcpy( ctype + 4, "-W2" ); - } else if( !strcmp( x_sys[ ix ], "AWAV" ) ) { - strcpy( ctype + 4, "-A2" ); - } else { - strcpy( ctype + 4, "-V2" ); - } - if( !strcmp( orig_system, "FREQ" ) || - !strcmp( orig_system, "ENER" ) || - !strcmp( orig_system, "WAVN" ) || - !strcmp( orig_system, "VRAD" ) ) { - strcpy( ctype + 7, "F" ); - } else if( !strcmp( orig_system, "WAVE" ) || - !strcmp( orig_system, "VOPT" ) || - !strcmp( orig_system, "ZOPT" ) ) { - strcpy( ctype + 7, "W" ); - } else if( !strcmp( orig_system, "AWAV" ) ) { - strcpy( ctype + 7, "A" ); - } else { - strcpy( ctype + 7, "V" ); - } - -/* Create a Mapping which gives S as a function of X. */ - tfrm = astCopy( specfrm ); - astSetC( tfrm, "System(1)", orig_system ); - astSetC( tfrm, "Unit(1)", lin_unit ); - tfs = astConvert( specfrm, tfrm, "" ); - tmap5 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); - tfs = astAnnul( tfs ); - tfrm = astAnnul( tfrm ); - -/* Use the inverse of this Mapping to get the X value at the reference S - value. */ - astTran1( tmap5, 1, &crval, 0, &xval ); - -/* Also use it to get the rate of change of S with respect to X at the - reference point. */ - dsbydx = astRate( tmap5, &xval, 0, 0 ); - -/* Create the Mapping which defines the spectral IWC axis. This is the - Mapping from WCS to IWC - it first converts from S to X, then subtracts - the X reference value value, and then scales the axis to ensure that - the rate of change of S with respect to IWC is unity (as required by - FITS-WCS paper III). Other non-spectral axes are left unchanged by - the Mapping. The spectral IWC axis has the same axis index as the - spectral WCS axis. */ - xval = -xval; - tmap2 = (AstMapping *) astShiftMap( 1, &xval, "", status ); - astInvert( tmap5 ); - tmap0 = (AstMapping *) astCmpMap( tmap5, tmap2, 1, "", status ); - tmap5 = astAnnul( tmap5 ); - tmap2 = astAnnul( tmap2 ); - tmap2 = (AstMapping *) astZoomMap( 1, dsbydx, "", status ); - tmap1 = (AstMapping *) astCmpMap( tmap0, tmap2, 1, "", status ); - tmap0 = astAnnul( tmap0 ); - tmap2 = astAnnul( tmap2 ); - axmap = AddUnitMaps( tmap1, iax, nwcs, status ); - tmap1 = astAnnul( tmap1 ); - } - tmap3 = astAnnul( tmap3 ); - -/* Re-instate the original system and unit attributes for the spectral axis. */ - astSetC( fs, system_attr, orig_system ); - astSetC( fs, unit_attr, lin_unit ); - } - } - -/* If the "non-linear" algorithm above is inappropriate, see if the - "log-linear" algorithm defined in FITS-WCS paper III can be used, in - which the spectral axis is logarithmically spaced in the spectral - system given by the SpecFrame. */ - if( !ctype[ 0 ] ) { - -/* If the "log-linear" algorithm is appropriate, the supplied SpecFrame (s) - is related to pixel coordinate (p) by s = Sr.EXP( a*p - b ). If this - is the case, then the log of s will be linearly related to pixel - coordinates. Test this. If the test is passed a Mapping is returned from - WCS to IWC. */ - axmap = LogAxis( map, iax, nwcs, lbnd_p, ubnd_p, crval, status ); - -/* If the axis is logarithmic... */ - if( axmap ) { - -/* CTYPE: First 4 characters specify the "S" system. */ - strcpy( ctype, orig_system ); - -/* The rest is "-LOG". */ - strcpy( ctype + 4, "-LOG" ); - } - } - -/* If the "log-linear" algorithm above is inappropriate, see if the "grism" - algorithm defined in FITS-WCS paper III can be used, in which pixel - coords are related to wavelength using a grism dispersion function, - implemented in AST by a GrismMap. GrismMaps produce either vacuum - wavelength or air wavelength as output. Temporarily set the SpecFrame - to these two systems in turn before we do the check for a GrismMap. */ - for( ix = 0; ix < 2 && !ctype[ 0 ]; ix++ ) { - astSetC( fs, system_attr, ( ix == 0 ) ? "WAVE" : "AWAV" ); - astSetC( fs, unit_attr, "m" ); - -/* Get the simplified Mapping from pixel to wavelength. If the Mapping is - a CmpMap containing a GrismMap, and if the output of the GrismMap is - scaled by a neighbouring ZoomMap (e.g. into different wavelength units), - then the GrismMap will be modified to incorporate the effect of the - ZoomMap, and the ZoomMap will be removed. */ - tmap2 = astGetMapping( fs, AST__BASE, AST__CURRENT ); - tmap1 = astSimplify( tmap2 ); - tmap2 = astAnnul( tmap2 ); - -/* Analyse this Mapping to see if the iax'th output is created diretcly by a - GrismMap (i.e. the output of theGrismMap must not subsequently be - modified by some other Mapping). If so, ExtractGrismMap returns a pointer - to the GrismMap as its function value, and also returns "tmap2" as a copy - of tmap1 in which the GrismMap has been replaced by a UnitMap. */ - gmap = ExtractGrismMap( tmap1, iax, &tmap2, status ); - if( gmap ) { - -/* The Mapping without the GrismMap must be linear on the spectral axis. */ - if( IsMapLinear( tmap2, lbnd_p, ubnd_p, iax, status ) ) { - -/* Get the reference wavelength (in "m") stored in the GrismMap. */ - crval = astGetGrismWaveR( gmap ); - -/* Save a copy of the current Wavelength (in "m") SpecFrame. */ - tfrm1 = astCopy( specfrm ); - -/* Re-instate the original System and Unit attributes for the SpecFrame. */ - astSetC( fs, system_attr, orig_system ); - astSetC( fs, unit_attr, lin_unit ); - -/* Find the Mapping from the original "S" system to wavelength (in "m"). */ - tfs = astConvert( specfrm, tfrm1, "" ); - tfrm1 = astAnnul( tfrm1 ); - if( tfs ) { - tmap3 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); - tfs = astAnnul( tfs ); - -/* Use the inverse of this Mapping to convert the reference value from - wavelength to the "S" system. */ - astTran1( tmap3, 1, &crval, 0, &crval ); - -/* Concatenate the "S"->wavelength Mapping with the inverse GrismMap (from - wavelength to grism parameter), to get the "S" -> "grism parameter" - Mapping. */ - astInvert( gmap ); - tmap4 = (AstMapping *) astCmpMap( tmap3, gmap, 1, "", status ); - tmap3 = astAnnul( tmap3 ); - -/* Use this Mapping to find the grism parameter at the reference point. */ - astTran1( tmap4, 1, &crval, 1, &gval ); - -/* Also use it to find the rate of change of grism parameter with respect - to "S" at the reference point. */ - dgbyds = astRate( tmap4, &crval, 0, 0 ); - -/* FITS-WCS paper III required ds/dw to be unity at the reference point. - Therefore the rate of change of grism parameter with respect to IWC ("w") - is equal to the rate of change of grism parameter with respect to "S" - (at the reference point). The mapping from "w" to grism parameter is a - ZoomMap which scales "w" by "dgbyds" followed by a ShiftMap which adds - on "gval". */ - tmap5 = (AstMapping *) astZoomMap( 1, dgbyds, "", status ); - tmap6 = (AstMapping *) astShiftMap( 1, &gval, "", status ); - tmap3 = (AstMapping *) astCmpMap( tmap5, tmap6, 1, "", status ); - tmap5 = astAnnul( tmap5 ); - tmap6 = astAnnul( tmap6 ); - -/* Create the Mapping which defines the spectral IWC axis. This is the - Mapping from WCS "S" to IWCS "w", formed by combining the Mapping from - "S" to grism parameter (tmap4), with the Mapping from grism parameter to - "w" (inverse of tmap3). Other non-spectral axes are left unchanged by the - Mapping. The spectral IWC axis has the same axis index as the spectral - WCS axis. */ - astInvert( tmap3 ); - tmap5 = (AstMapping *) astCmpMap( tmap4, tmap3, 1, "", status ); - tmap3 = astAnnul( tmap3 ); - tmap4 = astAnnul( tmap4 ); - axmap = AddUnitMaps( tmap5, iax, nwcs, status ); - tmap5 = astAnnul( tmap5 ); - -/* CTYPE: First 4 characters specify the "S" system. */ - strcpy( ctype, orig_system ); - -/* Last 4 characters are "-GRA" or "-GRI". */ - strcpy( ctype + 4, ( ix == 0 ) ? "-GRI" : "-GRA" ); - -/* Store values for the projection parameters in the FitsStore. Ignore - parameters which are set to the default values defined in FITS-WCS - paper III. */ - pv = astGetGrismG( gmap ); - if( pv != 0 ) SetItem( &(store->pv), fits_i, 0, s, pv, status ); - pv = (double) astGetGrismM( gmap ); - if( pv != 0 ) SetItem( &(store->pv), fits_i, 1, s, pv, status ); - pv = astGetGrismAlpha( gmap ); - if( pv != 0 ) SetItem( &(store->pv), fits_i, 2, s, pv*AST__DR2D, status ); - pv = astGetGrismNR( gmap ); - if( pv != 1.0 ) SetItem( &(store->pv), fits_i, 3, s, pv, status ); - pv = astGetGrismNRP( gmap ); - if( pv != 0 ) SetItem( &(store->pv), fits_i, 4, s, pv, status ); - pv = astGetGrismEps( gmap ); - if( pv != 0 ) SetItem( &(store->pv), fits_i, 5, s, pv*AST__DR2D, status ); - pv = astGetGrismTheta( gmap ); - if( pv != 0 ) SetItem( &(store->pv), fits_i, 6, s, pv*AST__DR2D, status ); - } - } - -/* Release resources. */ - tmap2 = astAnnul( tmap2 ); - gmap = astAnnul( gmap ); - } - -/* Release resources. */ - tmap1 = astAnnul( tmap1 ); - -/* Re-instate the original System and Unit attributes for the SpecFrame. */ - astSetC( fs, system_attr, orig_system ); - astSetC( fs, unit_attr, lin_unit ); - } - -/* If none of the above algorithms are appropriate, we must resort to - using the -TAB algorithm, in which the Mapping is defined by a look-up - table. Check the TabOK attribute to see -TAB is to be supported. */ - extver = astGetTabOK( this ); - if( !ctype[ 0 ] && extver > 0 ) { - -/* Get any pre-existing FitsTable from the FitsStore. This is the table - in which the tabular data will be stored (if the Mapping can be expressed - in -TAB form). */ - if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL; - -/* See if the Mapping can be expressed in -TAB form. */ - tmap0 = IsMapTab1D( map, 1.0, NULL, wcsfrm, dim, iax, fits_i, &table, - &icolmain, &icolindex, &interp, status ); - if( tmap0 ) { - -/* CTYPE: First 4 characters specify the "S" system. Last 4 characters are - "-TAB". */ - strcpy( ctype, orig_system ); - strcpy( ctype + 4, "-TAB" ); - -/* The values stored in the table index vector are GRID coords. So we - need to ensure that IWC are equivalent to GRID coords. So set CRVAL - to zero. First store the original CRVAL value (which gives the - observation centre) in AXREF. */ - SetItem( &(store->axref), fits_i, 0, s, crval, status ); - crval = 0.0; - -/* Store TAB-specific values in the FitsStore. First the name of the - FITS binary table extension holding the coordinate info. */ - SetItemC( &(store->ps), fits_i, 0, s, AST_TABEXTNAME, status ); - -/* Next the table version number. This is the set (positive) value for the - TabOK attribute. */ - SetItem( &(store->pv), fits_i, 1, s, extver, status ); - -/* Also store the table version in the binary table header. */ - astSetFitsI( table->header, "EXTVER", extver, - "Table version number", 0 ); - -/* Next the name of the table column containing the main coords array. */ - SetItemC( &(store->ps), fits_i, 1, s, - astColumnName( table, icolmain ), status ); - -/* Next the name of the column containing the index array */ - if( icolindex >= 0 ) SetItemC( &(store->ps), fits_i, 2, s, - astColumnName( table, icolindex ), status ); - -/* The interpolation method (an AST extension to the published -TAB - algorithm, communicated through the QVi_4a keyword). */ - SetItem( &(store->pv), fits_i, 4, s, interp, status ); - -/* Also store the FitsTable itself in the FitsStore. */ - astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL ); - -/* Create the WCS -> IWC Mapping (AST uses grid coords as IWC coords for - the -TAB algorithm). First, get a Mapping that combines the TAB axis - Mapping( tmap0) in parallel with one or two UnitMaps in order to put - the TAB axis at the required index. */ - tmap1 = AddUnitMaps( tmap0, iax, nwcs, status ); - -/* Now get a PermMap that permutes the WCS axes into the FITS axis order. */ - inperm = astMalloc( sizeof( double )*nwcs ); - outperm = astMalloc( sizeof( double )*nwcs ); - if( astOK ) { - for( j = 0; j < nwcs; j++ ) { - inperm[ j ] = wperm[ j ]; - outperm[ wperm[ j ] ] = j; - } - } - pm = astPermMap( nwcs, inperm, nwcs, outperm, NULL, "", - status ); - -/* Combine these two Mappings in series, to get the Mapping from WCS to - IWC. */ - axmap = (AstMapping *) astCmpMap( pm, tmap1, 1, " ", - status ); - -/* Free resources. */ - inperm = astFree( inperm ); - outperm = astFree( outperm ); - pm = astAnnul( pm ); - tmap0 = astAnnul( tmap0 ); - tmap1 = astAnnul( tmap1 ); - } - if( table ) table = astAnnul( table ); - } - -/* If this axis is a usable spectral axis... */ - if( ctype[ 0 ] ) { - -/* Add the Mapping for this axis in series with any existing result Mapping. */ - if( ret ) { - tmap0 = (AstMapping *) astCmpMap( ret, axmap, 1, "", status ); - (void) astAnnul( ret ); - ret = tmap0; - } else { - ret = astClone( axmap ); - } - axmap = astAnnul( axmap ); - -/* Store values for CTYPE, CRVAL and CUNIT in the FitsStore. */ - SetItemC( &(store->ctype), fits_i, 0, s, ctype, status ); - SetItem( &(store->crval), fits_i, 0, s, crval, status ); - SetItemC( &(store->cunit), fits_i, 0, s, lin_unit, status ); - -/* If the axis label has been set, use it as the CTYPE comment and CNAME - value. */ - if( astTestLabel( specfrm, 0 ) ) { - cname = (char *) astGetLabel( specfrm, 0 ); - SetItemC( &(store->ctype_com), fits_i, 0, s, cname, status ); - SetItemC( &(store->cname), fits_i, 0, s, cname, status ); - } - -/* Store values for the other FITS-WCS keywords which describe the - spectral system. Only store values which have been explicitly set in - the SpecFrame, which are different to the default values defined by - FITS-WCS paper III (if any), and which are not bad. */ - if( astTestObsLon( specfrm ) && astTestObsLat( specfrm ) && - s == ' ' ) { - geolon = astGetObsLon( specfrm ); - geolat = astGetObsLat( specfrm ); - h = astGetObsAlt( specfrm ); - if( geolat != AST__BAD && geolon != AST__BAD && h != AST__BAD ) { - eraGd2gc( 1, geolon, geolat, h, xyz ); - SetItem( &(store->obsgeox), 0, 0, ' ', xyz[0], status ); - SetItem( &(store->obsgeoy), 0, 0, ' ', xyz[1], status ); - SetItem( &(store->obsgeoz), 0, 0, ' ', xyz[2], status ); - } - } - if( astTestRestFreq( specfrm ) ) { - restfreq = astGetRestFreq( specfrm ); - if( restfreq != AST__BAD ) { - if( !strcmp( orig_system, "WAVE" ) || - !strcmp( orig_system, "VOPT" ) || - !strcmp( orig_system, "ZOPT" ) || - !strcmp( orig_system, "AWAV" ) ) { - SetItem( &(store->restwav), 0, 0, s, AST__C/restfreq, status ); - } else { - SetItem( &(store->restfrq), 0, 0, s, restfreq, status ); - } - } - if( astIsADSBSpecFrame( specfrm ) ) { - imagfreq = astGetImagFreq( (AstDSBSpecFrame *) specfrm ); - if( imagfreq != AST__BAD ) { - SetItem( &(store->imagfreq), 0, 0, s, imagfreq, status ); - } - } - } - cval = GetFitsSor( astGetC( specfrm, "StdOfRest" ), status ); - if( cval ) SetItemC( &(store->specsys), 0, 0, s, cval, status ); - if( astTestSourceVel( specfrm ) ) { - vsource = astGetSourceVel( specfrm ); - if( vsource != AST__BAD && fabs( vsource ) < AST__C ) { - zsource = sqrt( (AST__C + vsource)/ - (AST__C - vsource) ) - 1.0; - SetItem( &(store->zsource), 0, 0, s, zsource, status ); - cval = GetFitsSor( astGetC( specfrm, "SourceVRF" ), status ); - if( cval ) SetItemC( &(store->ssyssrc), 0, 0, s, cval, status ); - } - } else { - vsource = AST__BAD; - } - -/* Store the VELOSYS value (not strictly needed since it can be - determined from the other values, but FITS-WCS paper III says it can be - useful). We temporarily change the source velocity to be zero m/s - in the main rest frame (StdOfRest) (unless the main rest frame is - already the source rest frame). We then change the source rest - frame to topocentric and get the source velocity (i.e. the velocity of - the main rest Frame) in the topocentric system. We then re-instate the - original attribute values if they were set. */ - if( astGetStdOfRest( specfrm ) != AST__SCSOR ) { - sourcevrf = astGetSourceVRF( specfrm ); - astSetSourceVRF( specfrm, astGetStdOfRest( specfrm ) ); - astSetSourceVel( specfrm, 0.0 ); - } else { - vsource = AST__BAD; - sourcevrf = AST__NOSOR; - } - astSetSourceVRF( specfrm, AST__TPSOR ); - SetItem( &(store->velosys), 0, 0, s, - astGetSourceVel( specfrm ), status ); - if( vsource != AST__BAD ){ - astSetSourceVRF( specfrm, sourcevrf ); - astSetSourceVel( specfrm, vsource ); - } - -/* Indicate that this axis has been described. */ - axis_done[ iax ] = 1; - } - -/* Release resources. */ - map = astAnnul( map ); - } - } - pframe = astAnnul( pframe ); - } - -/* Release resources. */ - lbnd_p = astFree( lbnd_p ); - ubnd_p = astFree( ubnd_p ); - wcsfrm = astAnnul( wcsfrm ); - -/* If we have a Mapping to return, simplify it. Otherwise, create - a UnitMap to return. */ - if( ret ) { - tmap0 = ret; - ret = astSimplify( tmap0 ); - tmap0 = astAnnul( tmap0 ); - } else { - ret = (AstMapping *) astUnitMap( nwcs, "", status ); - } - -/* Return the result. */ - return ret; -} - -static AstFitsChan *SpecTrans( AstFitsChan *this, int encoding, - const char *method, const char *class, int *status ){ -/* -* Name: -* SpecTrans - -* Purpose: -* Translated non-standard WCS FITS headers into equivalent standard -* ones. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstFitsChan *SpecTrans( AstFitsChan *this, int encoding, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function checks the supplied FitsChan for selected -* non-standard WCS keywords and, if found, stores equivalent -* standard keywords in a newly created FitsChan which is returned as -* the function value. All the original keywords are marked -* as having been used, so that they are not written out when the -* FitsChan is deleted. -* - -* At the moment, the non-standard keywords checked for are: -* -* 1) RADECSYS is renamed as RADESYS -* -* 2) LONGPOLE is renamed as LONPOLE -* -* 3) CDjjjiii and CDj_i are converted to PCi_j (with unit CDELT) -* -* 4) CROTAj are converted to PCi_j -* -* 5) PROJPi are converted to PV<axlat>_i -* -* 6) CmVALi are converted to CRVALis (s=A,B,,, for m=1,2...). This -* is also done for CmPIXi, CmYPEi, and CmNITi. CmELTi is converted -* to a CDj_is array. -* -* 7) EQUINOX keywords with string values equal to a date preceded -* by the letter B or J (eg "B1995.0"). These are converted to the -* corresponding Julian floating point value without any epoch -* specifier. -* -* 8) EPOCH values are converted into Julian EQUINOX values (but only -* if the FitsChan does not already contain an EQUINOX value). -* -* 9) DATE-OBS values are converted into MJD-OBS values (but only -* if the FitsChan does not already contain an MJD-OBS value). -* -* 10) EQUINOX or EPOCH keywords with value zero are converted to -* B1950. -* -* 11) The AIPS NCP and GLS projections are converted into equivalent SIN -* or SFL projections. -* -* 12) The IRAF "ZPX" projection. If the last 4 chacaters of CTYPEi - -* (i = 1, naxis) are "-ZPX", then: -* - "ZPX" is replaced by "ZPN" within the CTYPEi value -* - A distortion code of "-ZPX" is appended to the end of the CTYPEi -* value (this is used later by the DistortMaps function). -* - If the FitsChan contains no PROJP keywords, then projection -* parameter valus are read from any WATi_nnn keywords, and -* corresponding PV keywords are added to the FitsChan. -* -* 13) The IRAF "TNX" projection. If the last 4 chacaters of CTYPEi - -* (i = 1, naxis) are "-TNX", then: -* - "TNX" is replaced by "TAN" within the CTYPEi value (the distorted -* TAN projection included in a pre-final version of FITS-WCS is still -* supported by AST using the WcsMap AST__TPN projection). -* - If the FitsChan contains no PROJP keywords, then projection -* parameter valus are read from any WATi_nnn keywords, and -* corresponding PV keywords are added to the FitsChan. -* - If the TNX projection cannot be converted exactly into a TAN -* projection, ASTWARN keywords are added to the FitsChan -* containing a warning message. The calling application can (if it -* wants to) check for this keyword, and report its contents to the -* user. -* -* 14) Keywords relating to the IRAF "mini-WCS" system are removed. -* This is the IRAF equivalent of the AST native encoding. Mini-WCS -* keywords are removed in order to avoid confusion arising between -* potentially inconsistent encodings. -* -* 15) "QV" parameters for TAN projections (as produced by AUTOASTROM) -* or "-TAB" (as produced by FitsChan) are renamed to "PV". -* -* 16) RESTFREQ is converted to RESTFRQ. -* -* 17) the "-WAV", "-FRQ" and "-VEL" CTYPE algorithms included in an -* early draft of FITS-WCS paper III are translated to the -* corresponding modern "-X2P" form. -* -* 18) AIPS spectral CTYPE values are translated to FITS-WCS paper III -* equivalents. -* -* 19) AIPS spectral keywords OBSRA and OBSDEC are used to create a -* pair of celestial axes with reference point at the specified -* (OBSRA,OBSDEC) position. This is only done if the header does not -* already contain a pair of celestial axes. -* -* 20) Common case insensitive CUNIT values: "Hz", "Angstrom", "km/s", -* "M/S" -* -* 21) Various translations specific to the FITS-CLASS encoding. -* -* 22) SAO distorted TAN projections (uses COi_j keywords to store -* polynomial coefficients) are converted to TPN projections. - -* 23) CTYPE == "LAMBDA" changed to CTYPE = "WAVE" -* -* 24) if the projection is TAN and the PolyTan attribute is non-zero, -* or if the projection is TPV (produced by SCAMP), the projection is -* changed to TPN (the AST code for the draft FITS-WCS paper II -* conventions for a distorted TAN projection). - -* Parameters: -* this -* Pointer to the FitsChan. -* encoding -* The FitsChan encoding in use. -* method -* Pointer to string holding name of calling method. -* class -* Pointer to a string holding the name of the supplied object class. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the new FitsChan containing the keywords which -* constitute the standard equivalents to any non-standard keywords in -* the supplied FitsChan. A NULL pointer is returned if there are no -* non-standard keywords in the supplied FitsChan. -*/ - -/* Local Variables: */ - AstFitsChan *ret; /* The returned FitsChan */ - char *assys; /* AIPS standad of rest type */ - char *astype; /* AIPS spectral type */ - char *comm; /* Pointer to comment string */ - char *cval; /* Pointer to character string */ - char *start; /* Pointer to start of projp term */ - char *watmem; /* Pointer to total WAT string */ - char bj; /* Besselian/Julian indicator */ - char format[ 50 ]; /* scanf format string */ - char keyname[ FITSNAMLEN + 5 ];/* General keyword name + formats */ - char lattype[MXCTYPELEN]; /* CTYPE value for latitude axis */ - char lontype[MXCTYPELEN]; /* CTYPE value for longitude axis */ - char prj[6]; /* Spatial projection string */ - char s; /* Co-ordinate version character */ - char spectype[MXCTYPELEN]; /* CTYPE value for spectral axis */ - char sprj[6]; /* Spectral projection string */ - char ss; /* Co-ordinate version character */ - char template[ FITSNAMLEN + 1 ];/* General keyword name template */ - double *cvals; /* PVi_m values for TPN projection */ - double cdelti; /* CDELT for longitude axis */ - double cdeltj; /* CDELT for latitude axis */ - double cosrota; /* Cos( CROTA ) */ - double crota; /* CROTA Value */ - double dval; /* General floating value */ - double lambda; /* Ratio of CDELTs */ - double projp; /* Projection parameter value */ - double rowsum2; /* Sum of squared CDi_j row elements */ - double sinrota; /* Sin( CROTA ) */ - double sinval; /* Sin( dec ref ) */ - int *mvals; /* "m" index of each PVi_m value */ - int axlat; /* Index of latitude axis */ - int axlon; /* Index of longitude axis */ - int diag; /* Sign of diagonal CDi_j element */ - int dim; /* Length of pixel axis */ - int gotpcij; /* Does FitsChan contain any PCi_j keywords? */ - int i,j; /* Indices */ - int iaxis; /* Axis index */ - int icoeff; /* Index of next PVi_m value */ - int iproj; /* Projection parameter index */ - int jhi; /* Highest axis index */ - int jlo; /* Lowest axis index */ - int lbnd[ 2 ]; /* Lower index bounds */ - int m; /* Co-ordinate version index */ - int naxis; /* Number of axes */ - int nc; /* Length of string */ - int ncoeff; /* Number of PVi_m values */ - int ok; /* Can projection be represented in FITS-WCS?*/ - int shifted; /* Non-zero if there is an origin shift */ - int tlbnd[ 2 ]; /* Lower index bounds */ - int tubnd[ 2 ]; /* Upper index bounds */ - int ubnd[ 2 ]; /* Upper index bounds */ - int use_projp; /* Use PROJP keywors in favour of PV keywords? */ - size_t size; /* Length of string value */ - -/* Check the global error status. */ - if ( !astOK ) return NULL; - -/* Initialise to avoid compiler warnings. */ - size = 0; - prj[ 0 ] = 0; - -/* Create the returned FitsChan. */ - ret = astFitsChan( NULL, NULL, "", status ); - -/* Loop round all axis descriptions, starting with primary (' '). */ - for( s = 'A' - 1; s <= 'Z' && astOK; s++ ){ - if( s == 'A' - 1 ) s = ' '; - -/* Find the number of axes by finding the highest axis number in any - CRPIXi keyword name. Pass on if there are no axes for this axis - description. */ - if( s != ' ' ) { - sprintf( template, "CRPIX%%d%c", s ); - } else { - strcpy( template, "CRPIX%d" ); - } - if( !astKeyFields( this, template, 1, &naxis, lbnd ) ) { - if( s == ' ' ) s = 'A' - 1; - continue; - } - -/* Find the longitude and latitude axes by examining the CTYPE values. - They are marked as read. Such markings are only provisional, and they - can be read again any number of times until the current astRead - operation is completed. Also note the projection type. */ - j = 0; - axlon = -1; - axlat = -1; - while( j < naxis && astOK ){ - if( GetValue2( ret, this, FormatKey( "CTYPE", j + 1, -1, s, status ), - AST__STRING, (void *) &cval, 0, method, - class, status ) ){ - nc = strlen( cval ); - if( !strncmp( cval, "RA--", 4 ) || - !strncmp( cval, "AZ--", 4 ) || - ( nc > 1 && !strncmp( cval + 1, "LON", 3 ) ) || - ( nc > 2 && !strncmp( cval + 2, "LN", 2 ) ) ) { - axlon = j; - strncpy( prj, cval + 4, 4 ); - strncpy( lontype, cval, 10 ); - prj[ 4 ] = 0; - } else if( !strncmp( cval, "DEC-", 4 ) || - !strncmp( cval, "EL--", 4 ) || - ( nc > 1 && !strncmp( cval + 1, "LAT", 3 ) ) || - ( nc > 2 && !strncmp( cval + 2, "LT", 2 ) ) ) { - axlat = j; - strncpy( prj, cval + 4, 4 ); - strncpy( lattype, cval, 10 ); - prj[ 4 ] = 0; - -/* Check for spectral algorithms from early drafts of paper III */ - } else { - sprj[ 0 ] = '-'; - if( ( nc > 4 && !strncmp( cval + 4, "-WAV", 4 ) ) ) { - sprj[ 1 ] = 'W'; - } else if( ( nc > 4 && !strncmp( cval + 4, "-FRQ", 4 ) ) ) { - sprj[ 1 ] = 'F'; - } else if( ( nc > 4 && !strncmp( cval + 4, "-VEL", 4 ) ) ) { - sprj[ 1 ] = 'V'; - } else { - sprj[ 0 ] = 0; - } - if( *sprj ) { - sprj[ 2 ] = '2'; - if( !strncmp( cval, "WAVE", 4 ) ) { - sprj[ 3 ] = 'W'; - } else if( !strncmp( cval, "FREQ", 4 ) ) { - sprj[ 3 ] = 'F'; - } else if( !strncmp( cval, "VELO", 4 ) ) { - sprj[ 3 ] = 'V'; - } else if( !strncmp( cval, "VRAD", 4 ) ) { - sprj[ 3 ] = 'F'; - } else if( !strncmp( cval, "VOPT", 4 ) ) { - sprj[ 3 ] = 'W'; - } else if( !strncmp( cval, "ZOPT", 4 ) ) { - sprj[ 3 ] = 'W'; - } else if( !strncmp( cval, "ENER", 4 ) ) { - sprj[ 3 ] = 'F'; - } else if( !strncmp( cval, "WAVN", 4 ) ) { - sprj[ 3 ] = 'F'; - } else if( !strncmp( cval, "BETA", 4 ) ) { - sprj[ 3 ] = 'V'; - } else { - sprj[ 0 ] = 0; - } - } - if( *sprj ) { - strcpy( spectype, cval ); - if( sprj[ 1 ] == sprj[ 3 ] ) { - strcpy( sprj, strlen( cval ) > 8 ? "----" : " " ); - } else { - sprj[ 4 ] = 0; - } - strncpy( spectype + 4, sprj, 4 ); - cval = spectype; - SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - } - } - j++; - } else { - break; - } - } - -/* RADECSYS keywords - ----------------- */ - if( s == ' ' ) { - if( GetValue2( ret, this, "RADECSYS", AST__STRING, (void *) &cval, 0, method, - class, status ) ){ - if( encoding == FITSPC_ENCODING || encoding == FITSIRAF_ENCODING ){ - SetValue( ret, "RADESYS", (void *) &cval, AST__STRING, - CardComm( this, status ), status ); - } - } - -/* LONGPOLE keywords - ----------------- */ - if( GetValue2( ret, this, "LONGPOLE", AST__FLOAT, (void *) &dval, 0, method, - class, status ) ){ - if( encoding == FITSPC_ENCODING || encoding == FITSIRAF_ENCODING ){ - SetValue( ret, "LONPOLE", (void *) &dval, AST__FLOAT, - CardComm( this, status ), status ); - } - } - } - -/* Zero CDELT values. - ----------------- */ - -/* Check there are some CDELT keywords... */ - if( s != ' ' ) { - sprintf( template, "CDELT%%d%c", s ); - } else { - strcpy( template, "CDELT%d" ); - } - if( astKeyFields( this, template, 0, NULL, NULL ) ){ - -/* Do each row in the matrix. */ - for( j = 0; j < naxis; j++ ){ - -/* Get the CDELT value for this row. */ - GetValue2( ret, this, FormatKey( "CDELT", j + 1, -1, s, status ), AST__FLOAT, - (void *) &cdeltj, 0, method, class, status ); - -/* If CDELT is zero, use 1.0E-6 of the corresponding CRVAL value - instead, or 1.0 if CRVAL is zero. Otherwise, the zeros could cause the - matrix to be non-invertable. The Mapping could then not be simplified - or used by a Plot. CDELT values of zero are usually used to indicate - "redundant" axes. For instance, a 2D image may be stored as a 3D cube - with a single plane with the "redundant" 3rd axis used to specify the - wavelength of the filter. The actual value used for CDELT shouldn't - matter since the axis only spans a single pixel anyway. */ - if( cdeltj == 0.0 ){ - GetValue2( ret, this, FormatKey( "CDELT", j + 1, -1, s, status ), AST__FLOAT, - (void *) &dval, 1, method, class, status ); - cdeltj = 1.0E-6*dval; - if( cdeltj == 0.0 ) cdeltj = 1.0; - SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ), (void *) &cdeltj, - AST__FLOAT, NULL, status ); - } - } - } - -/* Following conversions produce PCi_j keywords. Only do them if there - are currently no PCi_j keywords in the header. */ - if( s != ' ' ) { - sprintf( template, "PC%%d_%%d%c", s ); - } else { - strcpy( template, "PC%d_%d" ); - } - gotpcij = astKeyFields( this, template, 0, NULL, NULL ); - if( !gotpcij ){ - -/* CDjjjiii - -------- */ - if( s == ' ' && astKeyFields( this, "CD%3d%3d", 0, NULL, NULL ) ){ - -/* Do each row in the matrix. */ - for( j = 0; j < naxis; j++ ){ - -/* Do each column in the matrix. */ - for( i = 0; i < naxis; i++ ){ - -/* Get the CDjjjiii matrix element */ - sprintf( keyname, "CD%.3d%.3d", j + 1, i + 1 ); - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, - method, class, status ) ){ - -/* If found, save it with name PCj_i, and ensure the default value of 1.0 - is used for CDELT. */ - if( encoding == FITSIRAF_ENCODING ){ - SetValue( ret, FormatKey( "PC", j + 1, i + 1, ' ', status ), - (void *) &dval, AST__FLOAT, NULL, status ); - dval = 1.0; - SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ), - (void *) &dval, AST__FLOAT, NULL, status ); - gotpcij = 1; - } - } - } - } - } - -/* CDj_i - ---- */ - if( s != ' ' ) { - sprintf( template, "CD%%d_%%d%c", s ); - } else { - strcpy( template, "CD%d_%d" ); - } - if( !gotpcij && astKeyFields( this, template, 0, NULL, NULL ) ){ - -/* Do each row in the matrix. */ - for( j = 0; j < naxis; j++ ){ - -/* First find the sum of the squared elements in the row. and note the - sign of the diagonal element. */ - rowsum2 = 0.0; - diag = +1; - for( i = 0; i < naxis; i++ ){ - if( GetValue2( ret, this, FormatKey( "CD", j + 1, i + 1, s, status ), - AST__FLOAT, (void *) &dval, 0, method, class, status ) ){ - rowsum2 += dval*dval; - if( i == j ) diag = ( dval >= 0.0 ) ? +1 : -1; - } - } - -/* The CDELT value for this row will be the length of the row vector. This means that - each row will be a unit vector when converted to PCi_j form, and the CDELT will - give a real indication of the pixel size. Ensure that the diagonal - PCi+j element has a positive sign. */ - cdelti = sqrt( rowsum2 )*diag; - SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ), - (void *) &cdelti, AST__FLOAT, NULL, status ); - -/* Do each column in the matrix. */ - for( i = 0; i < naxis; i++ ){ - -/* Get the CDj_i matrix element (note default value for all CD elements - is zero (even diagonal elements!). */ - if( !GetValue2( ret, this, FormatKey( "CD", j + 1, i + 1, s, status ), - AST__FLOAT, (void *) &dval, 0, method, class, status ) ){ - dval = 0.0; - } - -/* Divide by the rows cdelt value and save it with name PCj_i. */ - if( cdelti != 0.0 ) dval /= cdelti; - SetValue( ret, FormatKey( "PC", j + 1, i + 1, s, status ), - (void *) &dval, AST__FLOAT, NULL, status ); - gotpcij = 1; - } - } - } - -/* PCjjjiii and CROTAi keywords - ---------------------------- */ - -/* Check there are some CDELT keywords... */ - if( s != ' ' ) { - sprintf( template, "CDELT%%d%c", s ); - } else { - strcpy( template, "CDELT%d" ); - } - if( !gotpcij && astKeyFields( this, template, 0, NULL, NULL ) ){ - -/* See if there is a CROTA keyword. Try to read values for both axes - since they are sometimes both included. This ensures they will not be - included in the output when the FitsChan is deleted. Read the latitude - axis second in order to give it priority in cases where both are - present. */ - crota = AST__BAD; - GetValue2( ret, this, FormatKey( "CROTA", axlon + 1, -1, s, status ), - AST__FLOAT, (void *) &crota, 0, method, class, status ); - GetValue2( ret, this, FormatKey( "CROTA", axlat + 1, -1, s, status ), - AST__FLOAT, (void *) &crota, 0, method, class, status ); - -/* If there are any PCjjjiii keywords, rename them as PCj_i. */ - if( s == ' ' && astKeyFields( this, "PC%3d%3d", 0, NULL, NULL ) ){ - -/* Do each row in the matrix. */ - for( j = 0; j < naxis; j++ ){ - -/* Do each column in the matrix. */ - for( i = 0; i < naxis; i++ ){ - -/* Get the PCiiijjj matrix element */ - sprintf( keyname, "PC%.3d%.3d", j + 1, i + 1 ); - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, - method, class, status ) ){ - } else if( i == j ) { - dval = 1.0; - } else { - dval = 0.0; - } - -/* Store it as PCi_j */ - SetValue( ret, FormatKey( "PC", j + 1, i + 1, ' ', status ), - (void *) &dval, AST__FLOAT, NULL, status ); - gotpcij = 1; - } - } - -/* If there is a CROTA value and no PCjjjii keywords, create a PCj_i - matrix from the CROTA values. We need to have latitude and longitude - axes for this. */ - } else if( s == ' ' && axlat != -1 && axlon != -1 && crota != AST__BAD ){ - -/* Get the sin and cos of CROTA */ - cosrota = cos( crota*AST__DD2R ); - sinrota = sin( crota*AST__DD2R ); - -/* Get the CDELT values for the longitude and latitude axes. */ - if( GetValue2( ret, this, FormatKey( "CDELT", axlat + 1, -1, ' ', status ), - AST__FLOAT, (void *) &cdeltj, 1, method, - class, status ) && - GetValue2( ret, this, FormatKey( "CDELT", axlon + 1, -1, ' ', status ), - AST__FLOAT, (void *) &cdelti, 1, method, - class, status ) ){ - -/* Save the ratio, needed below. */ - lambda = cdeltj/cdelti; - -/* Save a corresponding set of PCi_j keywords in the FitsChan. First do - the diagonal terms. */ - for( i = 0; i < naxis; i++ ){ - if( i == axlat ) { - dval = cosrota; - } else if( i == axlon ) { - dval = cosrota; - } else { - dval = 1.0; - } - SetValue( ret, FormatKey( "PC", i + 1, i + 1, ' ', status ), - (void *) &dval, AST__FLOAT, NULL, status ); - gotpcij = 1; - } - -/* Now do the non-zero off-diagonal terms. */ - dval = sinrota/lambda; - SetValue( ret, FormatKey( "PC", axlat + 1, axlon + 1, ' ', status ), - (void *) &dval, AST__FLOAT, NULL, status ); - dval = -sinrota*lambda; - SetValue( ret, FormatKey( "PC", axlon + 1, axlat + 1, ' ', status ), - (void *) &dval, AST__FLOAT, NULL, status ); - } - } - } - } - -/* Conversion of old PROJP, etc, is done once on the "primary" pass. */ - if( s == ' ' ) { - -/* PROJP keywords - -------------- */ - if( astKeyFields( this, "PROJP%d", 1, ubnd, lbnd ) && axlat != -1 ) { - -/* Some people produce headers with both PROJP and PV. Even worse, the - PROJP and PV values are sometimes inconsistent. In this case we trust - the PV values rather than the PROJP values, but only if the PV values - are not obviously incorrect for some reason. In particularly, we check - that, *if* either PVi_1 or PVi_2 (where i=longitude axis) is present, - then PVi_0 is also present. Conversely we check that if PVi_0 is - present then at least one of PVi_1 or PVi_2 is present. */ - use_projp = 1; - if( axlat != -1 && - astKeyFields( this, "PV%d_%d", 2, tubnd, tlbnd ) ){ - use_projp = 0; - -/* Are there any PV values for the longitude axis? */ - if( tlbnd[ 0 ] <= axlon + 1 && axlon + 1 <= tubnd[ 0 ] ) { - -/* Are either PVi_1 or PVi_2 available? */ - if( HasCard( this, FormatKey( "PV", axlon + 1, 1, ' ', status ), - method, class, status ) || - HasCard( this, FormatKey( "PV", axlon + 1, 2, ' ', status ), - method, class, status ) ) { - -/* If so use PROJP if PVi_0 is not also available. */ - if( !HasCard( this, FormatKey( "PV", axlon + 1, 0, ' ', status ), - method, class, status ) ) use_projp = 1; - -/* If neither PVi_1 or PVi_2 are available, use PROJP if PVi_0 is - available. */ - } else if( HasCard( this, FormatKey( "PV", axlon + 1, 0, ' ', status ), - method, class, status ) ) { - use_projp = 1; - } - } - } - -/* Translate PROJP to PV if required. */ - if( use_projp ) { - for( i = lbnd[ 0 ]; i <= ubnd[ 0 ]; i++ ){ - if( GetValue2( ret, this, FormatKey( "PROJP", i, -1, ' ', status ), - AST__FLOAT, (void *) &dval, 0, method, class, status ) && - ( encoding == FITSPC_ENCODING || - encoding == FITSIRAF_ENCODING ) ){ - SetValue( ret, FormatKey( "PV", axlat + 1, i, ' ', status ), - (void *) &dval, AST__FLOAT, CardComm( this, status ), status ); - } - } - } - } - -/* CmVALi keywords - --------------- */ - if( astKeyFields( this, "C%1dVAL%d", 2, ubnd, lbnd ) ){ - ss = 'A'; - for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ - for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){ - sprintf( keyname, "C%dVAL%d", m, i ); - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, - method, class, status ) && - ( encoding == FITSPC_ENCODING || - encoding == FITSIRAF_ENCODING ) ){ - sprintf( keyname, "CRVAL%d%c", i, ss ); - SetValue( ret, keyname, (void *) &dval, AST__FLOAT, - CardComm( this, status ), status ); - } - } - ss++; - } - } - -/* CmPIXi keywords - --------------- */ - if( astKeyFields( this, "C%1dPIX%d", 2, ubnd, lbnd ) ){ - ss = 'A'; - for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ - for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){ - sprintf( keyname, "C%dPIX%d", m, i ); - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, - method, class, status ) && - ( encoding == FITSPC_ENCODING || - encoding == FITSIRAF_ENCODING ) ){ - sprintf( keyname, "CRPIX%d%c", i, ss ); - SetValue( ret, keyname, (void *) &dval, AST__FLOAT, - CardComm( this, status ), status ); - } - } - ss++; - } - } - -/* CmYPEi keywords - --------------- */ - if( astKeyFields( this, "C%1dYPE%d", 2, ubnd, lbnd ) ){ - ss = 'A'; - for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ - for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){ - sprintf( keyname, "C%dYPE%d", m, i ); - if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, - method, class, status ) && - ( encoding == FITSPC_ENCODING || - encoding == FITSIRAF_ENCODING ) ){ - sprintf( keyname, "CTYPE%d%c", i, ss ); - SetValue( ret, keyname, (void *) &cval, AST__STRING, - CardComm( this, status ), status ); - } - } - ss++; - } - } - -/* CmNITi keywords - --------------- */ - if( astKeyFields( this, "C%1dNIT%d", 2, ubnd, lbnd ) ){ - ss = 'A'; - for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ - for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){ - sprintf( keyname, "C%dNIT%d", m, i ); - if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, - method, class, status ) && - ( encoding == FITSPC_ENCODING || - encoding == FITSIRAF_ENCODING ) ){ - sprintf( keyname, "CUNIT%d%c", i, ss ); - SetValue( ret, keyname, (void *) &cval, AST__STRING, - CardComm( this, status ), status ); - } - } - ss++; - } - } - -/* CmELTi keywords - --------------- */ - if( astKeyFields( this, "C%1dELT%d", 2, ubnd, lbnd ) ){ - ss = 'A'; - for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ - -/* Create a PCj_is matrix by copying the PCjjjiii values and rename CmELTi as - CDELTis. */ - -/* Do each row in the matrix. */ - for( j = 0; j < naxis; j++ ){ - -/* Get the CDELT value for this row. Report an error if not present. */ - sprintf( keyname, "C%dELT%d", m, j + 1 ); - GetValue2( ret, this, keyname, AST__FLOAT, (void *) &cdeltj, 1, - method, class, status ); - -/* If CDELT is zero, use one hundredth of the corresponding CRVAL value - instead, or 1.0 if CRVAL is zero. Otherwise, the zeros could cause the - matrix to be non-invertable. The Mapping could then not be simplified - or used by a Plot. CDELT values of zero are usually used to indicate - "redundant" axes. For instance, a 2D image may be stored as a 3D cube - with a single plane with the "redundant" 3rd axis used to specify the - wavelength of the filter. The actual value used for CDELT shouldn't - matter since the axis only spans a single pixel anyway. */ - if( cdeltj == 0.0 ){ - GetValue2( ret, this, FormatKey( "CRVAL", j + 1, -1, ss, status ), AST__FLOAT, - (void *) &dval, 1, method, class, status ); - cdeltj = 0.01*dval; - if( cdeltj == 0.0 ) cdeltj = 1.0; - } - -/* Save it as CDELTis */ - sprintf( keyname, "CDELT%d%c", j + 1, ss ); - SetValue( ret, keyname, (void *) &cdeltj, AST__FLOAT, - CardComm( this, status ), status ); - -/* Do each column in the matrix. */ - for( i = 0; i < naxis; i++ ){ - -/* Get the PCiiijjj matrix element */ - sprintf( keyname, "PC%.3d%.3d", j + 1, i + 1 ); - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, - method, class, status ) ){ - } else if( i == j ) { - dval = 1.0; - } else { - dval = 0.0; - } - -/* Store it as PCi_js. */ - SetValue( ret, FormatKey( "PC", j + 1, i + 1, ss, status ), - (void *) &dval, AST__FLOAT, NULL, status ); - } - } - ss++; - } - } - -/* EPOCH keywords - ------------ */ - -/* Get any EPOCH card, marking it as read. */ - if( GetValue2( ret, this, "EPOCH", AST__FLOAT, (void *) &dval, 0, method, - class, status ) ){ - -/* Convert values of zero to B1950. */ - if( dval == 0.0 ) dval = 1950.0; - -/* Save a new EQUINOX card in the FitsChan, so long as there is not - already one there. */ - if( !GetValue2( ret, this, "EQUINOX", AST__STRING, (void *) &cval, 0, - method, class, status ) ){ - SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT, - "Reference equinox", status ); - } - } - -/* String EQUINOX values - --------------------- - If found, EQUINOX will be used in favour of any EPOCH value found - above. */ - if( GetValue2( ret, this, "EQUINOX", AST__STRING, (void *) &cval, 0, method, - class, status ) ){ - -/* Note the first character. */ - bj = cval[ 0 ]; - -/* If it is "B" or "J", read a floating value from the rest */ - if( bj == 'B' || bj == 'J' ) { - if( 1 == astSscanf( cval + 1, " %lf ", &dval ) ){ - -/* If it is a Besselian epoch, convert to Julian. */ - if( bj == 'B' ) dval = palEpj( palEpb2d( dval ) ); - -/* Replace the original EQUINOX card. */ - SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT, - CardComm( this, status ), status ); - } - } - } - -/* EQUINOX = 0.0 keywords - ---------------------- */ - if( GetValue2( ret, this, "EQUINOX", AST__FLOAT, (void *) &dval, 0, method, - class, status ) ){ - if( dval == 0.0 ){ - dval = 1950.0; - SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT, - CardComm( this, status ), status ); - } - } - } - -/* DATE-OBS keywords - ---------------- */ - -/* Read any DATE-OBS card. This prevents it being written out when the - FitsChan is deleted. */ - if( s == ' ' ) { - strcpy( keyname, "DATE-OBS" ); - if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method, - class, status ) ){ - -/* Ignore DATE-OBS values if the header contains an MJD-OBS value */ - strcpy( keyname, "MJD-OBS" ); - if( !GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, - method, class, status ) ){ - -/* Get the corresponding mjd-obs value, checking that DATE-OBS is valid. */ - dval = DateObs( cval, status ); - if( dval != AST__BAD ){ - SetValue( ret, keyname, (void *) &dval, AST__FLOAT, - "Date of observation", status ); - } - } - } - } - -/* Things specific to the CLASS encoding - ------------------------------------- */ - if( encoding == FITSCLASS_ENCODING ) ClassTrans( this, ret, axlat, - axlon, method, class, status ); - -/* Convert SAO distorted TAN headers to TPN distorted TAN headers. - -------------------------------------------------------------- */ - if( s == ' ' && !Ustrcmp( prj, "-TAN", status ) ){ - -/* Translate the COi_m keywords into PV i+m keywords. */ - if( SAOTrans( this, ret, method, class, status ) ) { - -/* Change the CTYPE projection form TAN to TPV. */ - strcpy( prj, "-TPN" ); - strcpy( lontype + 4, "-TPN" ); - cval = lontype; - SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - strcpy( lattype + 4, "-TPN" ); - cval = lattype; - SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - } - } - -/* AIPS "NCP" projections - --------------------- */ - -/* Compare the projection type with "-NCP" */ - if( !Ustrcmp( prj, "-NCP", status ) ) { - -/* Get the latitude reference value, and take is cot. */ - GetValue2( ret, this, FormatKey( "CRVAL", axlat + 1, -1, s, status ), - AST__FLOAT, (void *) &dval, 1, method, class, status ); - sinval = sin( dval*AST__DD2R ); - if( sinval != 0.0 ) { - dval = cos( dval*AST__DD2R )/sinval; - -/* Replace NCP with SIN in the CTYPE values. */ - strcpy( lontype + 4, "-SIN" ); - cval = lontype; - SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - strcpy( lattype + 4, "-SIN" ); - cval = lattype; - SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - -/* Store the new projection parameters using names suitable to FITS_WCS - encoding. */ - SetValue( ret, FormatKey( "PV", axlat + 1, 2, s, status ), - (void *) &dval, AST__FLOAT, NULL, status ); - dval = 0.0; - SetValue( ret, FormatKey( "PV", axlat + 1, 1, s, status ), - (void *) &dval, AST__FLOAT, NULL, status ); - } - } - -/* CLASS "ATF" projections - ---------------------- */ - -/* Replace ATF with AIT in the CTYPE values. */ - if( !Ustrcmp( prj, "-ATF", status ) ) { - strcpy( lontype + 4, "-AIT" ); - cval = lontype; - SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - strcpy( lattype + 4, "-AIT" ); - cval = lattype; - SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - } - -/* AIPS "GLS" projections - --------------------- */ - -/* Compare the projection type with "-GLS" */ - if( !Ustrcmp( prj, "-GLS", status ) ) { - -/* Convert to "-SFL" */ - strcpy( lontype + 4, "-SFL" ); - cval = lontype; - SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - strcpy( lattype + 4, "-SFL" ); - cval = lattype; - SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - -/* FITS-WCS paper 2 (sec. 6.1.4) describes how to handle AIPS GLS - projections, but requires that the axes are not rotated. Instead, we - modify the native latitude at the fiducial point, theta_0, as is done - in wcslib function celfix in file wcsfix.c (see also FITS-WCS paper - II sec. 2.5). We only need to change theta_0 if the CRVAL position is - not the celestial origin. */ - shifted = 0; - sprintf( keyname, "CRVAL%d", axlon + 1 ); - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, - method, class, status ) ){ - if( dval != 0.0 ) shifted = 1; - } - sprintf( keyname, "CRVAL%d", axlat + 1 ); - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, - method, class, status ) ){ - if( dval != 0.0 ) shifted = 1; - } - - if( 0 && shifted ) { - SetValue( ret, FormatKey( "PV", axlon + 1, 2, s, status ), - (void *) &dval, AST__FLOAT, NULL, status ); - dval = 0.0; - SetValue( ret, FormatKey( "PV", axlon + 1, 1, s, status ), - (void *) &dval, AST__FLOAT, NULL, status ); - dval = 1.0; - SetValue( ret, FormatKey( "PV", axlon + 1, 0, s, status ), - (void *) &dval, AST__FLOAT, NULL, status ); - } - } - -/* Rename any "QV" projection parameters to "PV" (such as used by - -TAB to indicate the interpolation method, or by the internal - -TPN projection to indicate distortion coefficients). - ------------------------------------------------------------ */ - -/* Rewind the FitsChan. */ - astClearCard( this ); - -/* Search the FitsChan for QV cards. */ - if( s != ' ' ) { - sprintf( template, "QV%%d_%%d%c", s ); - } else { - strcpy( template, "QV%d_%d" ); - } - while( FindKeyCard( this, template, method, class, status ) && astOK ) { - -/* If the projection name is "TAN", replace TAN with TPN in the CTYPE values. */ - if( !Ustrcmp( prj, "-TAN", status ) ){ - strcpy( prj, "-TPN" ); - strcpy( lontype + 4, "-TPN" ); - cval = lontype; - SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - strcpy( lattype + 4, "-TPN" ); - cval = lattype; - SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - } - -/* Indicate that the QV card has been consumed. */ - MarkCard( this, status ); - -/* Get the keyword name and change it from QV to PV. */ - strcpy( keyname, CardName( this, status ) ); - keyname[ 0 ] ='P'; - -/* Store the new PV card so long as there it is not already present in the - FitsChan. */ - if( !GetValue2( ret, this, keyname, AST__FLOAT, (void *) &cval, 0, - method, class, status ) ){ - SetValue( ret, keyname, CardData( this, &size, status ), AST__FLOAT, - CardComm( this, status ), status ); - } - -/* Move on to the next card. */ - MoveCard( this, 1, method, class, status ); - } - - - -/* Change any TAN projection to TPN projection if the PolyTan attribute - is non-zero. Also change any TPV projection to TPN projection. - --------------------------------------------------- */ - if( ( !Ustrcmp( prj, "-TAN", status ) && - GetUsedPolyTan( this, ret, axlat + 1, axlon + 1, s, method, class, status ) ) || - !Ustrcmp( prj, "-TPV", status ) ){ - strcpy( prj, "-TPN" ); - strcpy( lontype + 4, "-TPN" ); - cval = lontype; - SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - strcpy( lattype + 4, "-TPN" ); - cval = lattype; - SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - } - - - -/* IRAF "ZPX" projections - --------------------- */ - if( s == ' ' && !Ustrcmp( prj, "-ZPX", status ) ) { - -/* Replace "ZPX" with "ZPN-ZPX" (i.e. ZPN projection with ZPX distortion - code) in the CTYPE values. */ - strcpy( lontype + 4, "-ZPN-ZPX" ); - cval = lontype; - SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, ' ', status ), - (void *) &cval, AST__STRING, NULL, status ); - strcpy( lattype + 4, "-ZPN-ZPX" ); - cval = lattype; - SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, ' ', status ), - (void *) &cval, AST__STRING, NULL, status ); - -/* Check latitude then longitude axes */ - for( i = 0; i < 2; i++ ){ - iaxis = i ? axlat : axlon; - -/* Concatenate all the IRAF "WAT" keywords together for this axis. These - keywords are marked as having been used, so that they are not written - out when the FitsChan is deleted. */ - watmem = ConcatWAT( this, iaxis, method, class, status ); - -/* Search the total WAT string for any projp terms. */ - if( watmem ){ - for( iproj = 0; iproj < 10 && astOK; iproj++ ) { - sprintf( format, "projp%d=", iproj ); - start = strstr( watmem, format ); - if( start ) { - sprintf( format, "projp%d=%%lf", iproj ); - if( astSscanf( start, format, &projp ) ){ - SetValue( ret, FormatKey( "PV", axlat + 1, iproj, ' ', status ), - (void *) &projp, AST__FLOAT, - "ZPN projection parameter", status ); - } - } - } - -/* Release the memory used to hold the concatenated WAT keywords. */ - watmem = (char *) astFree( (void *) watmem ); - } - } - -/* IRAF "TNX" projections - --------------------- */ - } else if( s == ' ' && !Ustrcmp( prj, "-TNX", status ) ) { - -/* Replace TNX with TPN in the CTYPE values. */ - strcpy( lontype + 4, "-TPN" ); - cval = lontype; - SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, ' ', status ), - (void *) &cval, AST__STRING, NULL, status ); - strcpy( lattype + 4, "-TPN" ); - cval = lattype; - SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, ' ', status ), - (void *) &cval, AST__STRING, NULL, status ); - -/* Check latitude then longitude axes */ - for( i = 0; i < 2; i++ ){ - iaxis = i ? axlat : axlon; - -/* Concatenate all the IRAF "WAT" keywords together for this axis. These - keywords are marked as having been used, so that they are not written - out when the FitsChan is deleted. */ - watmem = ConcatWAT( this, iaxis, method, class, status ); - -/* Extract the polynomial coefficients from the concatenated WAT string. - These are returned in the form of a list of PVi_m values for a TPN - projection. */ - ncoeff = WATCoeffs( watmem, i, &cvals, &mvals, &ok, status ); - -/* If we can handle the TNX projection, store the PV values in the FitsChan. */ - if( ok ) { - for( icoeff = 0; icoeff < ncoeff; icoeff++ ) { - SetValue( ret, FormatKey( "PV", iaxis + 1, mvals[ icoeff ], - ' ', status ), - (void *) (cvals + icoeff), AST__FLOAT, - "TAN projection parameter", status ); - } - -/* If the TNX cannot be represented in FITS-WCS (within our restrictions), add - warning keywords to the FitsChan. */ - } else { - Warn( this, "tnx", "This FITS header includes, or was " - "derived from, a TNX projection which requires " - "unsupported IRAF-specific corrections. The WCS " - "information may therefore be incorrect.", method, class, status ); - } - -/* Release the memory used to hold the concatenated WAT keywords. */ - watmem = (char *) astFree( (void *) watmem ); - } - } - -/* MSX CAR projections. - ------------------- */ - if( !Ustrcmp( prj, "-CAR", status ) && !astGetCarLin( this ) ) { - -/* The CAR projection has valid projection plane points only for native - longitudes in the range [-180,+180]. The reference pixel (CRPIX) is at - native longitude zero. We need to check that the reference point is not - so far outside the image that the entire image lies outside the range - [-180,+180]. If it is, we modify the CRPIX value by the number of - pixels corresponding to 360 degres of longitude in order to bring the - array into the valid domain ([-180,+180]) of the projection. */ - if( GetValue2( ret, this, FormatKey( "CDELT", axlon + 1, -1, s, status ), - AST__FLOAT, (void *) &cdelti, 1, method, class, status ) && - GetValue2( ret, this, FormatKey( "CRPIX", axlon + 1, -1, s, status ), - AST__FLOAT, (void *) &dval, 0, method, class, status ) ) { - -/* We check if the mid point of the array is in the valiud longitude range. Use the - bottom left corner as a fallback if the image size is unknown. */ - if( !GetValue( this, FormatKey( "NAXIS", axlon + 1, -1, ' ', status ), - AST__INT, &dim, 0, 0, method, class, status ) ) { - dim = 0; - } - - if( cdelti != 0.0 ) { - double offset = 0.5*( dim + 1 ); - dval = offset + AST__DR2D*palDrange( AST__DD2R*( dval - offset )*cdelti )/cdelti; - SetValue( ret, FormatKey( "CRPIX", axlon + 1, -1, s, status ), - (void *) &dval, AST__FLOAT, CardComm( this, status ), status ); - } - } - } - -/* Replace RESTFREQ by RESTFRQ. - ---------------------------- */ - -/* Get any RESTFREQ card, marking it as read. */ - if( s != ' ' ) { - sprintf( keyname, "RESTFREQ%c", s ); - } else { - strcpy( keyname, "RESTFREQ" ); - } - if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, method, - class, status ) ){ - -/* Look for "MHz" and "GHz" within the comment. If found scale the value - into Hz. */ - comm = CardComm( this, status ); - if( comm ) { - if( strstr( comm, "GHz" ) ) { - dval *= 1.0E9; - comm = "[Hz] Rest Frequency"; - } else if( strstr( comm, "MHz" ) ) { - dval *= 1.0E6; - comm = "[Hz] Rest Frequency"; - } - } - -/* Save a new RESTFRQ card in the FitsChan, so long as there is not - already one there. */ - if( s != ' ' ) { - sprintf( keyname, "RESTFRQ%c", s ); - } else { - strcpy( keyname, "RESTFRQ" ); - } - if( !GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, - method, class, status ) ){ - SetValue( ret, keyname, (void *) &dval, AST__FLOAT, comm, status ); - } - } - -/* Translate AIPS spectral CTYPE values to FITS-WCS paper III equivalents. - These are of the form AAAA-BBB, where "AAAA" can be "FREQ", "VELO" (=VRAD!) - or "FELO" (=VOPT-F2W), and BBB can be "LSR", "LSD", "HEL" (=*Bary*centric!) - or "GEO". Also convert "LAMBDA" to "WAVE". */ - for( j = 0; j < naxis; j++ ) { - if( GetValue2( ret, this, FormatKey( "CTYPE", j + 1, -1, s, status ), - AST__STRING, (void *) &cval, 0, method, - class, status ) ){ - if( IsAIPSSpectral( cval, &astype, &assys, status ) ) { - SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ), - (void *) &astype, AST__STRING, NULL, status ); - SetValue( ret, "SPECSYS", (void *) &assys, AST__STRING, NULL, status ); - break; - } else if( !strcmp( cval, "LAMBDA " ) ) { - cval = "WAVE"; - SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ), - (void *) &cval, AST__STRING, NULL, status ); - break; - } - } - } - -/* Common case insensitive CUNIT values: "Hz", "Angstrom", "km/s", "M/S" */ - if( s != ' ' ) { - sprintf( template, "CUNIT%%d%c", s ); - } else { - strcpy( template, "CUNIT%d" ); - } - if( astKeyFields( this, template, 1, &jhi, &jlo ) ){ - -/* Convert keyword indices from 1-based to 0-base, and loop round them all. */ - jhi--; - jlo--; - for( j = jlo; j <= jhi; j++ ){ - char *keynam; - keynam = FormatKey( "CUNIT", j + 1, -1, s, status ); - if( GetValue2( ret, this, keynam, AST__STRING, (void *) &cval, 0, - method, class, status ) ){ - size_t nc = astChrLen( cval ); - if( nc == 0 ) { - cval = NULL; - } else if( !Ustrcmp( cval, "Hz", status ) ) { - cval = "Hz"; - } else if( !Ustrcmp( cval, "Angstrom", status ) ) { - cval = "Angstrom"; - } else if( !Ustrcmp( cval, "km/s", status ) ) { - cval = "km/s"; - } else if( !Ustrcmp( cval, "m/s", status ) ) { - cval = "m/s"; - } else { - cval = NULL; - } - if( cval ) SetValue( ret, keynam, (void *) &cval, AST__STRING, NULL, status ); - } - } - } - -/* After doing the primary axis descriptions, prepare to do the "A" - description. */ - if( s == ' ' ) s = 'A' - 1; - } - -/* IRAF mini-WCS keywords - ---------------------- */ - -/* Rewind the FitsChan to search from the first card. */ - astClearCard( this ); - -/* Search forward through until all cards have been checked. */ - while( !astFitsEof( this ) && astOK ){ - -/* Check to see if the keyword name from the current card matches - any of the known mini-WCS keywords. If so, mark the card as read. */ - if( Match( CardName( this, status ), "WAT%d_%d", 0, NULL, &m, method, class, status ) || - Match( CardName( this, status ), "LTM%d_%d", 0, NULL, &m, method, class, status ) || - Match( CardName( this, status ), "LTV%d", 0, NULL, &m, method, class, status ) || - Match( CardName( this, status ), "WSV%d_LEN", 0, NULL, &m, method, class, status ) || - Match( CardName( this, status ), "WSV%d_%d", 0, NULL, &m, method, class, status ) ){ - MarkCard( this, status ); - } - -/* Now move the current card on to the next card. */ - MoveCard( this, 1, method, class, status ); - } - -/* Delete the returned FitsChan if it is empty. */ - if( ret && !astGetNcard( ret ) ) ret = (AstFitsChan *) astDelete( ret ); - -/* Return. */ - return ret; -} - -int Split( AstFitsChan *this, const char *card, char **name, char **value, - char **comment, const char *method, const char *class, int *status ){ -/* -* Name: -* Split - -* Purpose: -* Extract the keyword name, value and comment from a FITS header card. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int Split( AstFitsChan *this, const char *card, char **name, char **value, -* char **comment, const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* The name, value and comment (if present) are extracted from the -* supplied card text and returned. - -* Parameters: -* this -* Pointer to the FitsCHan. -* card -* Pointer to a string holding the FITS header card. -* name -* Pointer to a location at which to return the pointer to a string -* holding the keyword name. -* value -* Pointer to a location at which to return the pointer to a string -* holding the keyword value. -* comment -* Pointer to a location at which to return the pointer to a string -* holding the keyword comment. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned value: -* - An integer identifying the data type of the keyword value. This -* will be one of the values AST__UNDEF, AST__COMMENT, AST__INT, -* AST__STRING, AST__CONTINUE, AST__FLOAT, AST__COMPLEXI or AST__COMPLEXF -* defined in fitschan.h. - -* Notes: -* - If the keyword value is a string, then the returned value does not -* include the delimiting quotes, and pairs of adjacent quotes within the -* string are replaced by single quotes. -* - A maximum of 80 characters are read from the supplied card, so the -* string does not need to be null terminated unless less than 80 -* characters are to be read. -* - The memory holding the three strings "name", "value" and "comment" -* should be released when no longer needed using astFree. -* - NULL pointers and a data type of AST__COMMENT are returned if an -* error has already occurred, or if this function fails for any reason. -*/ - -/* Local Variables: */ - char *c; /* Pointer to returned comment string */ - char *dd; /* Pointer to intermediate character */ - char *slash; /* Pointer to comment character */ - char *v; /* Pointer to returned value string */ - char buf[255]; /* Buffer for warning text */ - const char *d; /* Pointer to first comment character */ - const char *v0; /* Pointer to first non-blank value character */ - double fi, fr; /* Values read from value string */ - int badval; /* Is the keyword value illegal? */ - int blank_name; /* Is keyword name blank? */ - int cont; /* Is this a continuation card? */ - int i; /* Character index */ - int ii, ir; /* Values read from value string */ - int iopt; /* Index of option within list */ - int len; /* Used length of value string */ - int lq; /* Was previous character an escaping quote? */ - int nch; /* No. of characters used */ - int ndig; /* No. of digits in the formatted integer */ - int type; /* Keyword data type */ - size_t nc; /* Number of character in the supplied card */ - size_t ncc; /* No. of characters in the comment string */ - size_t ncv; /* No. of characters in the value string */ - -/* Initialise the returned pointers. */ - *name = NULL; - *value = NULL; - *comment = NULL; - type = AST__COMMENT; - -/* Check the global status. */ - if( !astOK ) return type; - -/* Assume initially that the keyword value is legal. */ - badval = 0; - -/* Store the number of characters to be read from the supplied card. This - is not allowed to be more than the length of a FITS header card. */ - nc = 0; - while( nc < AST__FITSCHAN_FITSCARDLEN && card[ nc ] ) nc++; - -/* Reduce the number of characters to read so that any non-printing - characters such as new-lines at the end of the string are ignored. */ - while( nc > 0 && !isprint( card[ nc - 1 ] ) ) nc--; - -/* Allocate memory for a copy of the keyword name plus a terminating - null character. */ - *name = (char *) astMalloc( ( 1 + FITSNAMLEN )*sizeof(char) ); - -/* Check the pointer can be used. */ - if( astOK ){ - -/* Initialise the name string by filling it with spaces, and terminating it. */ - for( i = 0; i < FITSNAMLEN; i++ ) (*name)[ i ] = ' '; - (*name)[ FITSNAMLEN ] = 0; - -/* Copy the the keyword name, ensuring that no more than FITSNAMLEN (8) - characters are copied. */ - strncpy( *name, card, ( nc > FITSNAMLEN ) ? FITSNAMLEN : nc ); - -/* If there is no keyword name, flag that we have a blank name which will - be treated as a comment card. */ - if( strspn( *name, " " ) == strlen( *name ) ){ - blank_name = 1; - -/* If the card contains a keyword name, replace any white space with - nulls. */ - } else { - blank_name = 0; - dd = *name + strlen( *name ) - 1; - while( isspace( *dd ) ) *(dd--) = 0; - } - -/* Check the keyword name is legal. */ - CheckFitsName( this, *name, method, class, status ); - -/* Allocate memory to hold the keyword value and comment strings. */ - *value = (char *) astMalloc( sizeof(char)*( 2 + nc ) ); - *comment = (char *) astMalloc( sizeof(char)*( 1 + nc ) ); - -/* Check the pointers can be used. */ - if( astOK ){ - -/* Check for CONTINUE cards. These have keyword CONTINUE but have a space - instead of an equals sign in column 9. They must also have a single quote - in column 11. */ - cont = ( !Ustrcmp( *name, "CONTINUE", status ) && - nc > FITSNAMLEN + 3 && - card[ FITSNAMLEN ] == ' ' && - card[ FITSNAMLEN + 2 ] == '\'' ); - -/* If column 9 does not contain an equals sign (but is not a CONTINUE card), or if - the keyword is "HISTORY", "COMMENT" or blank, then columns 9 to the end are - comment characters, and the value string is null. */ - if( ( nc <= FITSNAMLEN || card[ FITSNAMLEN ] != '=' - || !Ustrcmp( *name, "HISTORY", status ) - || !Ustrcmp( *name, "COMMENT", status ) - || blank_name ) && !cont ){ - (*value)[ 0 ] = 0; - if( nc > FITSNAMLEN ){ - (void) strncpy( *comment, card + FITSNAMLEN, - nc - FITSNAMLEN ); - (*comment)[ nc - FITSNAMLEN ] = 0; - } else { - (*comment)[ 0 ] = 0; - } - -/* Otherwise there is a value field. */ - } else { - -/* Find the first non-blank character in the value string. */ - v0 = card + FITSNAMLEN + 1; - while( (size_t)(v0 - card) < nc && - isspace( (int) *v0 ) ) v0++; - -/* Store pointers to the start of the returned value and comment strings. */ - v = *value; - c = *comment; - -/* If the first character in the value string is a single quote, the value is - a string. In this case the value ends at the first non-escaped single - quote. */ - if( *v0 == '\''){ - type = cont ? AST__CONTINUE : AST__STRING; - -/* We want to copy the string value, without the delimiting quotes, to the - returned value string. Single quotes within the string are represented - by two adjacent quotes, so we also need to check for these and replace - them by one quote in the returned string. First initialise a pointer - to the first character after the opening quote, and set a flag - indicating that (for the purposes of identifying pairs of adjacent - quotes within the string) the previous character was not a quote. */ - d = v0 + 1; - lq = 0; - -/* Loop round each remaining character in the supplied card. */ - while( (size_t)(d - card) < nc ){ - -/* If the current character is a single quote... */ - if( *d == '\'' ){ - -/* If the previous character was also a single quote then the quote does - not mark the end of the string, but is a quote to be included literally - in the value. Copy the quote to the returned string and clear the flag - to indicate that the pair of adjacent quotes is now complete. */ - if( lq ){ - *(v++) = '\''; - lq = 0; - -/* If the last character was not a quote, then set the flag for the next - pass through the loop, but do not copy the quote to the returned string - since it will either be a quote escaping a following adjacent quote, or - a quote to mark the end of the string. */ - } else { - lq = 1; - } - -/* If the current character is not a quote... */ - } else { - -/* If the previous character was a quote, then we have found a single - isolated quote which therefore marks the end of the string value. - The pointer "d" is left pointing to the first character - after the terminating quote. */ - if( lq ){ - break; - -/* If the last character was not a quote, copy it to the returned string. */ - } else { - *(v++) = *d; - } - } - d++; - } - -/* Terminate the returned value string. */ - *v = 0; - -/* Now deal with logical and numerical values. */ - } else { - -/* The end of the value field is marked by the first "/". Find the number - of characters in the value field. Pointer "d" is left pointing to the - first character in the comment (if any). Only use "/" characters which - occur within the first nc characters, and do not occur wiuthin the - keyword name (not strictly legal, but a warning will have been issued - by CheckFitsName in such cases). */ - d = strchr( card + FITSNAMLEN, '/' ); - if( !d || ( d - card ) >= nc ){ - ncv = nc - FITSNAMLEN - 1; - d = NULL; - } else { - ncv = (size_t)( d - card ) - FITSNAMLEN - 1; - } - -/* Copy the value string to the returned string. */ - if( ncv == 0 ){ - *v = 0; - } else { - strncpy( v, card + FITSNAMLEN + 1, ncv ); - v[ ncv ] = ' '; - v[ ncv + 1 ] = 0; - } - -/* Find the first non-blank character in the value string. */ - v0 = v; - while( *v0 && isspace( (int) *v0 ) ) v0++; - -/* See if the value string is one of the following strings (optionally - abbreviated and case insensitive): YES, NO, TRUE, FALSE. */ - iopt = FullForm( "YES NO TRUE FALSE", v0, 1, status ); - -/* Return the single character "T" or "F" at the start of the value string - if the value matches one of the above strings. */ - if( iopt == 0 || iopt == 2 ) { - type = AST__LOGICAL; - strcpy ( v, "T" ); - } else if( iopt == 1 || iopt == 3 ) { - type = AST__LOGICAL; - strcpy ( v, "F" ); - -/* If it does not match, see if the value is numerical. */ - } else { - -/* Save the length of the value string excluding trailing blanks. */ - len = ChrLen( v, status ); - -/* If the entire string is blank, the value type is UNDEF. */ - if( len == 0 ) { - type = AST__UNDEF; - -/* If there are no dots (decimal points) or exponents (D or E) in the value... */ - } else if( !strpbrk( v, ".EeDd" ) ){ - -/* First attempt to read two integers from the string (separated by white - space). */ - if( nch = 0, - ( 2 == astSscanf( v, " %d %d%n", &ir, &ii, &nch ) ) && - ( nch >= len ) ) { - type = AST__COMPLEXI; - -/* If that failed, attempt to read a single integer from the string. */ - } else if( nch = 0, - ( 1 == astSscanf( v, " %d%n", &ir, &nch ) ) && - ( nch >= len ) ) { - type = AST__INT; - } - -/* If there are dots (decimal points) in the value... */ - } else { - -/* First attempt to read two doubles from the string (separated by white - space). */ - if( nch = 0, - ( 2 == astSscanf( v, " %lf %lf%n", &fr, &fi, &nch ) ) && - ( nch >= len ) ) { - type = AST__COMPLEXF; - -/* If that failed, attempt to read a single double from the string. */ - } else if( nch = 0, - ( 1 == astSscanf( v, " %lf%n", &fr, &nch ) ) && - ( nch >= len ) ) { - type = AST__FLOAT; - } - -/* If both the above failed, it could be because the string contains a - "D" exponent (which is probably valid FITS) instead of an "E" exponent. - Replace any "D" in the string with "e" and try again. */ - if( type == AST__COMMENT && astOK ) { - -/* Replace "d" and "D" by "e" (if this doesn't produce a readable floating - point value then the value string will not be used, so it is safe to - do the replacement in situ). */ - for( i = 0; i < len; i++ ) { - if( v[ i ] == 'd' || v[ i ] == 'D' ) v[ i ] = 'e'; - } - -/* Attempt to read two doubles from the edited string (separated by white - space). */ - if( nch = 0, - ( 2 == astSscanf( v, " %lf %lf%n", &fr, &fi, &nch ) ) && - ( nch >= len ) ) { - type = AST__COMPLEXF; - -/* If that failed, attempt to read a single double from the edited string. */ - } else if( nch = 0, - ( 1 == astSscanf( v, " %lf%n", &fr, &nch ) ) && - ( nch >= len ) ) { - type = AST__FLOAT; - } - } - } - } - -/* If the value type could not be determined, indicate that a warning - should be issued. */ - if( type == AST__COMMENT && astOK ) { - badval = 1; - (*value)[ 0 ] = 0; - (*comment)[ 0 ] = 0; - d = NULL; - } - } - -/* Find the number of characters in the comment. Pointer "d" should point to - the first character following the value string. */ - if( d ){ - ncc = nc - (size_t)( d - card ); - } else { - ncc = 0; - } - -/* Copy the remainder of the card to the returned comment string. */ - if( astOK && ncc > 0 ){ - strncpy( c, d, ncc ); - c[ ncc ] = 0; - -/* Find the start of the comment (indicated by the first "/" after the - value string). */ - slash = strchr( c, '/' ); - -/* Temporarily terminate the string at the slash. */ - if( slash ) *slash = 0; - -/* Shuffle the characters following the slash down to the - start of the returned string. */ - if( slash ){ - ncc -= (size_t)( slash - c ) + 1; - d = slash + 1; - for( i = 0; i < 1 + (int) ncc; i++ ) *(c++) = *(d++); - } - -/* If there is no comment string, return a null string. */ - } else { - *c = 0; - } - } - } - } - -/* Truncate the returned string to avoid wasting space. */ - if( *name ) *name = (char *) astRealloc( (void *) *name, strlen( *name ) + 1 ); - if( *comment ) *comment = (char *) astRealloc( (void *) *comment, strlen( *comment ) + 1 ); - if( *value ) *value = (char *) astRealloc( (void *) *value, strlen( *value ) + 1 ); - -/* If the value is deemed to be integer, check that the number of digits - in the formatted value does not exceed the capacity of an int. This may - be the case if there are too many digits in the integer for an "int" to - hold. In this case, change the data type to float. */ - if( *value && type == AST__INT ) { - ndig = 0; - c = *value; - while( *c ) { - if( isdigit( *(c++) ) ) ndig++; - } - if( ndig >= int_dig ) type = AST__FLOAT; - } - -/* If an error occurred, free the returned strings and issue a context message. */ - if( !astOK ){ - *name = (char *) astFree( (void *) *name ); - *value = (char *) astFree( (void *) *value ); - *comment = (char *) astFree( (void *) *comment ); - type = AST__COMMENT; - astError( astStatus, "%s(%s): Unable to store the following FITS " - "header card:\n%.*s\n", status, method, class, - AST__FITSCHAN_FITSCARDLEN, card ); - -/* If a bad keyword value was encountered, issue a warning. Remember that - "card" may not be null terminated, so ensure that only one header is - included from "card". */ - } else if( badval ){ - snprintf( buf, sizeof(buf), "The keyword value is illegal in " - "'%.*s'", AST__FITSCHAN_FITSCARDLEN, card ); - Warn( this, "badkeyvalue", buf, method, class, status ); - } - -/* Return the data type. */ - return type; -} - -static int SplitMap( AstMapping *map, int invert, int ilon, int ilat, - AstMapping **map1, AstWcsMap **map2, AstMapping **map3, int *status ){ -/* -* Name: -* SplitMap - -* Purpose: -* Locate a WCS projection within a Mapping. - -* Type: -* Private function. - -* Synopsis: -* int SplitMap( AstMapping *map, int invert, int ilon, int ilat, -* AstMapping **map1, AstWcsMap **map2, AstMapping **map3, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* If possible, the supplied Mapping is decomposed into three component -* mappings to be compounded in series. To be acceptable, the second of -* these three Mappings must be an inverted WcsMap with a non-zero -* FITSProj attribute value, and there must not be such a WcsMap in -* either of the other two Mappings. If it is not possible to produce -* such a group of three Mappings, then a zero function value is returned, -* together with three NULL Mapping pointers. All the mappings before the -* WcsMap are compounded together and returned as "map1". The inverse of -* the WcsMap itself is returned as "map2", and any remaining Mappings -* are compounded together and returned as "map3". -* -* The search algorithm allows for an arbitrary combination of series and -* parallel CmpMaps. - -* Parameters: -* map -* A pointer to the Mapping from pixel to physical coordinates. -* invert -* The value of the Invert attribute to use with "map" (the value -* returned by astGetInvert is not used). -* ilon -* Index of mapping output which is connected to the longitude axis. -* ilat -* Index of mapping output which is connected to the latitude axis. -* map1 -* A location at which to return a pointer to the Mapping from pixel -* to intermediate world coordinates. -* map2 -* A location at which to return a pointer to the Mapping from -* intermediate world coordinates to native spherical coordinates. This -* will be an inverted WcsMap with non-zero FITSProj attribute value. -* map3 -* A location at which to return a pointer to the Mapping from -* native spherical coordinates to physical coordinates. -* dep -* The address of an integer holding the current depth of recursion -* into this function. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* One if a suitable WcsMap was found, zero otherwise. - -* Notes: -* - The returned Mappings contain independant copies of the relevant -* components of the supplied Mapping and can be modified without -* changing the supplied Mapping. -* - NULL pointers will be returned for all Mappings if no WcsMap -* can be found in the supplied Mapping. -* - A pointer to a UnitMap will be returned for map1 if no mappings -* exist before the WcsMap. -* - A pointer to a UnitMap will be returned for map3 if no mappings -* exist after the WcsMap. -* - NULL pointers will be returned for all Mappings and a function -* value of zero will be returned if an error has occurred, or if this -* function should fail for any reason. -*/ - -/* Local Variables */ - AstFitsChan *fc; /* Pointer to temporary FitsChan */ - AstFrameSet *tfs; /* Temporary FrameSet */ - AstMapping *mapa; /* Pre-wcs Mapping */ - AstMapping *mapc; /* Post-wcs Mapping */ - AstMapping *tmap1; /* Temporary Mapping */ - AstMapping *tmap2; /* Temporary Mapping */ - AstPointSet *pset1; /* Pixel positions */ - AstPointSet *pset2; /* WCS positions */ - AstWcsMap *mapb; /* WcsMap */ - char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Buffer for header card */ - double **ptr1; /* Pointer to pixel axis values */ - double **ptr2; /* Pointer to WCS axis values */ - double *iwc_origin; /* Array holding IWC at pixel origin */ - double *pix_origin; /* Array holding pixel coords at pixel origin */ - double *w1; /* Pointer to work space */ - int i; /* Loop index */ - int npix; /* Number of pixel axes */ - int nwcs; /* Number of WCS axes */ - int ret; /* Was a non-linear Mapping found? */ - -/* Initialise */ - *map1 = NULL; - *map2 = NULL; - *map3 = NULL; - ret = 0; - -/* Check the global status. */ - if( !astOK ) return ret; - -/* Call SplitMap2 to do the work. SplitMap2 does not check that the - WcsMap is an *inverted* WcsMap, neither does it check that there - are no WcsMaps in either map1 or map3. */ - if( SplitMap2( map, invert, map1, map2, map3, status ) ) { - -/* Check that the WcsMap is inverted. */ - if( astGetInvert( *map2 ) ) { - -/* Check that map 1 does not contain a WcsMap with non-zero FITSProj - attribute. */ - if( !SplitMap2( *map1, astGetInvert( *map1 ), &mapa, &mapb, &mapc, - status ) ) { - -/* Check that map 3 does not contain a WcsMap with non-zero FITSProj - attribute. */ - if( !SplitMap2( *map3, astGetInvert( *map3 ), &mapa, &mapb, &mapc, - status ) ) { - -/* If so, the three Mappings are OK. */ - ret = 1; - } else { - mapa = astAnnul( mapa ); - mapb = astAnnul( mapb ); - mapc = astAnnul( mapc ); - } - } else { - mapa = astAnnul( mapa ); - mapb = astAnnul( mapb ); - mapc = astAnnul( mapc ); - } - } - } - -/* If the above failed to find a suitable WcsMap, we now consider cases - where the pixel->WCS mapping is linear. We can invent a CAR projection - WcsMap for such cases. We use a ShiftMap to move the origin of the - longitude IWC axis to a sensible value (it is left at zero otherwise). - We cannot do this with the latitude axis since pre-FITS-WCS fits - readers could not handle the resulting rotation from native to celestial - coords. */ - if( !ret && astGetIsLinear( map ) ) { - nwcs = astGetNout( map ); - npix = astGetNin( map ); - iwc_origin = astMalloc( sizeof( double )*nwcs ); - pix_origin = astMalloc( sizeof( double )*npix ); - if( astOK ) { - for( i = 0; i < npix; i++ ) pix_origin[ i ] = 0.0; - astTranN( map, 1, npix, 1, pix_origin, 1, nwcs, 1, iwc_origin ); - for( i = 0; i < nwcs; i++ ) { - if( i != ilon ) { - iwc_origin[ i ] = 0.0; - } else { - iwc_origin[ i ] *= -1; - } - } - mapa = (AstMapping *) astShiftMap( nwcs, iwc_origin, "", status ); - *map1 = (AstMapping *) astCmpMap( map, mapa, 1, "", status ); - *map2 = astWcsMap( nwcs, AST__CAR, ilon + 1, ilat + 1, "Invert=1", status ); - astInvert( mapa ); - *map3 = astClone( mapa ); - mapa = astAnnul( mapa ); - ret = 1; - } - iwc_origin = astFree( iwc_origin ); - pix_origin = astFree( pix_origin ); - } - -/* If the above failed to find a suitable WcsMap, we now consider cases - where the output (long,lat) values are constants supplied by a - final PermMap. We can invent a WcsMap for such cases. */ - if( !ret ) { - -/* Transform two arbitrary pixel positions into the WCS Frame. */ - npix = astGetNin( map ); - nwcs = astGetNout( map ); - pset1 = astPointSet( 2, npix, "", status ); - pset2 = astPointSet( 2, nwcs, "", status ); - ptr1 = astGetPoints( pset1 ); - ptr2 = astGetPoints( pset2 ); - w1 = astMalloc( sizeof( double )*(size_t) nwcs ); - if( astOK ) { - for( i = 0; i < npix; i++ ) { - ptr1[ i ][ 0 ] = 1.0; - ptr1[ i ][ 1 ] = 1000.0; - } - (void) astTransform( map, pset1, 1, pset2 ); - -/* If the two wcs positions have equal longitude and latitude values, - assume that the output longitude and latitude axes are assigned - constant values by the Mapping. */ - if( ptr2[ ilon ][ 0 ] == ptr2[ ilon ][ 1 ] && - ptr2[ ilon ][ 0 ] != AST__BAD && - ptr2[ ilat ][ 0 ] == ptr2[ ilat ][ 1 ] && - ptr2[ ilat ][ 0 ] != AST__BAD ) { - -/* Create a set of Mappings to return, including a WcsMap, which result in - these constant latitude and longitude values. We do this by creating a - FITS-WCS header and reading the FrameSet from it. Keywords which are not - important to the final mappings are given arbitrary values. */ - fc = astFitsChan( NULL, NULL, "", status ); - for( i = 0; i < nwcs; i++ ) { - sprintf( card, "CRPIX%d = 0", i + 1 ); - astPutFits( fc, card, 0 ); - sprintf( card, "CDELT%d = 0.0003", i + 1 ); - astPutFits( fc, card, 0 ); - if( i == ilon ) { - sprintf( card, "CTYPE%d = 'RA---TAN'", i + 1 ); - } else if( i == ilat ) { - sprintf( card, "CTYPE%d = 'DEC--TAN'", i + 1 ); - } else { - sprintf( card, "CTYPE%d = 'DUMMY'", i + 1 ); - } - astPutFits( fc, card, 0 ); - if( i == ilon ) { - sprintf( card, "CRVAL%d = %.*g", i + 1, AST__DBL_DIG, AST__DR2D*ptr2[ ilon ][ 0 ] ); - } else if( i == ilat ) { - sprintf( card, "CRVAL%d = %.*g", i + 1, AST__DBL_DIG, AST__DR2D*ptr2[ ilat ][ 0 ] ); - } else { - sprintf( card, "CRVAL%d = 0.0", i + 1 ); - } - astPutFits( fc, card, 0 ); - } - astClearCard( fc ); - tfs = astRead( fc ); - if( tfs ) { - -/* Use SplitMap to get the required Mapings from the FrameSet. */ - tmap2 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); - SplitMap( tmap2, astGetInvert( tmap2 ), 0, 1, &tmap1, map2, - map3, status ); - tmap1 = astAnnul( tmap1 ); - tmap2 = astAnnul( tmap2 ); - -/* Create a ShiftMap which subtract the constant longitude and latitude - values off the inputs. */ - for( i = 0; i < nwcs; i++ ) w1[ i ] = 0.0; - w1[ ilon ] = -ptr2[ ilon ][ 0 ]; - w1[ ilat ] = -ptr2[ ilat ][ 0 ]; - tmap1 = (AstMapping *) astShiftMap( nwcs, w1, "", status ); - -/* Compose this with the supplied Mapping. This results in the celestial - outputs being zero. This gives the required "map1". */ - *map1 = (AstMapping *) astCmpMap( map, tmap1, 1, "", status ); - -/* Indicate success.*/ - ret = 1; - -/* Free resources. */ - tmap1 = astAnnul( tmap1 ); - tfs = astAnnul( tfs ); - } - fc = astAnnul( fc ); - } - } - -/* Free resources */ - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - w1 = astFree( w1 ); - } - if( !ret ) { - if( *map1 ) *map1 = astAnnul( *map1 ); - if( *map2 ) *map2 = astAnnul( *map2 ); - if( *map3 ) *map3 = astAnnul( *map3 ); - } - return ret; -} - -static int SplitMap2( AstMapping *map, int invert, AstMapping **map1, - AstWcsMap **map2, AstMapping **map3, int *status ){ -/* -* Name: -* SplitMap2 - -* Purpose: -* Locate a WCS projection within a Mapping. - -* Type: -* Private function. - -* Synopsis: -* int SplitMap2( AstMapping *map, int invert, AstMapping **map1, -* AstWcsMap **map2, AstMapping **map3, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* If possible, the supplied Mapping is decomposed into three component -* mappings to be compounded in series. To be acceptable, the second of -* these three Mappings must be a WcsMap with a non-zero FITSProj value. -* If it is not possible to produce such a group of three Mappings, then a -* zero function value is returned, together with three NULL Mapping -* pointers. All the mappings before the WcsMap are compounded together -* and returned as "map1". The WcsMap itself is returned as "map2", and -* any remaining Mappings are compounded together and returned as "map3". -* -* The search algorithm allows for an arbitrary combination of series and -* parallel CmpMaps. - -* Parameters: -* map -* A pointer to the Mapping from pixel to physical coordinates. -* invert -* The value of the Invert attribute to use with "map" (the value -* returned by astGetInvert is not used). -* map1 -* A location at which to return a pointer to the Mapping from pixel -* to intermediate world coordinates. -* map2 -* A location at which to return a pointer to the Mapping from relative -* physical coordinates to native spherical coordinates. This will -* be a WcsMap, and it will have a non-zero FITSProj value. -* map3 -* A location at which to return a pointer to the Mapping from -* native spherical coordinates to physical coordinates. -* dep -* The address of an integer holding the current depth of recursion -* into this function. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* One if a suitable WcsMap was found, zero otherwise. - -* Notes: -* - The returned Mappings contain independant copies of the relevant -* components of the supplied Mapping and can be modified without -* changing the supplied Mapping. -* - NULL pointers will be returned for all Mappings if no WcsMap -* with anon-zero FITSProj value can be found in the supplied Mapping. -* - A pointer to a UnitMap will be returned for map1 if no mappings -* exist before the WcsMap. -* - A pointer to a UnitMap will be returned for map3 if no mappings -* exist after the WcsMap. -* - NULL pointers will be returned for all Mappings and a function -* value of zero will be returned if an error has occurred, or if this -* function should fail for any reason. -* - "*map1" and "*map3" may contain WcsMaps, but they will have zero -* values for their FITSProj values. -*/ - -/* Local Variables */ - AstMapping **map_list; /* Mapping array pointer */ - AstMapping *mapa; /* Pre-wcs Mapping */ - AstWcsMap *mapb; /* WcsMap */ - AstMapping *mapc; /* Post-wcs Mapping */ - AstMapping *temp; /* Intermediate Mapping */ - const char *class; /* Pointer to class of supplied Mapping */ - double pv; /* Projection parameter value */ - int *invert_list; /* Invert array pointer */ - int axis; /* No. of axes in whole Mapping */ - int axlat; /* Index of latitude axis */ - int axlon; /* Index of longitude axis */ - int haswcs; /* Was a usable inverted WcsMap found? */ - int imap; /* Index of current Mapping in list */ - int i; /* axis index */ - int m; /* Parameter index */ - int nax; /* No. of axes in Mapping */ - int nmap; /* Number of Mappings in the list */ - int ret; /* Was a non-linear Mapping found? */ - int wcsaxis; /* Index of first WcsMap axis */ - -/* Initialise */ - *map1 = NULL; - *map2 = NULL; - *map3 = NULL; - ret = 0; - -/* Check the global status. */ - if( !astOK ) return ret; - -/* Get the class of the Mapping. */ - class = astGetClass( map ); - -/* If the supplied Mapping is a CmpMap... */ - wcsaxis = -1; - if( !strcmp( class, "CmpMap" ) ){ - -/* Decompose the Mapping into a sequence of Mappings to be applied in - series and an associated list of Invert flags. */ - map_list = NULL; - invert_list = NULL; - nmap = 0; - astMapList( map, 1, invert, &nmap, &map_list, &invert_list ); - -/* If there is more than one Mapping, this must be a series CmpMap. */ - if( nmap > 1 && astOK ){ - -/* Initialise the returned pre-wcs Mapping to be a UnitMap. */ - if( invert == astGetInvert( map ) ){ - *map1 = (AstMapping *) astUnitMap( astGetNin( map ), "", status ); - } else { - *map1 = (AstMapping *) astUnitMap( astGetNout( map ), "", status ); - } - -/* Indicate we have not yet found a WcsMap. */ - ret = 0; - -/* Process each series Mapping. */ - for( imap = 0; imap < nmap; imap++ ){ - -/* If we have not yet found a WcsMap... */ - if( !ret ){ - -/* Search this Mapping for a WcsMap. */ - ret = SplitMap2( map_list[ imap ], invert_list[ imap ], &mapa, - map2, map3, status ); - -/* If no WcsMap was found, use the whole mapping as part of the - pre-wcs Mapping. */ - if( !ret ){ - mapa = astCopy( map_list[ imap ] ); - astSetInvert( mapa, invert_list[ imap ] ); - } - -/* Add the pre-wcs mapping to the cumulative pre-wcs CmpMap. */ - temp = (AstMapping *) astCmpMap( *map1, mapa, 1, "", status ); - *map1 = astAnnul( *map1 ); - mapa = astAnnul( mapa ); - *map1 = temp; - -/* If we have previously found a WcsMap, use the whole mapping - as part of the post-wcs mapping. */ - } else { - mapc = astCopy( map_list[ imap ] ); - astSetInvert( mapc, invert_list[ imap ] ); - temp = (AstMapping *) astCmpMap( *map3, mapc, 1, "", status ); - *map3 = astAnnul( *map3 ); - mapc = astAnnul( mapc ); - *map3 = temp; - } - } - -/* If there is only one Mapping, this must be a parallel CmpMap. */ - } else { - -/* Annul the Mapping pointer in the series list created above, and free the - dynamic arrays. */ - map_list[ 0 ] = astAnnul( map_list[ 0 ] ); - map_list = astFree( map_list ); - invert_list = astFree( invert_list ); - nmap = 0; - -/* Decompose the Mapping into a sequence of Mappings to be applied in - parallel and an associated list of Invert flags. */ - astMapList( map, 0, invert, &nmap, &map_list, &invert_list ); - -/* Process each parallel Mapping. */ - axis = 0; - for( imap = 0; imap < nmap && astOK; imap++ ){ - -/* See if this Mapping contains a usable WcsMap. Only do the search - if no such WcsMap has already been found, since only the first is usable. */ - if( !ret ) { - -/* Search this Mapping for a WcsMap. */ - haswcs = SplitMap2( map_list[ imap ], invert_list[ imap ], &mapa, - &mapb, &mapc, status ); - -/* Note if we have found a usable WcsMap, and its first axis index. */ - if( haswcs ){ - ret = 1; - wcsaxis = axis; - } - -/* If a WcsMap has already been found, the mapping cannot contain a - usable WcsMap. */ - } else { - haswcs = 0; - } - -/* If the Mapping did not contain a usable WcsMap, use the whole mapping as - part of the pre-wcs Mapping, and create a UnitMap as part of the post-wcs - mapping. */ - if( !haswcs ){ - mapa = astCopy( map_list[ imap ] ); - astSetInvert( mapa, invert_list[ imap ] ); - nax = astGetNout( mapa ); - mapc = (AstMapping *) astUnitMap( nax, "", status ); - } - -/* Increment the index of the first axis in the next Mapping. */ - axis += astGetNout( mapa ); - -/* Add the pre-wcs mapping in parallel with the cumulative pre-wcs CmpMap. */ - if( *map1 ){ - temp = (AstMapping *) astCmpMap( *map1, mapa, 0, "", status ); - *map1 = astAnnul( *map1 ); - mapa = astAnnul( mapa ); - *map1 = temp; - } else { - *map1 = mapa; - } - -/* Add the post-wcs mapping in parallel with the cumulative post-wcs CmpMap. */ - if( *map3 ){ - temp = (AstMapping *) astCmpMap( *map3, mapc, 0, "", status ); - *map3 = astAnnul( *map3 ); - mapc = astAnnul( mapc ); - *map3 = temp; - } else { - *map3 = mapc; - } - } - -/* If a usable WcsMap was found, create a new one which has all the same - properties, but with enough axes to join the pre and post wcs Mappings - together. Ensure the correct axes are used for longitude and latitude, - and copy the projection parameters. */ - if( ret ){ - axlat = astGetWcsAxis( mapb, 1 ); - axlon = astGetWcsAxis( mapb, 0 ); - *map2 = astWcsMap( axis, astGetWcsType( mapb ), - axlon + wcsaxis + 1, - axlat + wcsaxis + 1, "", status ); - for( i = 0; i < astGetNin( mapb ); i++ ){ - for( m = 0; m < WCSLIB_MXPAR; m++ ){ - if( astTestPV( mapb, i, m ) ) { - pv = astGetPV( mapb, i, m ); - if( pv != AST__BAD ) astSetPV( *map2, i + wcsaxis, m, pv ); - } - } - } - astInvert( *map2 ); - mapb = astAnnul( mapb ); - } - } - -/* Loop to annul all the Mapping pointers in the list. */ - for ( imap = 0; imap < nmap; imap++ ) map_list[ imap ] = astAnnul( map_list[ imap ] ); - -/* Free the dynamic arrays. */ - map_list = astFree( map_list ); - invert_list = astFree( invert_list ); - -/* If the supplied Mapping is not a CmpMap, see if it is a WcsMap with a - non-zero FITSProj value. If so, take a copy and set its invert attribute - correctly. Also create UnitMaps for the pre and post wcs mappings. */ - } else if( astOK && !strcmp( class, "WcsMap" ) && astGetFITSProj( map ) ){ - ret = 1; - nax = astGetNin( map ); - *map1 = (AstMapping *) astUnitMap( nax, "", status ); - *map2 = astCopy( map ); - astSetInvert( *map2, invert ); - *map3 = (AstMapping *) astUnitMap( nax, "", status ); - } - -/* If an error has occurred, or if no suitable WcsMap was found, annul any - Mappings. */ - if( !astOK || !ret ){ - ret = 0; - if( *map1 ) *map1 = astAnnul( *map1 ); - if( *map2 ) *map2 = astAnnul( *map2 ); - if( *map3 ) *map3 = astAnnul( *map3 ); - } - -/* Return the answer. */ - return ret; -} - -static int SplitMat( int naxis, double *matrix, double *cdelt, int *status ){ -/* -* Name: -* SplitMat - -* Purpose: -* Factorises a single "CD"-style matrix into a diagonal CDELT matrix -* and a "PC"-style matrix. - -* Type: -* Private function. - -* Synopsis: -* int SplitMat( int naxis, double *matrix, double *cdelt, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function splits up the supplied CD matrix into separate PC and -* CDELT matrices. The product of the returned matrices (CDELT.PC) -* equals the supplied CD matrix. The CDELT values are chosen so that -* the corresponding row of the PC matrix represents a unit vector. -* The signs of the CDELT values are chosen so that the diagonal terms -* of the PC matrix are all positive. -* - -* Parameters: -* naxis -* The number of axes. -* matrix -* A pointer to an array of naxis*naxis elements. On entry this holds -* the "CD" matrix. On exit, it is modified to represent the "PC" -* matrix. -* cdelt -* A pointer to an array of naxis elements. On exit this holds the CDELT -* values for each axis (i.e. the diagonal terms of the CDELT matrix). -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero is returned if any bad values are found in the supplied -* matrix, or if an error has already occurred. One is returned otherwise. -*/ - -/* Local Variables: */ - int i; - int j; - int ok; - double *a; - int dineg; - double s2; - double cdlt; - -/* Check the inherited status. */ - if( !astOK ) return 0; - -/* Assume success. */ - ok = 1; - -/* Loop round every row in the matrix. Get a pointer to the first element - in the row. */ - for( i = 0; i < naxis; i++ ){ - a = matrix + i*naxis; - -/* Note the sign of the diagonal term (i.e. the i'th element) of this row. */ - dineg = ( a[ i ] < 0.0 ); - -/* Get the magnitude of the vector represented by this row. This is the - CDELT value for the row. BAD values cause the whole function to return. */ - s2 = 0.0; - for( j = 0; j < naxis; j++ ){ - if( *a == AST__BAD ) { - ok = 0; - break; - } - s2 += (*a)*(*a); - a++; - } - if( !ok ) break; - cdlt = sqrt( astMAX( 0.0, s2 ) ); - -/* If the diagonal term for this row of the matrix is negative, make - the CDELT value negative instead. This means that the diagonal term in - the final PC matrix will be positive. */ - if( dineg ) cdlt = -cdlt; - -/* Store the CDELT value. */ - cdelt[ i ] = cdlt; - -/* The row of the PC matrix is obtained by dividing the original row by - the CDELT value. Set to zero any PC values which are less than 1.0E-7 - (such values may be produced by rounding errors). */ - a = matrix + i*naxis; - for( j = 0; j < naxis; j++ ) { - if( cdlt != 0.0 ){ - *a /= cdlt; - if( fabs( *a ) < 1.E-7 ) *a = 0.0; - } else { - *a = 0.0; - } - a++; - } - } - return ok; -} -static void TableSource( AstFitsChan *this, - void (* tabsource)( AstFitsChan *, const char *, - int, int, int * ), - int *status ){ - -/* -*++ -* Name: -c astTableSource -f AST_TABLESOURCE - -* Purpose: -c Register a source function for accessing tables in FITS files. -f Register a source routine for accessing tables in FITS files. - -* Type: -* Public function. - -* Synopsis: -c #include "fitschan.h" -c void astTableSource( AstFitsChan *this, -c void (* tabsource)( AstFitsChan *, const char *, -c int, int, int * ) ) -f CALL AST_TABLESOURCE( THIS, TABSOURCE, STATUS ) - -* Class Membership: -* FitsChan member function. - -* Description: -c This function can be used to register a call-back function -f This routine can be used to register a call-back routine -* with a FitsChan. The registered -c function -f routine -* is called when-ever the FitsChan needs to read information from a -* binary table contained within a FITS file. This occurs if the -c astRead -f AST_READ -* function is invoked to read a FrameSet from a set of FITS headers -* that use the "-TAB" algorithm to describe one or more axes. Such -* axes use a FITS binary table to store a look-up table of axis values. -* The FitsChan will fail to read such axes unless the "TabOK" attribute -* is set to a non-zero positive integer value. The table containing the -* axis values must be made available to the FitsChan either by storing -* the table contents in the FitsChan (using -c astPutTables or astPutTable) prior to invoking astRead -f AST_PUTTABLES or AST_PUTTABLE) prior to invoking AST_READ -* or by registering a call-back -c function using astTableSource. -f routine using AST_TABLESOURCE. -* The first method is possibly simpler, but requires that the name of -* the extension containing the table be known in advance. Since the -* table name is embedded in the FITS headers, the name is often not -* known in advance. If a call-back is registered, the FitsChan will -* determine the name of the required table and invoke the call-back -c function -f routine -* to supply the table at the point where it is needed (i.e. within -c the astRead method). -f the AST_READ method). - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c tabsource -f TABSOURCE = SUBROUTINE (Given) -c Pointer to the table source function to use. -f The table source routine to use. -* It takes five arguments - the first is a pointer to the -* FitsChan, the second is a string holding the name of the -* FITS extension containing the required binary table ("EXTNAME"), -* the third is the integer FITS "EXTVER" header value for the -* required extension, the fourth is the integer FITS "EXTLEVEL" -* header value for the required extension, and the fifth is -c a pointer to -* the inherited integer status value. -* -* The call-back should read the entire contents (header and data) -* of the binary table in the named extension of the external FITS -* file, storing the contents in a newly created FitsTable object. It -* should then store this FitsTable in the FitsChan using the -c astPutTables or astPutTable -f AST_PUTTABLES or AST_PUTTABLE -* method, and finally annull its local copy of the FitsTable pointer. -* If the table cannot be read for any reason, or if any other -* error occurs, it should return a non-zero integer for the final -* (third) argument. -* -c If "tabsource" is NULL, -f If TABSOURCE is AST_NULL, -* any registered call-back function will be removed. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -c - Application code can pass arbitrary data (such as file -c descriptors, etc) to the table source function using the -c astPutChannelData function. The source function should use -c the astChannelData macro to retrieve this data. -f - The name of the routine supplied for the TABSOURCE -f argument should appear in an EXTERNAL statement in the Fortran -f routine which invokes AST_TABLESOURCE. However, this is not generally -f necessary for the null routine AST_NULL (so long as the AST_PAR -f include file has been used). -f - Note that the null routine AST_NULL (one underscore) is -f different to AST__NULL (two underscores), which is the null Object -f pointer. -*-- -*/ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Register the supplied source function, using the wrapper function - appropriate for calling C table source functions. */ - astSetTableSource( this, (void (*)( void )) tabsource, TabSourceWrap ); -} - -static AstMapping *TabMapping( AstFitsChan *this, FitsStore *store, char s, - int **tabaxis, const char *method, - const char *class, int *status ) { - -/* -* Name: -* TabMapping - -* Purpose: -* Create a Mapping that performs any -TAB look-ups for all WCS axes. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstMapping *TabMapping( AstFitsChan *this, FitsStore *store, char s, -* int **tabaxis, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function returns a Mapping that has "nwcs" inputs and outputs, -* where "nwcs" is the number of FITS WCS axes defined in the supplied -* FitsStore. The inputs and outputs are in the same order as the -* CTYPEi keywords in the FitsStore. The forward transformation of the -* Mapping converts positions from the axes defined by the CRVALi keywords -* to the WCS axes. This transformation will be a UnitMap except for -* any axes that are described using the "-TAB" algorithm. For "-TAB" -* axes, the transformation will implement the relevant coordinate -* look-up function. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore structure holding the values to use for -* the WCS keywords. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* tabaxis -* Address of a location at which to store a pointer to an array of -* flags, one for each output of the returned Mapping. A flag will -* be non-zero if the corresponding output of the returned Mapping -* corresponds to a -TAB axis. A NULL pointer is returned if the -* returned Mapping is NULL. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to a Mapping. A NULL pointer is returned if the FitsChan does -* not support the -TAB algorithm (i.e. if the value of the TabOK -* attribute is zero or negative), or if no axes use the "-TAB" algorithm. -*/ - -/* Local Variables: */ - AstFitsTable *table; - AstKeyMap *used_tables; - AstMapping *tmap1; - AstMapping *tmap2; - AstMapping *indexmap; - AstMapping *tmap0; - AstMapping *ret; - AstPermMap *pm; - char name[21]; - const char *indexcol; - const char *extname; - const char *cval; - const char *ctype; - const char *coordscol; - double dval; - int *marray; - int *permin; - int *permout; - int extlevel; - int extver; - int iaxis; - int iiaxis; - int ikey; - int interp; - int ival; - int maxis; - int mdim; - int nkey; - int nperm; - int unit; - int wcsaxes; - -/* Initialise */ - ret = NULL; - *tabaxis = NULL; - extname = NULL; - tmap0 = NULL; - tmap2 = NULL; - -/* Check the global status. */ - if( !astOK ) return ret; - -/* Obtain the number of physical axes in the header. If the WCSAXES header - was specified, use it. Otherwise assume it is the same as the number - of pixel axes. */ - dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); - if( dval != AST__BAD ) { - wcsaxes = (int) dval + 0.5; - } else { - wcsaxes = store->naxis; - } - -/* If the FitsChan does not support the -TAB algorithm, return a NULL - pointer. */ - if( astGetTabOK( this ) > 0 ) { - -/* Create a KeyMap to hold a list of the used extension names. */ - used_tables = astKeyMap( " ", status ); - -/* Allocate memory to indicate if each WCS axis is described by a -TAB - algorithm or not. Initialiss it to zero. */ - *tabaxis = astCalloc( wcsaxes, sizeof( int ) ); - -/* Allocate memory to hold the FITS-WCS axis index corresponding to each - input of the "tmap0" Mapping. Indicate that as yet, not values are - stored in this array. Also allocate memory for the inverse of this - permutation array. */ - permout = astMalloc( wcsaxes*sizeof( int ) ); - permin = astMalloc( wcsaxes*sizeof( int ) ); - nperm = 0; - if( astOK ) { - -/* Initialise the permutation arrays. */ - for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) { - permout[ iaxis ] = permin[ iaxis ] = -1; - } - -/* Otherwise, loop round all FITS WCS axis indices present in the FitsStore. */ - for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) { - -/* If the current FITS WCS axis is already included in the returned - Mapping, skip it. This will be the case if the axis uses the same - coordinate array as an earlier axis since all FITS WCS axes associated - with a coordinate array are processed together. */ - if( permin[ iaxis ] == -1 ) { - -/* See if this WCS axis uses the -TAB algorithm. */ - ctype = GetItemC( &(store->ctype), iaxis, 0, s, NULL, method, - class, status ); - if( ctype && strlen(ctype) > 4 && !strncmp( ctype + 4, "-TAB", 4 ) ) { - -/* Get the name of the FITS binary table extension holding the coordinate - info. No default, so report an error if not present. */ - sprintf( name, "PS%d_0%c", iaxis + 1, s ); - extname = GetItemC( &(store->ps), iaxis, 0, s, name, method, - class, status ); - -/* Get the extension version and level. */ - dval = GetItem( &(store->pv), iaxis, 1, s, NULL, method, - class, status ); - extver = ( dval != AST__BAD ) ? (int) dval : 1; - dval = GetItem( &(store->pv), iaxis, 2, s, NULL, method, - class, status ); - extlevel = ( dval != AST__BAD ) ? (int) dval : 1; - -/* Get the FITS binary table. This will invoke any supplied table source - function, and put a copy of the table into the FitsChan structure. - Report an error if the table can not be obtained. */ - table = GetNamedTable( this, extname, extver, extlevel, 1, - method, status ); - -/* Add this extension name to a list of used extensions. */ - astMapPut0I( used_tables, extname, 1, NULL ); - -/* Get the name of the table column containing the main coords array. No - default so report error if not present. Report an error if the column - is not present in the table. */ - sprintf( name, "PS%d_1%c", iaxis + 1, s ); - coordscol = GetItemC( &(store->ps), iaxis, 1, s, name, method, - class, status ); - if( !astHasColumn( table, coordscol ) && astOK ) { - astError( AST__BADTAB, "%s(%s): Unable to find the " - "coordinate array for FITS-WCS axis %d (type %s): " - "column '%s' cannot be found in table '%s'.", status, - method, class, iaxis + 1, ctype, coordscol, extname ); - } - -/* Get the number of dimensions spanned by the coordinate array. Report - an error if the coordinate array has only one axis (FITS-WCS paper III - requires it to have at leats two axes). */ - mdim = astGetColumnNdim( table, coordscol ); - if( mdim == 1 && astOK ) { - astError( AST__BADTAB, "%s(%s): Unable to use the " - "coordinate array for FITS-WCS axis %d (type %s): " - "column '%s' in table '%s' has one axis but at " - "least two are required.", status, method, class, - iaxis + 1, ctype, coordscol, extname ); - } - -/* Allocate memory to hold the FITS-WCS axis corresponding to each dimension - of the coordinate array. Initialise it to hold -1 (i.e. "no matching - FITS-WCS axis yet found") at every element. */ - marray = astMalloc( mdim*sizeof( int ) ); - if( astOK ) { - for( maxis = 0; maxis < mdim; maxis++ ) { - marray[ maxis ] = -1; - } - -/* Loop round each dimension of the coordinate array, storing the index - of the corresponding FITS-WCS axis in "marray". We omit the first axis - (axis 0) since FITS-WCS Paper III defines it is a "conventional" axis - used to enumerate the planes of coordinate values. */ - for( maxis = 1; maxis < mdim && astOK ; maxis++ ) { - -/* Each axis of the coordinate array (except axis 0) must have one, and only - one, corresponding FITS-WCS axis. Check each FITS-WCS axis to find one - that uses the same table and column as the "iaxis" axis, and which - corresponds to axis "maxis" of the coordinate array. */ - for( iiaxis = 0; iiaxis < wcsaxes; iiaxis++ ) { - cval = GetItemC( &(store->ps), iiaxis, 0, s, NULL, - method, class, status ); - if( cval && !strcmp( cval, extname ) ) { - cval= GetItemC( &(store->ps), iiaxis, 1, s, NULL, - method, class, status ); - if( cval && !strcmp( cval, coordscol ) ) { - dval = GetItem( &(store->pv), iiaxis, 3, s, - NULL, method, class, status ); - if( dval != AST__BAD ) { - ival = (int)( dval + 0.5 ); - } else { - ival = 1; - } - if( ival == maxis ) { - -/* Arrive here if the "iiaxis" FITS-WCS axis uses the same table and column - as "iaxis", and corresponds to the "maxis" axis in the coordinate - array. If this is the first matching FITS-WCS axis, store its index. */ - if( marray[ maxis ] == -1 ) { - marray[ maxis ] = iiaxis; - -/* If a matching FITS-WCS axis has already been found, report an error. */ - } else if( astOK ) { - astError( AST__BADTAB, "%s(%s): Unable to use " - "the coordinate array for FITS-WCS " - "axis %d (type %s): more than one " - "intermediate WCS axis is mapped onto " - " dimension %d of the coordinate " - "array in column '%s' of table '%s'.", - status, method, class, iaxis + 1, - ctype, maxis, coordscol, extname ); - } - } - } - } - } - } - -/* Check that every dimension of the coordinate array (except the first) has - a corresponding FITS-WCS axis. */ - for( maxis = 1; maxis < mdim && astOK ; maxis++ ) { - if( marray[ maxis ] == -1 ) { - astError( AST__BADTAB, "%s(%s): Unable to use the " - "coordinate array for FITS-WCS axis %d (type " - "%s): no intermediate WCS axis is mapped onto " - " dimension %d of the coordinate array in column " - " '%s' of table '%s'.", status, method, class, - iaxis + 1, ctype, maxis, coordscol, extname ); - } - } - -/* Now we know which FITS-WCS axis corresponds to each dimension of the - coordinate array. We now need to form a parallel CmpMap (compound Mapping) - by gathering together the indexing vectors for each dimension of the - coordinates array. Each indexing vector is represented by an inverted - 1D LutMap - dimensions that do not have an indexing vector are - represented using a UnitMap. */ - indexmap = NULL; - unit = 1; - for( maxis = 1; maxis < mdim && astOK ; maxis++ ) { - -/* Get the name of the column containing the index array. Defaults is to - use a unit index, so do not report an error if not present. */ - indexcol = GetItemC( &(store->ps), marray[ maxis ], 2, - s, NULL, method, class, status ); - -/* If the table contains an index vector, create a LutMap from it, then - invert it. */ - if( indexcol ) { - tmap1 = MakeColumnMap( table, indexcol, 1, 0, - method, class, status ); - astInvert( tmap1 ); - unit = 0; - -/* If the table does not contain an index vector, use a UnitMap. */ - } else { - tmap1 = (AstMapping *) astUnitMap( 1, " ", status ); - } - -/* Combine the index Mapping for this dimension in parallel with the - Mapping for all earlier dimensions. */ - if( indexmap ) { - tmap2 = (AstMapping *) astCmpMap( indexmap, tmap1, - 0, " ", status ); - indexmap = astAnnul( indexmap ); - tmap1 = astAnnul( tmap1 ); - indexmap = tmap2; - } else { - indexmap = tmap1; - } - } - -/* Get the interpolation method to use for the main coordinate array. - This is an extension to the published -TAB algorithm in which the - QVi_4a keyword is assumed to hold zero for linear interpolation (the - default) and non-zero for nearest neighbour interpolation. The QVi_4a - keyword will be translated to PVi_4a by the SpecTrans function. */ - dval = GetItem( &(store->pv), iaxis, 4, s, - NULL, method, class, status ); - if( dval != AST__BAD ) { - interp = (int)( dval + 0.5 ); - } else { - interp = 0; - } - -/* Make a Mapping from the main coordinate array, and then if required - append it in series to the end of the index Mapping created above. */ - tmap1 = MakeColumnMap( table, coordscol, 0, interp, - method, class, status ); - if( ! unit ) { - tmap2 = (AstMapping *) astCmpMap( indexmap, tmap1, 1, - " ", status ); - } else { - tmap2 = astClone( tmap1 ); - } - indexmap = astAnnul( indexmap ); - tmap1 = astAnnul( tmap1 ); - -/* Extend the array that holds the zero-based FITS-WCS axis index - corresponding to each input of the extended "tmap0" mapping. Also create - the inverse permutation (i.e. zero-based "tmap0" input indexed by - zero-based FITS-WCS axis index). */ - for( maxis = 1; maxis < mdim; maxis++ ) { - permout[ nperm ] = marray[ maxis ]; - permin[ marray[ maxis ] ] = nperm++; - } - -/* Free resources. */ - marray = astFree( marray ); - } - -/* Annul the table pointer. */ - table = astAnnul( table ); - -/* Clear the CTYPE algorithm code to indicate that the axis should be - considered to be linear from now on. This means that the following - functions will create a Mapping from pixel to psi (the system in which - the CRVAL values are defined when using -TAB). The psi axes will then - be mapping into the CS axes using the Mappign returned by this function. */ - strncpy( name, ctype, 4 ); - strcpy( name + 4, ctype + 8 ); - SetItemC( &(store->ctype), iaxis, 0, s, name, status ); - -/* Set the returned flag for this axis. */ - (*tabaxis)[ iaxis ] = 1; - -/* If the FITS WCS axis "iaxis" does not use a -TAB algorithm, describe - it in the returned Mapping using a 1D UnitMap. */ - } else { - tmap2 = (AstMapping *) astUnitMap( 1, " ", status ); - -/* Extend the array that holds the zero-based FITS-WCS axis index - corresponding to each input of the extended "tmap0" mapping. Also create - the inverse permutation (i.e. zero-based "tmap0" input indexed by - zero-based FITS-WCS axis index). */ - permout[ nperm ] = iaxis; - permin[ iaxis ] = nperm++; - } - -/* Append the Mapping describing the FITS WCS axis "iaxis" in parallel to any - Mappings created for earlier "iaxis" axes. */ - if( tmap0 ) { - tmap1 = (AstMapping *) astCmpMap( tmap0, tmap2, 0, " ", status ); - tmap0 = astAnnul( tmap0 ); - tmap2 = astAnnul( tmap2 ); - tmap0 = tmap1; - } else { - tmap0 = tmap2; - } - } - } - -/* If no -TAB axes were found, just return a NULL pointer. */ - if( extname && astOK ) { - -/* Do a sanity check on the permutation arrays. */ - for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) { - if( permin[ iaxis ] < 0 || permin[ iaxis ] >= wcsaxes || - permout[ permin[ iaxis ] ] != iaxis ) { - astError( AST__INTER, "%s(%s): Invalid permutation " - "arrays in function TabMapping (internal AST " - "progranmming error).", status, method, class ); - break; - } - } - -/* Sandwich the "tmap0" Mapping in series between two PermMaps to create a - Mapping in which the inputs and outputs correspond to FITS WCS axis - numbering. */ - pm = astPermMap( wcsaxes, permin, wcsaxes, permout, NULL, " ", - status ); - tmap1 = (AstMapping *) astCmpMap( pm, tmap0, 1, " ", status ); - astInvert( pm ); - tmap2 = (AstMapping *) astCmpMap( tmap1, pm, 1, " ", status ); - pm = astAnnul( pm ); - tmap1 = astAnnul( tmap1 ); - -/* Simplify the returned Mapping. */ - ret = astSimplify( tmap2 ); - tmap2 = astAnnul( tmap2 ); - } - -/* Free remaining resources */ - tmap0 = astAnnul( tmap0 ); - } - permout = astFree( permout ); - permin = astFree( permin ); - -/* Remove all used tables from the FitsChan now that they have been used. */ - nkey = astMapSize( used_tables ); - for( ikey = 0; ikey < nkey; ikey++ ) { - astRemoveTables( this, astMapKey( used_tables, ikey ) ); - } - -/* Delete the KeyMap holding the used table names. */ - used_tables = astAnnul( used_tables ); - -/* If we are not returning a Mapping, ensure we do not return any axis - flags either. */ - if( !ret ) *tabaxis = astFree( *tabaxis ); - } - -/* Return the result */ - return ret; -} -static void TabSourceWrap( void (*tabsource)( void ), - AstFitsChan *this, const char *extname, - int extver, int extlevel, int *status ){ - -/* -* Name: -* TabSourceWrap - -* Purpose: -* Wrapper function to invoke the C table source function. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void TabSourceWrap( void (*tabsource)( void ), -* AstFitsChan *this, const char *extname, -* int extver, int extlevel, int *status ) - -* Class Membership: -* Channel member function. - -* Description: -* This function invokes the table source function whose pointer is -* supplied in order to read a named FITS binary table from an external -* FITS file. - -* Parameters: -* tabsource -* Pointer to the C tab source function. -* this -* Pointer to the FitsChan. The reference count for the FitsChan is -* decremented by this function (this behaviour is imposed by -* restrictions in the equivalent Fortran wrapper function). -* extname -* Pointer to the string holding the name of the FITS extension -* from which a table is to be read. -* extver -* The integer "EXTVER" value for the required extension. -* extlevel -* The integer "EXTLEVEL" value for the required extension. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - AstFitsChan *this_id; - int lstat; - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get an external identifier for the FitsChan. Could use astClone here - to avoid this function anulling the supplied pointer, but the F77 wrapper - cannot use the protected version of astClone, so for consistency we do - not use it here either. */ - this_id = astMakeId( this ); - -/* Invoke the table source function (casting it to the C API first) to - read the table, and store it in the FitsChan. */ - ( *( void (*)( struct AstFitsChan *, const char *, int, int, int * ) )tabsource )( this_id, extname, extver, extlevel, &lstat ); - -/* Free the FitsChan identifier (this annuls the supplied "this" pointer). */ - this_id = astAnnulId( this_id ); - -/* Report an error if the source function failed. */ - if( !lstat ) { - astError( AST__NOTAB, "astRead(%s): The table source function failed to read " - "a binary table from extension %s in an external FITS file.", - status, astGetClass( this ), extname ); - } -} - -static double TDBConv( double mjd, int timescale, int fromTDB, - const char *method, const char *class, int *status ){ -/* -* Name: -* TDBConv - -* Purpose: -* Convert an MJD between the TDB time scale and another timescale. - -* Type: -* Private function. - -* Synopsis: -* double TDBConv( double mjd, int timescale, int fromTDB, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function converts the supplied mjd value to or from the TDB -* timescale. - -* Parameters: -* mjd -* The input MJD value. -* timescale -* The other timescale. -* fromTDB -* Indicates the direction of the required conversion. If non-zero, -* the supplied "mjd" value should be in the TDB timescale, and the -* returned value will be in the timescale specified by "timescale". -* If zero, the supplied "mjd" value should be in the timescale -* specified by "timescale", and the returned value will be in the -* TDB timescale. -* method -* The calling method. Used only in error messages. -* class -* The object class. Used only in error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The converted MJD value, or AST__BAD if an error occurs. -*/ - -/* Local Variables: */ - AstFrameSet *fs; /* Mapping from supplied timescale to TDB */ - double ret; /* The returned value */ - -/* Initialise */ - ret = AST__BAD; - -/* Check inherited status and supplied TDB value. */ - if( !astOK || mjd == AST__BAD ) return ret; - -/* Return the supplied value if no conversion is needed. */ - if( timescale == AST__TDB ) { - ret = mjd; - -/* Otherwise, do the conversion. */ - } else { - -/* Lock the timeframes for use by the current thread, waiting if they are - currently locked by another thread. */ - astManageLock( timeframe, AST__LOCK, 1, NULL ); - astManageLock( tdbframe, AST__LOCK, 1, NULL ); - -/* Set the required timescale. */ - astSetTimeScale( timeframe, timescale ); - -/* Get the Mapping between the two timescales, and use it to convert the - suipplied value. */ - fs = astConvert( tdbframe, timeframe, "" ); - astTran1( fs, 1, &mjd, fromTDB, &ret ); - fs = astAnnul( fs ); - -/* Unlock the timeframes. */ - astManageLock( timeframe, AST__UNLOCK, 1, NULL ); - astManageLock( tdbframe, AST__UNLOCK, 1, NULL ); - } - -/* Return the result */ - return ret; -} - -static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { -/* -* Name: -* TestAttrib - -* Purpose: -* Test if a specified attribute value is set for a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int TestAttrib( AstObject *this, const char *attrib, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the astTestAttrib protected -* method inherited from the Channel class). - -* Description: -* This function returns a boolean result (0 or 1) to indicate whether -* a value has been set for one of a FitsChan's attributes. - -* Parameters: -* this -* Pointer to the FitsChan. -* attrib -* Pointer to a null-terminated string specifying the attribute -* name. This should be in lower case with no surrounding white -* space. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* One if a value has been set, otherwise zero. - -* 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: */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - int result; /* Result value to return */ - -/* Initialise. */ - result = 0; - -/* Check the global error status. */ - if ( !astOK ) return result; - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_object; - -/* Card. */ -/* ----- */ - if ( !strcmp( attrib, "card" ) ) { - result = astTestCard( this ); - -/* Encoding. */ -/* --------- */ - } else if ( !strcmp( attrib, "encoding" ) ) { - result = astTestEncoding( this ); - -/* FitsAxisOrder. */ -/* -------------- */ - } else if ( !strcmp( attrib, "fitsaxisorder" ) ) { - result = astTestFitsAxisOrder( this ); - -/* FitsDigits. */ -/* ----------- */ - } else if ( !strcmp( attrib, "fitsdigits" ) ) { - result = astTestFitsDigits( this ); - -/* DefB1950. */ -/* --------- */ - } else if ( !strcmp( attrib, "defb1950" ) ) { - result = astTestDefB1950( this ); - -/* TabOK. */ -/* ------ */ - } else if ( !strcmp( attrib, "tabok" ) ) { - result = astTestTabOK( this ); - -/* CDMatrix. */ -/* --------- */ - } else if ( !strcmp( attrib, "cdmatrix" ) ) { - result = astTestCDMatrix( this ); - -/* CarLin. */ -/* --------- */ - } else if ( !strcmp( attrib, "carlin" ) ) { - result = astTestCarLin( this ); - -/* SipReplace. */ -/* ----------- */ - } else if ( !strcmp( attrib, "sipreplace" ) ) { - result = astTestSipReplace( this ); - -/* FitsTol. */ -/* -------- */ - } else if ( !strcmp( attrib, "fitstol" ) ) { - result = astTestFitsTol( this ); - -/* PolyTan */ -/* ------- */ - } else if ( !strcmp( attrib, "polytan" ) ) { - result = astTestPolyTan( this ); - -/* SipOK */ -/* ----- */ - } else if ( !strcmp( attrib, "sipok" ) ) { - result = astTestSipOK( this ); - -/* Iwc. */ -/* ---- */ - } else if ( !strcmp( attrib, "iwc" ) ) { - result = astTestIwc( this ); - -/* Clean. */ -/* ------ */ - } else if ( !strcmp( attrib, "clean" ) ) { - result = astTestClean( this ); - -/* Warnings. */ -/* -------- */ - } else if ( !strcmp( attrib, "warnings" ) ) { - result = astTestWarnings( this ); - -/* If the name is not recognised, test if it matches any of the - read-only attributes of this class. If it does, then return - zero. */ - } else if ( !strcmp( attrib, "ncard" ) || - !strcmp( attrib, "nkey" ) || - !strcmp( attrib, "cardtype" ) || - !strcmp( attrib, "cardcomm" ) || - !strcmp( attrib, "cardname" ) || - !strcmp( attrib, "allwarnings" ) ){ - result = 0; - -/* If the attribute is still not recognised, pass it on to the parent - method for further interpretation. */ - } else { - result = (*parent_testattrib)( this_object, attrib, status ); - } - -/* Return the result, */ - return result; -} - -static int TestCard( AstFitsChan *this, int *status ){ - -/* -*+ -* Name: -* astTestCard - -* Purpose: -* Test the Card attribute. - -* Type: -* Protected virtual function. - -* Synopsis: -* #include "fitschan.h" -* int astTestCard( AstFitsChan *this ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function tests the Card attribute for the supplied FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. - -* Returned Value: -* If the Card attribute has its "cleared" value (i.e. if the first card -* in the FitsChan will be the next one to be read), then zero is returned, -* otherwise 1 is returned. -*- -*/ - -/* Local Variables: */ - int card; /* The original value of Card */ - int ret; /* The returned flag */ - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Get the current value of Card. */ - card = astGetCard( this ); - -/* Temporarily clear Card. */ - astClearCard( this ); - -/* See if the original Card is equal to the cleared card, and set the - returned flag appropriately. Re-instate the original value of card is - required.*/ - if( astGetCard( this ) == card ) { - ret = 0; - } else { - astSetCard( this, card ); - ret = 1; - } - -/* Return the flag. */ - return ret; -} - -static int TestFits( AstFitsChan *this, const char *name, int *there, - int *status ){ - -/* -*++ -* Name: -c astTestFits -f AST_TESTFITS - -* Purpose: -* See if a named keyword has a defined value in a FitsChan. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" - -c int astTestFits( AstFitsChan *this, const char *name, int *there ) -f RESULT = AST_TESTFITS( THIS, NAME, THERE, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -* This function serches for a named keyword in a FitsChan. If found, -* and if the keyword has a value associated with it, a -c non-zero -f .TRUE. -* value is returned. If the keyword is not found, or if it does not -* have an associated value, a -c zero -f .FALSE. -* value is returned. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -c name -f NAME = CHARACTER * ( * ) (Given) -c Pointer to a null-terminated character string -f A character string -* containing the FITS keyword name. This may be a complete FITS -* header card, in which case the keyword to use is extracted from -* it. No more than 80 characters are read from this string. If -c NULL -f a single dot '.' -* is supplied, the current card is tested. -c there -f THERE = LOGICAL (Returned) -c Pointer to an integer which will be returned holding a non-zero -c value if the keyword was found in the header, and zero otherwise. -f A value of .TRUE. will be returned if the keyword was found in the -f header, and .FALSE. otherwise. -* This parameter allows a distinction to be made between the case -* where a keyword is not present, and the case where a keyword is -* present but has no associated value. -c A NULL pointer may be supplied if this information is not -c required. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Returned Value: -c astTestFits() -f AST_TESTFITS = LOGICAL -* A value of zero -f .FALSE. -* is returned if the keyword was not found in the FitsChan or has -* no associated value. Otherwise, a value of -c one -f .TRUE. -* is returned. - -* Notes: -* - The current card is left unchanged by this function. -* - The card following the current card is checked first. If this is -* not the required card, then the rest of the FitsChan is searched, -* starting with the first card added to the FitsChan. Therefore cards -* should be accessed in the order they are stored in the FitsChan (if -* possible) as this will minimise the time spent searching for cards. -* - An error will be reported if the keyword name does not conform -* to FITS requirements. -c - Zero -f - .FALSE. -* is returned as the function value if an error has already occurred, -* or if this function should fail for any reason. -*-- -*/ - -/* Local Variables: */ - const char *class; /* Object class */ - const char *method; /* Calling method */ - char *lcom; /* Supplied keyword comment */ - char *lname; /* Supplied keyword name */ - char *lvalue; /* Supplied keyword value */ - int icard; /* Current card index on entry */ - int ret; /* The returned value */ - int type; /* The card's type */ - -/* Initialise */ - if( there ) *there = 0; - -/* Check the global error status. */ - if ( !astOK ) return 0; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Store the calling method and object class. */ - method = "astTestFits"; - class = astGetClass( this ); - -/* Initialise the returned value. */ - ret = 0; - -/* Extract the keyword name from the supplied string. */ - if( name ) { - (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); - } else { - lname = NULL; - lvalue = NULL; - lcom = NULL; - } - -/* Store the current card index. */ - icard = astGetCard( this ); - -/* Attempt to find a card in the FitsChan refering to this keyword, - and make it the current card. Only proceed if a card was found. No - need to do the search if the value of the current card is required. */ - if( !lname || SearchCard( this, lname, method, class, status ) ){ - -/* Get the card type. */ - type = CardType( this, status ); - -/* Check the card exists. */ - if( type != AST__NOTYPE ) { - -/* If the cards data type is not undefined, return 1. */ - if( CardType( this, status ) != AST__UNDEF ) ret = 1; - -/* Indicate the card has been found. */ - if( there ) *there = 1; - } - } - -/* Re-instate the original current card index. */ - astSetCard( this, icard ); - -/* Release the memory used to hold keyword name, value and comment strings. */ - lname = (char *) astFree( (void *) lname ); - lvalue = (char *) astFree( (void *) lvalue ); - lcom = (char *) astFree( (void *) lcom ); - -/* Return the answer. */ - return ret; -} - -static void TidyOffsets( AstFrameSet *fset, int *status ) { -/* -* Name: -* TidyOffsets - -* Purpose: -* Remove un-needed offset coordinate Frames. - -* Type: -* Private function. - -* Synopsis: -* void TidyOffsets( AstFrameSet *fset, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FITS header stores offset sky coordinates as two alternaive axis -* descriptions - one giving the offset axes and one giving the absolute -* axes. But AST can hold both forms in a single SkyFrame. This function -* removes the FITS Frames describing offset axes from the FrameSet. -* The remaining absolute Frame is then used to describe both absolute -* and offset. - -* Parameters: -* fset -* A FrameSet holding the Frames read from a FITS-WCS Header. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - AstFrame *frm; - AstFrame *pfrm; - const char *dom; - const char *skyrefis; - int hasabs; - int hasoff; - int iax; - int icurr; - int icurr_is_offset; - int ifrm; - int nax; - int nfrm; - int pax; - int remove; - -/* Check the inherited status. */ - if( !astOK ) return; - -/* Note the original current Frame index. */ - icurr = astGetCurrent( fset ); - -/* Assume the current Frame is not an offset frame until proven - otherwise. */ - icurr_is_offset = 0; - -/* Does the FrameSet contain any Frames holding sky offsets? Such Frames - should have been given a Domain of SKY_OFFSETS within function - WcsSkyFrame. Loop round all Frames, checking each one. Also note if - the FrameSet contains any (absolute) SKY frames. Also set the SkyRefIs - attribute for any absolute SkyFrames that were marked with domains - SKY_POLE or SKY_OFFSET in WcsSkyFrame. */ - hasabs = 0; - hasoff = 0; - nfrm = astGetNframe( fset ); - for( ifrm = 1; ifrm <= nfrm; ifrm++ ){ - skyrefis = NULL; - frm = astGetFrame( fset, ifrm ); - nax = astGetNaxes( frm ); - for( iax = 0; iax < nax; iax++ ) { - astPrimaryFrame( frm, iax, &pfrm, &pax ); - if( IsASkyFrame( pfrm ) ) { - dom = astGetDomain( pfrm ); - if( dom ) { - if( !strcmp( dom, "SKY_OFFSETS" ) ){ - hasoff = 1; - if( ifrm == icurr ) icurr_is_offset = 1; - iax = nax; - } else if( !strcmp( dom, "SKY" ) ){ - hasabs = 1; - iax = nax; - } else if( !strcmp( dom, "SKY_POLE" ) ){ - hasabs = 1; - skyrefis = "POLE"; - iax = nax; - } else if( !strcmp( dom, "SKY_ORIGIN" ) ){ - hasabs = 1; - skyrefis = "ORIGIN"; - iax = nax; - } - } - } - pfrm = astAnnul( pfrm ); - } - frm = astAnnul( frm ); - - if( skyrefis ) { - astSetI( fset, "Current", ifrm); - astSetC( fset, "SkyRefIs", skyrefis ); - astSetI( fset, "Current", icurr ); - } - } - -/* If one or more absolute sky frames were found, then remove any offset - sky frames. Clear the Ident attribute (that holds the FITS-WCS alternate - axis description character) for any absoute Frames. */ - if( hasabs && hasoff ) { - - for( ifrm = nfrm; ifrm > 0; ifrm-- ) { - remove = 0; - frm = astGetFrame( fset, ifrm ); - nax = astGetNaxes( frm ); - for( iax = 0; iax < nax; iax++ ) { - astPrimaryFrame( frm, iax, &pfrm, &pax ); - if( IsASkyFrame( pfrm ) ) { - dom = astGetDomain( pfrm ); - if( dom ) { - if( !strcmp( dom, "SKY_OFFSETS" ) ){ - remove = 1; - iax = nax; - - } else if( !strcmp( dom, "SKY_POLE" ) || - !strcmp( dom, "SKY_ORIGIN" ) ){ - astClearIdent( frm ); - astClearDomain( pfrm ); - -/* If we will be deleting the original current Frame (because it is an - offset Frame), then mark the first absolute Frame as the new current - Frame. */ - if( icurr_is_offset ) { - astSetCurrent( fset, ifrm ); - icurr_is_offset = 0; - } - iax = nax; - } - } - } - pfrm = astAnnul( pfrm ); - } - frm = astAnnul( frm ); - - if( remove ) astRemoveFrame( fset, ifrm ); - } - } -} - -static AstTimeScaleType TimeSysToAst( AstFitsChan *this, const char *timesys, - const char *method, const char *class, int *status ){ -/* -* Name: -* TimeSysToAst - -* Purpose: -* Convert a FITS TIMESYS value to an AST TimeFrame timescale value. - -* Type: -* Private function. - -* Synopsis: -* AstTimeScaleType TimeSysToAst( AstFitsChan *this, const char *timesys, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function returns the value used by the AST TimeFrame class to -* represent the timescale specified by the "timesys" parameter, which -* should hold the value of a FITS TIMESYS keyword. The TIMESYS -* convention was introduced as part of the Y2K DATE-OBS changes, and -* is not currently part of the published FITS-WCS conventions. -* -* If the requested timescale is not supported by AST, then a warning is -* added to the FitsChan and a value of AST__UTC is returned (but no -* error is reported). - -* Parameters: -* this -* Pointer to the FitsChan. -* timesys -* Pointer to the string holding the TIMESYS value. A NULL pointer -* returns the default timescale of UTC. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The equivalent AstTimeScaleType value. -*/ - -/* Local Variables: */ - AstTimeScaleType result; /* The returned timescale */ - char buf[ 200 ]; /* Buffer for warning message */ - -/* Initialise */ - result = AST__UTC; - -/* Check the inherited status. */ - if( !astOK ) return result; - if( !timesys ) { - result = AST__UTC; - } else if( !strcmp( timesys, "UTC" ) ) { - result = AST__UTC; - } else if( !strcmp( timesys, "UT" ) ) { - result = AST__UTC; - Warn( this, "badval", "The original FITS header contained a value of UT " - "for keyword TIMESYS which is being interpreted as UTC.", method, - class, status ); - } else if( !strcmp( timesys, "TAI" ) ) { - result = AST__TAI; - } else if( !strcmp( timesys, "IAT" ) ) { - result = AST__TAI; - } else if( !strcmp( timesys, "ET" ) ) { - result = AST__TT; - Warn( this, "badval", "The original FITS header contained a value of ET " - "for keyword TIMESYS. TT will be used instead.", method, class, status ); - } else if( !strcmp( timesys, "TT" ) ) { - result = AST__TT; - } else if( !strcmp( timesys, "TDT" ) ) { - result = AST__TT; - } else if( !strcmp( timesys, "TDB" ) ) { - result = AST__TDB; - } else if( !strcmp( timesys, "TCG" ) ) { - result = AST__TCG; - } else if( !strcmp( timesys, "TCB" ) ) { - result = AST__TCB; - } else { - result = AST__UTC; - sprintf( buf, "The original FITS header contained a value of %s for " - "keyword TIMESYS. AST does not support this timescale so " - "UTC will be used instead.", timesys ); - Warn( this, "badval", buf, method, class, status ); - } - -/* Return the result */ - return result; -} - -static char *UnPreQuote( const char *string, int *status ) { -/* -* Name: -* UnPreQuote - -* Purpose: -* Reverse the pre-quoting of FITS character data. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* char *UnPreQuote( const char *string, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function reverses the effect of the PreQuote function on a -* string (apart from any loss of data due to truncation). It -* should be used to recover the original character data from the -* pre-quoted version of a string retrieved from a FITS character -* value associated with a keyword. - -* Parameters: -* string -* Pointer to a constant null-terminated string containing the -* pre-quoted character data. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Pointer to a dynamically allocated null-terminated string -* containing the un-quoted character data. The memory holding this -* string should be freed by the caller (using astFree) when no -* longer required. - -* Notes: -* - A NULL pointer value will be returned if this function is -* invoked wth the global error status set, or if it should fail -* for any reason. -*/ - -/* Local Variables: */ - char *result; /* Pointer value to return */ - int i1; /* Offset of first useful character */ - int i2; /* Offest of last useful character */ - -/* Check the global error status. */ - if ( !astOK ) return NULL; - -/* Initialise to use the first and last characters in the input - string. */ - i1 = 0; - i2 = strlen( string ) - 1; - -/* If the string contains at least 2 characters, check if the first - and last characters are double quotes ("). If so, adjust the - offsets to exclude them. */ - if ( ( i2 > i1 ) && - ( string[ i1 ] == '"' ) && ( string[ i2 ] == '"' ) ) { - i1++; - i2--; - } - -/* Make a dynamically allocated copy of the useful part of the - string. */ - result = astString( string + i1, i2 - i1 + 1 ); - -/* Return the answer. */ - return result; -} - -static int Use( AstFitsChan *this, int set, int helpful, int *status ) { - -/* -* Name: -* Use - -* Purpose: -* Decide whether to write a value to a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int Use( AstFitsChan *this, int set, int helpful, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* This function decides whether a value supplied by a class "Dump" -* function, via a call to one of the astWrite... protected -* methods, should actually be written to a FitsChan. -* -* This decision is based on the settings of the "set" and -* "helpful" flags supplied to the astWrite... method, plus the -* attribute settings of the FitsChan. - -* Parameters: -* this -* A pointer to the FitsChan. -* set -* The "set" flag supplied. -* helpful -* The "helpful" value supplied. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* One if the value should be written out, otherwise zero. - -* Notes: -* - A value of zero 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 full; /* Full attribute value */ - int result; /* Result value to be returned */ - -/* Check the global error status. */ - if ( !astOK ) return 0; - -/* If "set" is non-zero, then so is the result ("set" values must - always be written out). */ - result = ( set != 0 ); - -/* Otherwise, obtain the value of the FitsChan's Full attribute. */ - if ( !set ) { - full = astGetFull( this ); - -/* If Full is positive, display all values, if zero, display only - "helpful" values, if negative, display no (un-"set") values. */ - if ( astOK ) result = ( ( helpful && ( full > -1 ) ) || ( full > 0 ) ); - } - -/* Return the result. */ - return result; -} - -static int Ustrcmp( const char *a, const char *b, int *status ){ -/* -* Name: -* Ustrcmp - -* Purpose: -* A case blind version of strcmp. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int Ustrcmp( const char *a, const char *b, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns 0 if there are no differences between the two strings, and 1 -* otherwise. Comparisons are case blind. - -* Parameters: -* a -* Pointer to first string. -* b -* Pointer to second string. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero if the strings match, otherwise one. - -* Notes: -* - This function does not consider the sign of the difference between -* the two strings, whereas "strcmp" does. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - const char *aa; /* Pointer to next "a" character */ - const char *bb; /* Pointer to next "b" character */ - int ret; /* Returned value */ - -/* Initialise the returned value to indicate that the strings match. */ - ret = 0; - -/* Initialise pointers to the start of each string. */ - aa = a; - bb = b; - -/* Loop round each character. */ - while( 1 ){ - -/* We leave the loop if either of the strings has been exhausted. */ - if( !(*aa ) || !(*bb) ){ - -/* If one of the strings has not been exhausted, indicate that the - strings are different. */ - if( *aa || *bb ) ret = 1; - -/* Break out of the loop. */ - break; - -/* If neither string has been exhausted, convert the next characters to - upper case and compare them, incrementing the pointers to the next - characters at the same time. If they are different, break out of the - loop. */ - } else { - if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ - ret = 1; - break; - } - } - } - -/* Return the result. */ - return ret; -} - -static int Ustrncmp( const char *a, const char *b, size_t n, int *status ){ -/* -* Name: -* Ustrncmp - -* Purpose: -* A case blind version of strncmp. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int Ustrncmp( const char *a, const char *b, size_t n, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* Returns 0 if there are no differences between the first "n" -* characters of the two strings, and 1 otherwise. Comparisons are -* case blind. - -* Parameters: -* a -* Pointer to first string. -* b -* Pointer to second string. -* n -* The maximum number of characters to compare. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Zero if the strings match, otherwise one. - -* Notes: -* - This function does not consider the sign of the difference between -* the two strings, whereas "strncmp" does. -* - This function attempts to execute even if an error has occurred. -*/ - -/* Local Variables: */ - const char *aa; /* Pointer to next "a" character */ - const char *bb; /* Pointer to next "b" character */ - int i; /* Character index */ - int ret; /* Returned value */ - -/* Initialise the returned value to indicate that the strings match. */ - ret = 0; - -/* Initialise pointers to the start of each string. */ - aa = a; - bb = b; - -/* Compare up to "n" characters. */ - for( i = 0; i < (int) n; i++ ){ - -/* We leave the loop if either of the strings has been exhausted. */ - if( !(*aa ) || !(*bb) ){ - -/* If one of the strings has not been exhausted, indicate that the - strings are different. */ - if( *aa || *bb ) ret = 1; - -/* Break out of the loop. */ - break; - -/* If neither string has been exhausted, convert the next characters to - upper case and compare them, incrementing the pointers to the next - characters at the same time. If they are different, break out of the - loop. */ - } else { - if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ - ret = 1; - break; - } - } - } - -/* Return the result. */ - return ret; -} - -static void Warn( AstFitsChan *this, const char *condition, const char *text, - const char*method, const char *class, int *status ){ -/* -* Name: -* Warn - -* Purpose: -* Store warning cards in a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int Warn( AstFitsChan *this, const char *condition, const char *text, -* const char*method, const char *class, int *status ); - -* Class Membership: -* FitsChan member function. - -* Description: -* If the Warnings attribute indicates that occurences of the specified -* condition should be reported, the supplied text is split into lines -* and stored in the FitsChan as a series of ASTWARN cards, in front -* of the current card. If the specified condition is not being reported, -* this function returns without action. - -* Parameters: -* this -* The FitsChan. If NULL, this function returns without action. -* condition -* Pointer to a string holding a lower case condition name. -* text -* Pointer to a string holding the text of the warning. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - char buff[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Buffer for new card text */ - const char *a; /* Pointer to 1st character in next card */ - const char *b; /* Pointer to terminating null character */ - const char *c; /* Pointer to last character in next card */ - int exists; /* Has the supplied warning already been issued? */ - int icard; /* Index of original card */ - int nc; /* No. of characters in next card */ - -/* Check the inherited status, warning text, FitsChan and Clean attribute. */ - if( !astOK || !text || !text[0] || !this || astGetClean( this ) ) return; - -/* Ignore the warning if the supplied condition is not contained within - the list of conditions to be reported in this way (given by the - Warnings attribute). */ - if( FullForm( astGetWarnings( this ), condition, 0, status ) >= 0 ){ - -/* If found, store the warning in the parent Channel structure. */ - astAddWarning( this, 1, "%s", method, status, text ); - -/* For historical reasons, warnings are also stored in the FitsChan as a - set of FITS cards... First save the current card index, and rewind the - FitsChan. */ - icard = astGetCard( this ); - astClearCard( this ); - -/* Break the supplied text into lines and check the FitsChan to see if - a block of adjacent ASTWARN cards with these lines already exist - within the FitsChan. Assume they do until proven otherwise. */ - exists = 1; - a = text; - b = a + strlen( text ); - while( a < b ){ - -/* Each card contains about 60 characters of the text. Get a pointer to - the nominal last character in the next card. */ - c = a + 60; - -/* If this puts the last character beyond the end of the text, use the - last character before the null as the last character in the card. */ - if( c >= b ) { - c = b - 1; - -/* Otherwise, if the last character is not a space, move the last - character backwards to the first space. This avoids breaking words - across cards. */ - } else { - while( !isspace( *c ) && c > a ) c--; - } - -/* Copy the text into a null terminated buffer. */ - nc = c - a + 1; - strncpy( buff, a, nc ); - buff[ nc ] = 0; - -/* If this is the first line, search the entire FitsChan for an ASTWARN card - with this text. If not, indiate that the supplied text needs to be - stored in the FitsChan, and break out of the loop. */ - if( a == text ) { - exists = 0; - while( !exists && - FindKeyCard( this, "ASTWARN", method, class, status ) ) { - if( !strcmp( (const char *) CardData( this, NULL, status ), buff ) ) { - exists = 1; - } - MoveCard( this, 1, method, class, status ); - } - if( !exists ) break; - -/* If this is not the first line, see if the next card in the FitsChan is - an ASTWARN card with this text. If not, indiate that the supplied text - needs to be stored in the FitsChan, and break out of the loop. */ - } else { - if( !strcmp( CardName( this, status ), "ASTWARN" ) && - !strcmp( (const char *) CardData( this, NULL, status ), buff ) ) { - MoveCard( this, 1, method, class, status ); - } else { - exists = 0; - break; - } - } - -/* Set the start of the next bit of the text. */ - a = c + 1; - } - -/* Reinstate the original current card index. */ - astSetCard( this, icard ); - -/* We only add new cards to the FitsChan if they do not already exist. */ - if( !exists ) { - -/* Break the text into lines using the same algorithm as above, and store - each line as a new ASTWARN card. Start with a blank ASTWARN card. */ - astSetFitsS( this, "ASTWARN", " ", NULL, 0 ); - -/* Loop until the entire text has been written out. */ - a = text; - b = a + strlen( text ); - while( a < b ){ - -/* Each card contains about 60 characters of the text. Get a pointer to - the nominal last character in the next card. */ - c = a + 60; - -/* If this puts the last character beyond the end of the text, use the - last character before the null as the last character in the card. */ - if( c >= b ) { - c = b - 1; - -/* Otherwise, if the last character is not a space, move the last - character backwards to the first space. This avoids breaking words - across cards. */ - } else { - while( !isspace( *c ) && c > a ) c--; - } - -/* Copy the text into a null terminated buffer. */ - nc = c - a + 1; - strncpy( buff, a, nc ); - buff[ nc ] = 0; - -/* Store the buffer as the next card. */ - astSetFitsS( this, "ASTWARN", buff, NULL, 0 ); - -/* Set the start of the next bit of the text. */ - a = c + 1; - } - -/* Include a final blank card. */ - astSetFitsS( this, "ASTWARN", " ", NULL, 0 ); - } - } -} - -static int WATCoeffs( const char *watstr, int iaxis, double **cvals, - int **mvals, int *ok, int *status ){ -/* -* Name: -* WATCoeffs - -* Purpose: -* Get the polynomial coefficients from the lngcor or latcor component -* of an IRAF WAT string. - -* Type: -* Private function. - -* Synopsis: -* int WATCoeffs( const char *watstr, int iaxis, double **cvals, -* int **mvals, int *ok, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function extracts the polynomial coefficients from a supplied -* string containing the concatenated values of a set of IRAF "WAT" -* keywords, such as used for the IRAF-specific TNX and ZPX projections. -* The coefficients are returned in the form of a set of PVi_m values -* for a TPN projection. - -* Parameters: -* watstr -* The concatentated WAT keyword values. -* iaxis -* Zero based index of the axis to which the WAT keywords refer (0 -* or 1). -* cvals -* Location at which to return a pointer to a dynamically allocated -* list of coefficient values, or NULL if no lngcor/latcor values -* were found in the WAT string. Free using astFree. -* mvals -* Location at which to return a pointer to a dynamically allocated -* list of coefficient indices, or NULL if no lngcor/latcor values -* were found in the WAT string. Free using astFree. -* ok -* Pointer to an in which is returned set to zero if the polynomial -* in the supplied WAT string cannot be represented using TPN form. -* Non-zero otherwise. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The size of the returned cvals and mvals arrays. - -*/ - -/* Local Variables: */ - char **w1; - char **w2; - double *coeff; - double *pc; - int result; - double dval; - double etamax; - double etamin; - double ximax; - double ximin; - int cheb; - int etaorder; - int iword; - int m; - int mn; - int nword; - int order; - int porder; - int xiorder; - int ires; - -/* The number of lngcor/latcor values needed for each order. */ - static const int nab[] = {1,3,6,10,15,21,28,36}; - -/* Initialise the pointer to the returned Mapping. */ - result = 0; - *mvals = NULL; - *cvals = NULL; - *ok = 1; - -/* Other initialisation to avoid compiler warnings. */ - etamin = 0.0; - etamax = 0.0; - ximax = 0.0; - ximin = 0.0; - order = 0; - -/* Check the global status. */ - if ( !astOK || !watstr ) return result; - -/* Look for cor = "..." and extract the "..." string. */ - w1 = astChrSplitRE( watstr, "cor *= *\"(.*)\"", &nword, NULL ); - if( w1 ) { - -/* Split the "..." string into words. */ - w2 = astChrSplit( w1[ 0 ], &nword ); - if( w2 ) { - -/* Initialise flags. */ - cheb = 0; - xiorder = 0; - etaorder = 0; - coeff = NULL; - porder = -1; - -/* Loop round each word. Break early if we find that the projection - cannot be represented as a TPN projection. */ - for( iword = 0; iword < nword && *ok; iword++ ) { - -/* Convert the word to double. */ - dval = astChr2Double( w2[ iword ] ); - if( dval == AST__BAD ) { - astError( AST__BDFTS, "astRead(FitsChan): Failed to read a " - "numerical value from sub-string \"%s\" found in " - "an IRAF \"WAT...\" keyword.", status, w2[ iword ] ); - break; - } - -/* The first value gives the correction surface type. We can only handle type - 1 (chebyshev) or 3 (simple polynomial). */ - if( iword == 0 ){ - if( dval == 1.0 ) { - cheb = 1; - } else if( dval == 2.0 ) { - *ok = 0; - } - -/* The second and third numbers gives the orders of the polynomial in X - and Y. We can only handle cases in which the orders are the same on - both axes, and greater than 0 and less than 8. Store a pointer to the - first TAN projection parameter index to use. */ - } else if( iword == 1 ){ - order = dval; - porder = order - 1; - - } else if( iword == 2 ){ - if( dval - 1 != porder || dval < 0 || dval > 7 ) *ok = 0; - -/* The fourth number defines the type of cross-terms. We can only handle - type 2 (half-cross terms). */ - } else if( iword == 3 ){ - if( dval != 2.0 ) *ok = 0; - -/* We now know the maximum number of co-efficients that may be needed. - Allocate memory to hold them, and fill it with zeros. They are - stored in this array as if full cross-terms have been supplied (the - unspecified coefficients retain their initialised value of zero). */ - coeff = astCalloc( order*order, sizeof( double ) ); - if( !astOK ) break; - -/* The next 4 numbers describe the region of validity of the fits in IRAF's - xi and eta space, e.g. ximin, ximax, etamin, etamax. We only uses - these if we have a chebyshev polynomial. */ - } else if( iword == 4 ) { - ximin = dval; - - } else if( iword == 5 ) { - ximax = dval; - - } else if( iword == 6 ) { - etamin = dval; - - } else if( iword == 7 ) { - etamax = dval; - -/* The remaining terms are the coefficients of the polynomial terms. */ - } else if( iword > 7 ){ - -/* Store the coefficient in the array. They are stored so that power of - xi increases fastest. */ - coeff[ xiorder + order*etaorder ] = dval; - -/* Increment the powers of the next coefficient. We know we only have half - cross-terms, so the maximum power of xi decreases from order to zero - as we move through the list of coefficients. */ - if( ++xiorder == order - etaorder ) { - xiorder = 0; - etaorder++; - } - } - } - -/* Check that all the required co-efficients were found */ - if( porder == -1 || nword != 8 + nab[ porder ] ) *ok = 0; - -/* If we can handle the projection, proceed. */ - if( *ok && astOK ) { - -/* If the coefficients were supplied in chebyshev form, convert to simple - form. */ - if( cheb ) { - double *tcoeff = coeff; - coeff = Cheb2Poly( tcoeff, order, order, ximin, - ximax, etamin, etamax, status ); - tcoeff = astFree( tcoeff ); - } - -/* The polynomials provide a "correction* to be added to the supplied X and - Y values. Therefore increase the linear co-efficients by 1 on the axis - that is being calculated. */ - coeff[ iaxis ? order : 1 ] += 1.0; - -/* Loop round all coefficients, keeping track of the power of xi and eta - for the current coefficient. */ - pc = coeff; - for( etaorder = 0; etaorder < order; etaorder++ ) { - for( xiorder = 0; xiorder < order; xiorder++,pc++ ) { - -/* Skip coefficients that have their default values (zero, except for the - linear coefficients which default to 1.0). */ - mn = xiorder + etaorder; - if( *pc != ( mn == 1 ? 1.0 : 0.0 ) ) { - -/* Find the "m" index of the PVi_m FITS keyword for the current - coefficient. */ - m = mn*( 1 + mn )/2 + mn/2; - m += iaxis ? xiorder : etaorder; - -/* Append the PV and m values to the ends of the returned arrays. */ - ires = result++; - *cvals = astGrow( *cvals, sizeof( double ), result ); - *mvals = astGrow( *mvals, sizeof( int ), result ); - if( astOK ) { - (*cvals)[ ires ] = *pc; - (*mvals)[ ires ] = m; - } - } - } - } - -/* Free coefficients arrays */ - coeff = astFree( coeff ); - } - -/* Free resources */ - w2 = astFree( w2 ); - } - w1 = astFree( w1 ); - } - -/* Return the result. */ - return result; -} - -static AstMatrixMap *WcsCDeltMatrix( FitsStore *store, char s, int naxes, - const char *method, const char *class, int *status ){ -/* -* Name: -* WcsCDeltMatrix - -* Purpose: -* Create a MatrixMap representing the CDELT scaling. - -* Type: -* Private function. - -* Synopsis: -* AstMatrixMap *WcsCDeltMatrix( FitsStore *store, char s, int naxes, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A diagonal MatrixMap representing the FITS "CDELT" keywords is -* returned. - -* Parameters: -* store -* A structure containing values for FITS keywords relating to -* the World Coordinate System. -* s -* A character s identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* naxes -* The number of intermediate world coordinate axes (WCSAXES). -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the created MatrixMap or a NULL pointer if an -* error occurred. -*/ - -/* Local Variables: */ - AstMatrixMap *new; /* The created MatrixMap */ - double *el; /* Pointer to next matrix element */ - double *mat; /* Pointer to matrix array */ - int i; /* Pixel axis index */ - -/* Initialise/ */ - new = NULL; - -/* Check the global status. */ - if ( !astOK ) return new; - -/* Allocate memory for the diagonal matrix elements. */ - mat = (double *) astMalloc( sizeof(double)*naxes ); - if( astOK ){ - -/* Fill the matrix diagonal with values from the FitsStore. */ - el = mat; - for( i = 0; i < naxes; i++ ){ - -/* Get the CDELTi value for this axis. Missing terms can be defaulted so - do not report an error if the required value is not present in the - FitsStore. */ - *el = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - -/* Missing terms default to to 1.0. */ - if( *el == AST__BAD ) *el = 1.0; - -/* Move on to the next matrix element. */ - el++; - } - -/* Create the diagional matrix. */ - new = astMatrixMap( naxes, naxes, 1, mat, "", status ); - -/* Report an error if the inverse transformation is undefined. */ - if( !astGetTranInverse( new ) && astOK ) { - astError( AST__BDFTS, "%s(%s): Unusable CDELT values found " - "in the FITS-WCS header - one or more values are zero.", status, method, class ); - } - -/* Release the memory used to hold the matrix. */ - mat = (double *) astFree( (void *) mat ); - } - -/* If an error has occurred, attempt to annul the returned MatrixMap. */ - if( !astOK ) new = astAnnul( new ); - -/* Return the MatrixMap. */ - return new; -} - -static AstMapping *WcsCelestial( AstFitsChan *this, FitsStore *store, char s, - AstFrame **frm, AstFrame *iwcfrm, double *reflon, double *reflat, - AstSkyFrame **reffrm, AstMapping **tabmap, - int *tabaxis, const char *method, - const char *class, int *status ){ -/* -* Name: -* WcsCelestial - -* Purpose: -* Create a Mapping from intermediate world coords to celestial coords -* as described in a FITS header. - -* Type: -* Private function. - -* Synopsis: -* AstMapping *WcsCelestial( AstFitsChan *this, FitsStore *store, char s, -* AstFrame **frm, AstFrame *iwcfrm, double *reflon, double *reflat, -* AstSkyFrame **reffrm, , AstMapping **tabmap, -* int *tabaxis, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function interprets the contents of the supplied FitsStore -* structure, looking for world coordinate axes which describe positions -* on the sky. If a pair of such longitude/latitude axes is found, a -* Mapping is returned which transforms the corresponding intermediate -* world coordinates to celestial world coordinates (this mapping leaves -* any other axes unchanged). It also, modifies the supplied Frame to -* describe the axes (again, other axes are left unchanged). If no -* pair of celestial axes is found, a UnitMap is returned, and the -* supplied Frame is left unchanged. - -* Parameters: -* this -* The FitsChan. -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* frm -* The address of a location at which to store a pointer to the -* Frame describing the world coordinate axes. -* iwcfrm -* A pointer to the Frame describing the intermediate world coordinate -* axes. The properties of this Frame may be changed on exit. -* reflon -* Address of a location at which to return the celestial longitude -* at the reference point. It is returned as AST__BAD if no -* celestial coordinate frame is found. -* reflat -* Address of a location at which to return the celestial latitude -* at the reference point. It is returned as AST__BAD if no -* celestial coordinate frame is found. -* reffrm -* Address of a location at which to return a pointer to a SkyFrame -* which define the reference values returned in reflon and reflat. -* It is returned as NULL if no celestial coordinate frame is found. -* tabmap -* Address of a pointer to a Mapping describing any -TAB -* transformations to be applied to the results of the Mapping returned -* by this function. If any celestial axes are found, the supplied -* Mapping is modified so that the celestial axes produce values in -* radians rather than degrees. NULL if no axes are described by -TAB. -* tabaxis -* Pointer to an array of flags, one for each WCS axis, indicating -* if the corresponding WCS axis is described by the -TAB algorithm. -* NULL if no axes are described by -TAB. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the Mapping. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFrame *ofrm; /* Pointer to a Frame */ - AstMapping *map1; /* Pointer to a Mapping */ - AstMapping *map2; /* Pointer to a Mapping */ - AstMapping *map3; /* Pointer to a Mapping */ - AstMapping *map4; /* Pointer to a Mapping */ - AstMapping *ret; /* Pointer to the returned Mapping */ - AstMapping *newmap; /* Modified PIXEL->IWC Mapping */ - AstMapping *shiftmap; /* ShiftMap from IWC to PPC */ - AstSkyFrame *sfrm; /* Pointer to a SkyFrame */ - char *ctype; /* Pointer to CTYPE string */ - char *keyname; /* Pointer to keyword name string */ - char buf[300]; /* Text buffer */ - char latctype[MXCTYPELEN];/* Latitude CTYPE keyword value */ - char latkey[10]; /* Latitude CTYPE keyword name */ - char lattype[4]; /* Buffer for celestial system */ - char lonctype[MXCTYPELEN];/* Longitude CTYPE keyword value */ - char lonkey[10]; /* Longitude CTYPE keyword name */ - char lontype[4]; /* Buffer for celestial system */ - double *shifts; /* Array holding axis shifts */ - double *ina; /* Pointer to memory holding input position A */ - double *inb; /* Pointer to memory holding input position B */ - double *mat; /* Pointer to data for deg->rad scaling matrix */ - double *outa; /* Pointer to memory holding output position A */ - double *outb; /* Pointer to memory holding output position B */ - double latval; /* CRVAL for latitude axis */ - double lonval; /* CRVAL for longitude axis */ - double pv; /* Projection parameter value */ - double x0; /* IWC X at the projection fiducial point */ - double y0; /* IWC Y at the projection fiducial point */ - int *axes; /* Point to a list of axis indices */ - int axlat; /* Index of latitude physical axis */ - int axlon; /* Index of longitude physical axis */ - int carlin; /* Assume native and WCS axes are the same? */ - int ctlen; /* Length of CTYPE string */ - int gotax; /* Celestial axis found? */ - int i; /* Loop count */ - int j; /* Axis index */ - int latprj; /* Latitude projection type identifier */ - int lonprj; /* Longitude projection type identifier */ - int m; /* Parameter index */ - int mxpar_lat; /* Max. projection parameter index on lat axis */ - int mxpar_lon; /* Max. projection parameter index on lon axis */ - int naxes; /* Number of axes */ - int nc; /* String length */ - int np; /* Max parameter index */ - int prj; /* Projection type identifier */ - -/* Initialise the returned values. */ - ret = NULL; - *reflon = AST__BAD; - *reflat = AST__BAD; - *reffrm = NULL; - -/* Other initialisation to avoid compiler warnings. */ - map1 = NULL; - -/* Check the global status. */ - if ( !astOK ) return ret; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* Get the number of physical axes. */ - naxes = astGetNaxes( *frm ); - -/* See if CAR projections should be interpreted in the old fashioned way - (i.e native coords are always the same as WCS coords, so no need for - any rotation). */ - carlin = astGetCarLin( this ); - -/* The first major section sees if the physical axes include a pair of - longitude/latitude celestial axes. - ================================================================= */ - -/* We have not yet found any celestial axes. */ - axlon = -1; - axlat = -1; - latprj = AST__WCSBAD; - lonprj = AST__WCSBAD; - prj = AST__WCSBAD; - -/* First, we examine the CTYPE values in the FitsStore to determine - which axes are the longitude and latitude axes, and what the celestial - co-ordinate system and projection are. Loop round the physical axes, - getting each CTYPE value. */ - for( i = 0; i < naxes && astOK; i++ ){ - keyname = FormatKey( "CTYPE", i + 1, -1, s, status ); - ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - -/* Issue a warning if no CTYPE value was found. */ - if( !ctype ) { - sprintf( buf, "Axis type keywords (CTYPE, etc) were not found " - "for one or more axes in the original FITS header. These " - "axes will be assumed to be linear." ); - Warn( this, "noctype", buf, method, class, status ); - } else { - -/* See if this is a longitude axis (e.g. if the first 4 characters of CTYPE - are "RA--" or "xLON" or "yzLN" ). If so, store the value of "x" or "yz" - (or "EQU" for equatorial coordinates) in variable "type" to indicate which - coordinate system is being used. */ - nc = strlen( ctype ); - gotax = 0; - if( !strcmp( ctype, "RA" ) || !strncmp( ctype, "RA--", 4 ) ){ - strcpy( wcscelestial_type, "EQU" ); - gotax = 1; - } else if( !strcmp( ctype, "AZ" ) || !strncmp( ctype, "AZ--", 4 ) ){ - strcpy( wcscelestial_type, "AZL" ); - gotax = 1; - } else if( nc > 1 && ( !strcmp( ctype + 1, "LON" ) || - !strncmp( ctype + 1, "LON-", 4 ) ) ){ - wcscelestial_type[ 0 ] = ctype[ 0 ]; - wcscelestial_type[ 1 ] = 0; - gotax = 1; - } else if( nc > 2 && ( !strcmp( ctype + 2, "LN" ) || - !strncmp( ctype + 2, "LN-", 3 ) ) ){ - wcscelestial_type[ 0 ] = ctype[ 0 ]; - wcscelestial_type[ 1 ] = ctype[ 1 ]; - wcscelestial_type[ 2 ] = 0; - gotax = 1; - } - -/* If this is a longitude axis... */ - if( gotax ){ - -/* Check that this is the first longitude axis to be found. */ - if( axlon == -1 ){ - -/* Find the projection type as specified by the last 4 characters - in the CTYPE keyword value. AST__WCSBAD is stored in "prj" if the - last 4 characters do not specify a known WCS projection, but no error - is reported. Assume simple linear axes if no projection code is - supplied. Note, AST__WCSBAD is used to indicate a TAB header. */ - ctlen = strlen( ctype ); - if( ctlen > 4 ) { - prj = astWcsPrjType( ctype + ctlen - 4 ); - } else if( tabmap && *tabmap ) { - prj = AST__WCSBAD; - } else { - prj = AST__CAR; - carlin = 1; - } - -/* Report an error if the projection is unknown. */ - if( prj == AST__WCSBAD && ctlen > 4 ){ - astError( AST__BDFTS, "%s(%s): FITS keyword '%s' refers to " - "an unknown projection type '%s'.", status, method, class, - keyname, ctype + ctlen - 4 ); - break; - } - -/* Store the index of the longitude axis, type of longitude, etc. */ - axlon = i; - strcpy( lontype, wcscelestial_type ); - strcpy( lonkey, keyname ); - strcpy( lonctype, ctype ); - lonprj = prj; - -/* If another longitude axis has already been found, report an error. */ - } else { - astError( AST__BDFTS, "%s(%s): FITS keywords '%s' (='%s') " - "and '%s' (='%s') both describe celestial longitude axes.", status, - method, class, keyname, ctype, lonkey, lonctype ); - break; - } - } - -/* Do the same for the latitude axis, checking for "DEC-" and "xLAT" and - "yzLT". */ - gotax = 0; - if( !strcmp( ctype, "DEC" ) || !strncmp( ctype, "DEC-", 4 ) ){ - strcpy( wcscelestial_type, "EQU" ); - gotax = 1; - } else if( !strcmp( ctype, "EL" ) || !strncmp( ctype, "EL--", 4 ) ){ - strcpy( wcscelestial_type, "AZL" ); - gotax = 1; - } else if( !strcmp( ctype + 1, "LAT" ) || !strncmp( ctype + 1, "LAT-", 4 ) ){ - wcscelestial_type[ 0 ] = ctype[ 0 ]; - wcscelestial_type[ 1 ] = 0; - gotax = 1; - } else if( !strcmp( ctype + 2, "LT" ) || !strncmp( ctype + 2, "LT-", 3 ) ){ - wcscelestial_type[ 0 ] = ctype[ 0 ]; - wcscelestial_type[ 1 ] = ctype[ 1 ]; - wcscelestial_type[ 2 ] = 0; - gotax = 1; - } - if( gotax ){ - if( axlat == -1 ){ - ctlen = strlen( ctype ); - if( ctlen > 4 ) { - prj = astWcsPrjType( ctype + ctlen - 4 ); - } else if( tabmap && *tabmap ) { - prj = AST__WCSBAD; - } else { - prj = AST__CAR; - carlin = 1; - } - - if( prj == AST__WCSBAD && ctlen > 4 ){ - astError( AST__BDFTS, "%s(%s): FITS keyword '%s' refers to " - "an unknown projection type '%s'.", status, method, class, - keyname, ctype + ctlen - 4 ); - break; - } - axlat = i; - strcpy( lattype, wcscelestial_type ); - strcpy( latkey, keyname ); - strcpy( latctype, ctype ); - latprj = prj; - } else { - astError( AST__BDFTS, "%s(%s): FITS keywords '%s' (='%s') " - "and '%s' (='%s') both describe celestial latitude axes.", status, - method, class, keyname, ctype, latkey, latctype ); - break; - } - } - } - } - -/* Check the above went OK */ - if( astOK ){ - -/* If both longitude and latitude axes were found... */ - if( axlat != -1 && axlon != -1 ){ - -/* Report an error if they refer to different celestial coordinate systems. */ - if( strcmp( lattype, lontype ) ){ - astError( AST__BDFTS, "%s(%s): FITS keywords '%s' and '%s' " - "indicate different celestial coordinate systems " - "('%s' and '%s').", status, method, class, latkey, lonkey, - latctype, lonctype ); - -/* Otherwise report an error if longitude and latitude axes use different - projections. */ - } else if( lonprj != latprj ){ - astError( AST__BDFTS, "%s(%s): FITS keywords '%s' and '%s' " - "indicate different projections ('%s' and '%s').", status, - method, class, latkey, lonkey, latctype, lonctype ); - } - -/* If only one axis has been provided without the other (e.g. longitude but no - latitude), report an error. */ - } else if( axlat != -1 && prj != AST__WCSBAD ){ - astError( AST__BDFTS, "%s(%s): A latitude axis ('%s') was found " - "without a corresponding longitude axis.", status, method, class, - latctype ); - } else if( axlon != -1 && prj != AST__WCSBAD ){ - astError( AST__BDFTS, "%s(%s): A longitude axis ('%s') was found " - "without a corresponding latitude axis.", status, method, class, - lonctype ); - } - } - -/* If a pair of matching celestial axes was not found, return a UnitMap - and leave the Frame unchanged. - ===================================================================== */ - if( axlat == -1 || axlon == -1 ) { - ret = (AstMapping *) astUnitMap( naxes, "", status ); - -/* The rest of this function deals with creating a Mapping from - intermediate world coords to celestial coords, and modifying the - Frame appropriately. - ===================================================================== */ - } else if( astOK ) { - -/* Create a MatrixMap which scales the intermediate world coordinate axes - corresponding to the longitude and latitude axes from degrees to radians. - Only do this if a projection was supplied. */ - if( latprj != AST__WCSBAD ) { - mat = (double *) astMalloc( sizeof(double)*naxes ); - if( mat ){ - for( i = 0; i < naxes; i++ ){ - if( i == axlat || i == axlon ){ - mat[ i ] = AST__DD2R; - } else { - mat[ i ] = 1.0; - } - } - map1 = (AstMapping *) astMatrixMap( naxes, naxes, 1, mat, "", status ); - mat = (double *) astFree( (void *) mat ); - } - } else { - map1 = (AstMapping *) astUnitMap( naxes, " ", status ); - } - -/* If the projection is a CAR projection, but the CarLin attribute is - set, then we consider the CAR projection to be a simple linear mapping - of pixel coords to celestial coords. Do this by using a WcsMap with no - projection. All axes will then be treated as linear and non-celestial. - If no projection was specified (i.e. if prj == AST__WCSBAD, as is the - case when using -TAB for instance) then do the same but use a UnitMap - instead of a WcsMap. */ - map3 = NULL; - if( ( latprj == AST__CAR && carlin ) || latprj == AST__WCSBAD ) { - if( latprj == AST__CAR ) { - map2 = (AstMapping *) astWcsMap( naxes, AST__WCSBAD, axlon + 1, - axlat + 1, "", status ); - } else { - map2 = (AstMapping *) astUnitMap( naxes, "", status ); - } - -/* Now create a WinMap which adds on the CRVAL values to each axis. */ - ina = astMalloc( sizeof(double)*naxes ); - inb = astMalloc( sizeof(double)*naxes ); - outa = astMalloc( sizeof(double)*naxes ); - outb = astMalloc( sizeof(double)*naxes ); - if( astOK ) { - for( i = 0; i < naxes; i++ ) { - ina[ i ] = 0.0; - inb[ i ] = 1.0; - outa[ i ] = 0.0; - outb[ i ] = 1.0; - } - lonval = GetItem( &(store->crval), axlon, 0, s, NULL, method, class, status ); - if( lonval != AST__BAD ) { - -/* For recognised projections the CRVAL value is required to be degrees, - so convert to radians. For other algorithms (e.g. -TAB) the CRVAL - values are in unknown units so retain their original scaling. */ - *reflon = ( latprj == AST__CAR ) ? lonval*AST__DD2R : lonval; - - outa[ axlon ] += *reflon; - outb[ axlon ] += *reflon; - } else { - outa[ axlon ] = AST__BAD; - outb[ axlon ] = AST__BAD; - } - - latval = GetItem( &(store->crval), axlat, 0, s, NULL, method, class, status ); - if( latval != AST__BAD ) { - *reflat = ( latprj == AST__CAR ) ? latval*AST__DD2R : latval; - outa[ axlat ] += *reflat; - outb[ axlat ] += *reflat; - } else { - outa[ axlat ] = AST__BAD; - outb[ axlat ] = AST__BAD; - } - - map3 = (AstMapping *) astWinMap( naxes, ina, inb, outa, outb, "", status ); - - } - ina = astFree( ina ); - inb = astFree( inb ); - outa = astFree( outa ); - outb = astFree( outb ); - -/* Otherwise, create a WcsMap with the specified projection. The WcsMap - is equivalent to a unit mapping for all axes other than "axlat" and - "axlon". */ - } else { - -/* Get the highest index ("m" value) of any supplied PVi_m projection - parameters (on any axes). */ - np = GetMaxJM( &(store->pv), s, status ); - -/* Create the WcsMap */ - map2 = (AstMapping *) astWcsMap( naxes, latprj, axlon + 1, - axlat + 1, "", status ); - -/* If the FITS header contains any projection parameters, store them in - the WcsMap. */ - mxpar_lat = astGetPVMax( map2, axlat ); - mxpar_lon = astGetPVMax( map2, axlon ); - for( m = 0; m <= np; m++ ){ - pv = GetItem( &(store->pv), axlat, m, s, NULL, method, class, status ); - if( pv != AST__BAD ) { - if( m <= mxpar_lat ) { - astSetPV( map2, axlat, m, pv ); - } else { - sprintf( buf, "Projection parameter PV%d_%d found, " - "but is not used by %s projections.", axlat + 1, - m, astWcsPrjName( astGetWcsType( map2 ) ) ); - Warn( this, "badpv", buf, method, class, status ); - } - } - pv = GetItem( &(store->pv), axlon, m, s, NULL, method, class, status ); - if( pv != AST__BAD ) { - if( m <= mxpar_lon ) { - astSetPV( map2, axlon, m, pv ); - } else { - sprintf( buf, "Projection parameter PV%d_%d found, " - "but is not used by %s projections.", axlon + 1, - m, astWcsPrjName( astGetWcsType( map2 ) ) ); - Warn( this, "badpv", buf, method, class, status ); - } - } - } - -/* Invert the WcsMap to get a DEprojection. */ - astInvert( map2 ); - -/* Now produce a Mapping which converts the axes holding "Native Spherical - Coords" into "Celestial Coords", leaving all other axes unchanged. */ - map3 = WcsNative( this, store, s, (AstWcsMap *) map2, -1, -1, - method, class, status ); - -/* Retrieve and store the reference longitude and latitude. */ - *reflon = GetItem( &(store->crval), axlon, 0, s, NULL, method, class, status ); - if( *reflon != AST__BAD ) *reflon *= AST__DD2R; - *reflat = GetItem( &(store->crval), axlat, 0, s, NULL, method, class, status ); - if( *reflat != AST__BAD ) *reflat *= AST__DD2R; - } - -/* If projection parameter PVi_0a for the longitude axis "i" is non-zero, - then there is a shift of origin between Intermediate World Coords, IWC, - (the CRPIXi values correspond to the origin of IWC), and Projection Plane - Coords, PPC (these are the cartesian coordinates used by the WcsMap). - This shift of origin results in the fiducial point specified by the - CRVALi values mapping onto the pixel reference point specified by the - CRPIXj values. In this case we need to add a Mapping which implements - the shift of origin. Note, the AST-specific "TPN" projection cannot use - this convention since it uses PVi_0 to hold a polynomial correction term. */ - if( latprj != AST__WCSBAD && astGetWcsType( map2 ) != AST__TPN && - astGetPV( map2, axlon, 0 ) != 0.0 ) { - -/* Find the projection plane coords corresponding to the fiducial point - of the projection. This is done by using the inverse WcsMap to convert - the native spherical coords at the fiducial point into PPC (x,y), which - are returned in units of radians (not degrees). */ - GetFiducialPPC( (AstWcsMap *) map2, &x0, &y0, status ); - if( x0 != AST__BAD && y0 != AST__BAD ) { - -/* Allocate resources. */ - shifts = astMalloc( sizeof( double )*(size_t) naxes ); - -/* Check pointers can be used safely. */ - if( astOK ) { - -/* Create a Mapping (a ShiftMap) from IWC to PPC. */ - for( i = 0; i < naxes; i++ ) shifts[ i ] = 0.0; - shifts[ axlon ] = x0; - shifts[ axlat ] = y0; - shiftmap = (AstMapping *) astShiftMap( naxes, shifts, "", status ); - -/* Produce a CmpMap which combines "map1" (which converts degrees to - radians on the celestial axes) with the above ShiftMap. */ - newmap = (AstMapping *) astCmpMap( map1, shiftmap, 1, "", status ); - -/* Annul the component Mappings and use the new one in place of map1. */ - shiftmap = astAnnul( shiftmap ); - map1 = astAnnul( map1 ); - map1 = newmap; - } - -/* Free resources. */ - shifts = astFree( shifts ); - } - } - -/* Now concatenate the Mappings to produce the returned Mapping. */ - map4 = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); - ret = (AstMapping *) astCmpMap( map4, map3, 1, "", status ); - -/* Annul the component Mappings. */ - map1 = astAnnul( map1 ); - map2 = astAnnul( map2 ); - map3 = astAnnul( map3 ); - map4 = astAnnul( map4 ); - -/* We now make changes to the supplied Frame so that the longitude and - latitude axes are described by a SkyFrame. First create an appropriate - SkyFrame. */ - sfrm = WcsSkyFrame( this, store, s, prj, wcscelestial_type, axlon, - axlat, method, class, status ); - -/* The values currently stored in *reflat and *reflon are the CRVAL - values. In some circumstances, these may not be the original values in - the supplied header but may have been translated within the SpecTrans - function as part of the process of translating an old unsupported - projection into a new supported projection. Since the returned RefLat - and RefLon values may be used to set the reference position for a - SpecFrame, we should return the original values rather than the - translated values. The original values will have been stored (within - SpecTrans) in the FitsChan as keywords RFVALi. If such keywords can - be found, use their values in preference to the currently stored CRVAL - values.*/ - if( GetValue( this, FormatKey( "RFVAL", axlon + 1, -1, s, status ), - AST__FLOAT, (void *) &lonval, 0, 0, method, class, status ) && - GetValue( this, FormatKey( "RFVAL", axlat + 1, -1, s, status ), - AST__FLOAT, (void *) &latval, 0, 0, method, class, status ) ) { - *reflon = lonval*AST__DD2R; - *reflat = latval*AST__DD2R; - } - -/* Store the reflon and reflat values as the SkyRef position in the - SkyFrame, and set SkyRefIs to "ignore" so that the SkyFrame continues - to represent absolute celestial coords. Do not change the SkyFrame if - it already had a set reference posiiton. */ - if( ! astTestSkyRef( sfrm, 0 ) ) { - if( *reflon != AST__BAD && *reflat != AST__BAD ) { - astSetSkyRef( sfrm, 0, *reflon ); - astSetSkyRef( sfrm, 1, *reflat ); - astSet( sfrm, "SkyRefIs=Ignored", status ); - } - } - -/* Return a clone of this SkyFrame as the reference Frame. */ - *reffrm = astClone( sfrm ); - -/* Create a Frame by picking all the other (non-celestial) axes from the - supplied Frame. */ - axes = astMalloc( naxes*sizeof( int ) ); - if( axes ) { - j = 0; - for( i = 0; i < naxes; i++ ) { - if( i != axlat && i != axlon ) axes[ j++ ] = i; - } - -/* If there were no other axes, replace the supplied Frame with the skyframe. */ - if( j == 0 ) { - (void) astAnnul( *frm ); - *frm = (AstFrame *) astClone( sfrm ); - -/* Otherwise pick the other axes from the supplied Frame */ - } else { - ofrm = astPickAxes( *frm, j, axes, NULL ); - -/* Replace the supplied Frame with a CmpFrame made up of this Frame and - the SkyFrame. */ - (void) astAnnul( *frm ); - *frm = (AstFrame *) astCmpFrame( ofrm, sfrm, "", status ); - ofrm = astAnnul( ofrm ); - } - -/* Permute the axis order to put the longitude and latitude axes back in - their original position. The SkyFrame will have the default axis - ordering (lon=axis 0, lat = axis 1). */ - j = 0; - for( i = 0; i < naxes; i++ ) { - if( i == axlat ) { - axes[ i ] = naxes - 1; - } else if( i == axlon ) { - axes[ i ] = naxes - 2; - } else { - axes[ i ] = j++; - } - } - astPermAxes( *frm, axes ); - -/* Free the axes array. */ - axes= astFree( axes ); - } - -/* Set the units in the supplied IWC Frame for the longitude and latitude - axes. Unless using -TAB, these are degrees (the conversion from degs to - rads is part of the Mapping from IWC to WCS). If using -TAB the units - are unknown. */ - if( !tabaxis || !tabaxis[ axlon ] ) astSetUnit( iwcfrm, axlon, "deg" ); - if( !tabaxis || !tabaxis[ axlat ] ) astSetUnit( iwcfrm, axlat, "deg" ); - -/* Modify any supplied tabmap so that the celestial outputs create - radians rather than degrees (but only if the celestial axes are - generated by the -TAB algorithm). */ - if( tabaxis && tabaxis[ axlon ] && tabaxis[ axlat ] ) { - mat = (double *) astMalloc( sizeof(double)*naxes ); - if( mat ){ - for( i = 0; i < naxes; i++ ){ - if( i == axlat || i == axlon ){ - mat[ i ] = AST__DD2R; - } else { - mat[ i ] = 1.0; - } - } - map1 = (AstMapping *) astMatrixMap( naxes, naxes, 1, mat, "", status ); - mat = (double *) astFree( (void *) mat ); - map2 = (AstMapping *) astCmpMap( *tabmap, map1, 1, " ", status ); - map1 = astAnnul( map1 ); - (void) astAnnul( *tabmap ); - *tabmap = map2; - } - -/* Also modify the returned reflon and reflat values to transform them - using the tabmap. Also transform the reference position in the SkyFrame. */ - if( *reflon != AST__BAD && *reflat != AST__BAD ) { - ina = astMalloc( sizeof(double)*naxes ); - outa = astMalloc( sizeof(double)*naxes ); - if( astOK ) { - for( i = 0; i < naxes; i++ ) ina[ i ] = 0.0; - ina[ axlat ] = *reflat; - ina[ axlon ] = *reflon; - astTranN( *tabmap, 1, naxes, 1, ina, 1, naxes, 1, outa ); - *reflon = outa[ axlon ]; - *reflat = outa[ axlat ]; - } - ina = astFree( ina ); - outa = astFree( outa ); - -/* Store this transformed reference position in the SkyFrame. */ - astSetSkyRef( sfrm, 0, *reflon ); - astSetSkyRef( sfrm, 1, *reflat ); - astSet( sfrm, "SkyRefIs=Ignored", status ); - } - } - -/* If the header contains AXREF values for both lon and lat axes, use - them as the sky reference position in preferences to the values - derived form the CRVAL values. AXREF keywords are created by the - astWrite method for axes described by -TAB algorithm that have no inverse - transformation. */ - if( GetValue( this, FormatKey( "AXREF", axlon + 1, -1, s, status ), - AST__FLOAT, (void *) &lonval, 0, 0, method, class, status ) && - GetValue( this, FormatKey( "AXREF", axlat + 1, -1, s, status ), - AST__FLOAT, (void *) &latval, 0, 0, method, class, status ) ) { - *reflon = lonval*AST__DD2R; - *reflat = latval*AST__DD2R; - astSetSkyRef( sfrm, 0, *reflon ); - astSetSkyRef( sfrm, 1, *reflat ); - astSet( sfrm, "SkyRefIs=Ignored", status ); - } - -/* Free resources. */ - sfrm = astAnnul( sfrm ); - } - -/* Return the result. */ - return ret; -} - -static void WcsFcRead( AstFitsChan *fc, AstFitsChan *fc2, FitsStore *store, - const char *method, const char *class, int *status ){ -/* -* Name: -* WcsFcRead - -* Purpose: -* Extract WCS information from a supplied FitsChan using a FITSWCS -* encoding, and store it in the supplied FitsStore. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WcsFcRead( AstFitsChan *fc, AstFitsChan *fc2, FitsStore *store, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function extracts FITSWCS keywords from the supplied FitsChan, -* and stores the corresponding WCS information in the supplied FitsStore. - -* Parameters: -* fc -* Pointer to the FitsChan containing the cards read from the -* original FITS header. This should not include any un-used -* non-standard keywords. -* fc2 -* Pointer to a second FitsChan. If a card read from "fc" fails to -* be converted to its correct data type, a warning is only issued -* if there is no card for this keyword in "fc2". "fc2" may be NULL -* in which case a warning is always issued. -* store -* Pointer to the FitsStore structure. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. -*/ - -/* Local Variables: */ - char buf[200]; /* Buffer for warning message */ - char *cval; /* String keyword value */ - char *keynam; /* Pointer to current keyword name */ - char s; /* Co-ordinate version character */ - char *telescop; /* Pointer to TELESCOP keyword value */ - double dval; /* Floating point keyword value */ - int fld[2]; /* Integer field values from keyword name */ - int jm; /* Pixel axis or projection parameter index */ - int i; /* Intermediate axis index */ - int mark; /* Non-zero if card should be removed once used */ - int nfld; /* Number of integer fields in test string */ - int ok; /* Was value converted succesfully? */ - int type; /* Keyword data type */ - int undef; /* Is an undefined keyword value acceptable? */ - void *item; /* Pointer to item to get/put */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Ensure the FitsChan is re-wound. */ - astClearCard( fc ); - -/* Loop round all the cards in the FitsChan obtaining the keyword name for - each card. Note, the single "=" is correct in the following "while" - statement. */ - s = 0; - jm = -1; - i = -1; - type = AST__NOTYPE; - while( (keynam = CardName( fc, status )) ){ - item = NULL; - -/* Assume the card is to be consumed by the reading process. This means - the card will be marked as used and effectively excluded from the header. - Keywords which supply observation details that do not depend on the - mapping from pixel to WCS axes, or on the nature of the WCS axes, - are not removed as they may be needed for other, non-WCS related, - purposes. */ - mark = 1; - -/* For most keywords, if the keyword is present in the header it must - have a definded value. However, some keywords are read from the header - but not actually used for anything. This is done to ensure that the - keyword is stripped from the header. It is acceptable for such - keywords to have an undefined value. Initialise a flag indicating that - the next keyword read is not allowed to have an undefined value. */ - undef = 0; - -/* Is this a primary CRVAL keyword? */ - if( Match( keynam, "CRVAL%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->crval); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = ' '; - -/* Is this a secondary CRVAL keyword? */ - } else if( Match( keynam, "CRVAL%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->crval); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary CRPIX keyword? */ - } else if( Match( keynam, "CRPIX%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->crpix); - type = AST__FLOAT; - i = 0; - jm = fld[ 0 ] - 1; - s = ' '; - -/* Is this a secondary CRPIX keyword? */ - } else if( Match( keynam, "CRPIX%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->crpix); - type = AST__FLOAT; - i = 0; - jm = fld[ 0 ] - 1; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary CDELT keyword? */ - } else if( Match( keynam, "CDELT%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->cdelt); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = ' '; - -/* Is this a secondary CDELT keyword? */ - } else if( Match( keynam, "CDELT%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->cdelt); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary CTYPE keyword? If so, store the associated comment. */ - } else if( Match( keynam, "CTYPE%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->ctype); - type = AST__STRING; - i = fld[ 0 ] - 1; - jm = 0; - s = ' '; - SetItemC( &(store->ctype_com), i, 0, ' ', CardComm( fc, status ), status ); - -/* Is this a secondary CTYPE keyword? If so, store the associated comment. */ - } else if( Match( keynam, "CTYPE%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->ctype); - type = AST__STRING; - i = fld[ 0 ] - 1; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - SetItemC( &(store->ctype_com), i, 0, s, CardComm( fc, status ), status ); - -/* Is this a primary CNAME keyword? */ - } else if( Match( keynam, "CNAME%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->cname); - type = AST__STRING; - i = fld[ 0 ] - 1; - jm = 0; - s = ' '; - -/* Is this a secondary CNAME keyword? */ - } else if( Match( keynam, "CNAME%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->cname); - type = AST__STRING; - i = fld[ 0 ] - 1; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary CUNIT keyword? */ - } else if( Match( keynam, "CUNIT%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->cunit); - type = AST__STRING; - i = fld[ 0 ] - 1; - jm = 0; - s = ' '; - -/* Is this a secondary CUNIT keyword? */ - } else if( Match( keynam, "CUNIT%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->cunit); - type = AST__STRING; - i = fld[ 0 ] - 1; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary PC keyword? */ - } else if( Match( keynam, "PC%d_%d", 2, fld, &nfld, method, class, status ) ){ - item = &(store->pc); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = fld[ 1 ] - 1; - s = ' '; - -/* Is this a secondary PC keyword? */ - } else if( Match( keynam, "PC%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ - item = &(store->pc); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = fld[ 1 ] - 1; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary PV keyword? */ - } else if( Match( keynam, "PV%d_%d", 2, fld, &nfld, method, class, status ) ){ - item = &(store->pv); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = fld[ 1 ]; - s = ' '; - -/* Is this a secondary PV keyword? */ - } else if( Match( keynam, "PV%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ - item = &(store->pv); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = fld[ 1 ]; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary PS keyword? */ - } else if( Match( keynam, "PS%d_%d", 2, fld, &nfld, method, class, status ) ){ - item = &(store->ps); - type = AST__STRING; - i = fld[ 0 ] - 1; - jm = fld[ 1 ]; - s = ' '; - -/* Is this a secondary PS keyword? */ - } else if( Match( keynam, "PS%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ - item = &(store->ps); - type = AST__STRING; - i = fld[ 0 ] - 1; - jm = fld[ 1 ]; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary RADESYS keyword? */ - } else if( Match( keynam, "RADESYS", 0, fld, &nfld, method, class, status ) ){ - item = &(store->radesys); - type = AST__STRING; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary RADESYS keyword? */ - } else if( Match( keynam, "RADESYS%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->radesys); - type = AST__STRING; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary EQUINOX keyword? */ - } else if( Match( keynam, "EQUINOX", 0, fld, &nfld, method, class, status ) ){ - item = &(store->equinox); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary EQUINOX keyword? */ - } else if( Match( keynam, "EQUINOX%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->equinox); - type = AST__FLOAT; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary LATPOLE keyword? */ - } else if( Match( keynam, "LATPOLE", 0, fld, &nfld, method, class, status ) ){ - item = &(store->latpole); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary LATPOLE keyword? */ - } else if( Match( keynam, "LATPOLE%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->latpole); - type = AST__FLOAT; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary LONPOLE keyword? */ - } else if( Match( keynam, "LONPOLE", 0, fld, &nfld, method, class, status ) ){ - item = &(store->lonpole); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary LONPOLE keyword? */ - } else if( Match( keynam, "LONPOLE%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->lonpole); - type = AST__FLOAT; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary WXSAXES keyword? */ - } else if( Match( keynam, "WCSAXES", 0, fld, &nfld, method, class, status ) ){ - item = &(store->wcsaxes); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary WCSAXES keyword? */ - } else if( Match( keynam, "WCSAXES%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->wcsaxes); - type = AST__FLOAT; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary DUT1 keyword? */ - } else if( Match( keynam, "DUT1", 0, fld, &nfld, method, class, status ) ){ - mark = 0; - item = &(store->dut1); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a primary DTAI keyword? Only handle if the telescope is JCMT - or UKIRT, since other telescopes may use DTAI for different purposes. */ - } else if( Match( keynam, "DTAI", 0, fld, &nfld, method, class, status ) ){ - if( GetValue( fc, "TELESCOP", AST__STRING, &telescop, 0, 0, method, - class, status ) && ( !strncmp( telescop, "JCMT", 4) - || !strncmp( telescop, "UKIRT", 5 ) ) ){ - mark = 0; - item = &(store->dtai); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - } - -/* Is this a primary MJD-OBS keyword? */ - } else if( Match( keynam, "MJD-OBS", 0, fld, &nfld, method, class, status ) ){ - mark = 0; - item = &(store->mjdobs); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a primary WCSNAME keyword? */ - } else if( Match( keynam, "WCSNAME", 0, fld, &nfld, method, class, status ) ){ - item = &(store->wcsname); - type = AST__STRING; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary WCSNAME keyword? */ - } else if( Match( keynam, "WCSNAME%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->wcsname); - type = AST__STRING; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary SPECSYS keyword? */ - } else if( Match( keynam, "SPECSYS", 0, fld, &nfld, method, class, status ) ){ - item = &(store->specsys); - type = AST__STRING; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary SPECSYS keyword? */ - } else if( Match( keynam, "SPECSYS%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->specsys); - type = AST__STRING; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary SSYSSRC keyword? */ - } else if( Match( keynam, "SSYSSRC", 0, fld, &nfld, method, class, status ) ){ - item = &(store->ssyssrc); - type = AST__STRING; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary SSYSSRC keyword? */ - } else if( Match( keynam, "SSYSSRC%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->ssyssrc); - type = AST__STRING; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary ZSOURCE keyword? */ - } else if( Match( keynam, "ZSOURCE", 0, fld, &nfld, method, class, status ) ){ - item = &(store->zsource); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary ZSOURCE keyword? */ - } else if( Match( keynam, "ZSOURCE%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->zsource); - type = AST__FLOAT; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary VELOSYS keyword? */ - } else if( Match( keynam, "VELOSYS", 0, fld, &nfld, method, class, status ) ){ - item = &(store->velosys); - type = AST__FLOAT; - undef = 1; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary VELOSYS keyword? */ - } else if( Match( keynam, "VELOSYS%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->velosys); - type = AST__FLOAT; - undef = 1; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary RESTFRQ keyword? */ - } else if( Match( keynam, "RESTFRQ", 0, fld, &nfld, method, class, status ) ){ - item = &(store->restfrq); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary RESTFRQ keyword? */ - } else if( Match( keynam, "RESTFRQ%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->restfrq); - type = AST__FLOAT; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary RESTWAV keyword? */ - } else if( Match( keynam, "RESTWAV", 0, fld, &nfld, method, class, status ) ){ - item = &(store->restwav); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary RESTWAV keyword? */ - } else if( Match( keynam, "RESTWAV%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->restwav); - type = AST__FLOAT; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary IMAGFREQ keyword? */ - } else if( Match( keynam, "IMAGFREQ", 0, fld, &nfld, method, class, status ) ){ - item = &(store->imagfreq); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a primary SKYREF keyword? */ - } else if( Match( keynam, "SREF%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->skyref); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = ' '; - -/* Is this a secondary SKYREF keyword? */ - } else if( Match( keynam, "SREF%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->skyref); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary SKYREFP keyword? */ - } else if( Match( keynam, "SREFP%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->skyrefp); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = ' '; - -/* Is this a secondary SKYREFP keyword? */ - } else if( Match( keynam, "SREFP%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->skyrefp); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary SKYREFIS keyword? */ - } else if( Match( keynam, "SREFIS", 0, fld, &nfld, method, class, status ) ){ - item = &(store->skyrefis); - type = AST__STRING; - i = 0; - jm = 0; - s = ' '; - -/* Is this a secondary SKYREFIS keyword? */ - } else if( Match( keynam, "SREFIS%1c", 0, fld, &nfld, method, class, status ) ){ - item = &(store->skyrefis); - type = AST__STRING; - i = 0; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary AXREF keyword? */ - } else if( Match( keynam, "AXREF%d", 1, fld, &nfld, method, class, status ) ){ - item = &(store->axref); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = ' '; - -/* Is this a secondary AXREF keyword? */ - } else if( Match( keynam, "AXREF%d%1c", 1, fld, &nfld, method, class, status ) ){ - item = &(store->axref); - type = AST__FLOAT; - i = fld[ 0 ] - 1; - jm = 0; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a MJD-AVG keyword? */ - } else if( Match( keynam, "MJD-AVG", 0, fld, &nfld, method, class, status ) ){ - mark = 0; - item = &(store->mjdavg); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a OBSGEO-X keyword? */ - } else if( Match( keynam, "OBSGEO-X", 0, fld, &nfld, method, class, status ) ){ - mark = 0; - item = &(store->obsgeox); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a OBSGEO-Y keyword? */ - } else if( Match( keynam, "OBSGEO-Y", 0, fld, &nfld, method, class, status ) ){ - mark = 0; - item = &(store->obsgeoy); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a OBSGEO-Z keyword? */ - } else if( Match( keynam, "OBSGEO-Z", 0, fld, &nfld, method, class, status ) ){ - mark = 0; - item = &(store->obsgeoz); - type = AST__FLOAT; - i = 0; - jm = 0; - s = ' '; - -/* Is this a TIMESYS keyword? */ - } else if( Match( keynam, "TIMESYS", 0, fld, &nfld, method, class, status ) ){ - mark = 0; - item = &(store->timesys); - type = AST__STRING; - i = 0; - jm = 0; - s = ' '; - -/* Following keywords are used to describe "-SIP" distortion as used by - the Spitzer project... */ - -/* Is this a primary A keyword? */ - } else if( Match( keynam, "A_%d_%d", 2, fld, &nfld, method, class, status ) ){ - item = &(store->asip); - type = AST__FLOAT; - i = fld[ 0 ]; - jm = fld[ 1 ]; - s = ' '; - -/* Is this a secondary A keyword? */ - } else if( Match( keynam, "A_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ - item = &(store->asip); - type = AST__FLOAT; - i = fld[ 0 ]; - jm = fld[ 1 ]; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary B keyword? */ - } else if( Match( keynam, "B_%d_%d", 2, fld, &nfld, method, class, status ) ){ - item = &(store->bsip); - type = AST__FLOAT; - i = fld[ 0 ]; - jm = fld[ 1 ]; - s = ' '; - -/* Is this a secondary B keyword? */ - } else if( Match( keynam, "B_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ - item = &(store->bsip); - type = AST__FLOAT; - i = fld[ 0 ]; - jm = fld[ 1 ]; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary AP keyword? */ - } else if( Match( keynam, "AP_%d_%d", 2, fld, &nfld, method, class, status ) ){ - item = &(store->apsip); - type = AST__FLOAT; - i = fld[ 0 ]; - jm = fld[ 1 ]; - s = ' '; - -/* Is this a secondary AP keyword? */ - } else if( Match( keynam, "AP_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ - item = &(store->apsip); - type = AST__FLOAT; - i = fld[ 0 ]; - jm = fld[ 1 ]; - s = keynam[ strlen( keynam ) - 1 ]; - -/* Is this a primary BP keyword? */ - } else if( Match( keynam, "BP_%d_%d", 2, fld, &nfld, method, class, status ) ){ - item = &(store->bpsip); - type = AST__FLOAT; - i = fld[ 0 ]; - jm = fld[ 1 ]; - s = ' '; - -/* Is this a secondary BP keyword? */ - } else if( Match( keynam, "BP_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ - item = &(store->bpsip); - type = AST__FLOAT; - i = fld[ 0 ]; - jm = fld[ 1 ]; - s = keynam[ strlen( keynam ) - 1 ]; - } - -/* If this keyword was recognized, store it in the FitsStore, and mark it - as having been read. */ - if( item ){ - ok = 1; - if( type == AST__FLOAT ){ - if( CnvValue( fc, AST__FLOAT, undef, &dval, method, status ) ) { - SetItem( (double ****) item, i, jm, s, dval, status ); - if( mark ) MarkCard( fc, status ); - } else { - ok = 0; - } - } else { - if( CnvValue( fc, AST__STRING, undef, &cval, method, status ) ) { - cval[ astChrLen( cval ) ] = 0; /* Exclude trailing spaces */ - SetItemC( (char *****) item, i, jm, s, cval, status ); - if( mark ) MarkCard( fc, status ); - } else { - ok = 0; - } - } - -/* Issue a warning if the value could not be converted to the expected - type. */ - if( !ok ) { - -/* First check that the keyword is not included in "fc2". */ - if( !HasCard( fc2, keynam, method, class, status ) ) { - sprintf( buf, "The original FITS header contained a value for " - "keyword %s which could not be converted to a %s.", - keynam, ( type==AST__FLOAT ? "floating point number": - "character string" ) ); - Warn( fc, "badval", buf, "astRead", "FitsChan", status ); - } - } - } - -/* Move on to the next card. */ - MoveCard( fc, 1, method, class, status ); - } -} - -static int WcsFromStore( AstFitsChan *this, FitsStore *store, - const char *method, const char *class, int *status ){ - -/* -* Name: -* WcsFromStore - -* Purpose: -* Store WCS keywords in a FitsChan using FITS-WCS encoding. - -* Type: -* Private function. - -* Synopsis: -* int WcsFromStore( AstFitsChan *this, FitsStore *store, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function copies the WCS information stored in the supplied -* FitsStore into the supplied FitsChan, using FITS-WCS encoding. - -* Parameters: -* this -* Pointer to the FitsChan. -* store -* Pointer to the FitsStore. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A value of 1 is returned if succesfull, and zero is returned -* otherwise. -*/ - -/* Local Variables: */ - char *comm; /* Pointer to comment string */ - char *cval; /* Pointer to string keyword value */ - char combuf[80]; /* Buffer for FITS card comment */ - char parprefix[4]; /* Prefix for projection parameter keywords */ - char s; /* Co-ordinate version character */ - char sign[2]; /* Fraction's sign character */ - char sup; /* Upper limit on s */ - char type[MXCTYPELEN];/* Buffer for CTYPE value */ - const char *order_kwd; /* Name for SIP max order keyword */ - double ****item; /* Address of FitsStore item to use */ - double cdl; /* CDELT value */ - double fd; /* Fraction of a day */ - double mjd99; /* MJD at start of 1999 */ - double val; /* General purpose value */ - int *tabaxis; /* Flags WCS axes that use -TAB algorithm */ - int i; /* Axis index */ - int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ - int iymdf[ 4 ]; /* Year, month, date, fractional day */ - int j; /* Axis index */ - int jj; /* SlaLib status */ - int m; /* Parameter index */ - int maxm; /* Upper limit on m */ - int naxis; /* Value of NAXIS keyword */ - int nc; /* Length of STYPE string */ - int nwcs; /* No. of WCS axes */ - int ok; /* Frame created succesfully? */ - int order; /* Max SIP polynomial order */ - int p; /* Power of u or U */ - int pmax; /* Max power of u or U */ - int prj; /* Projection type */ - int q; /* Power of v or V */ - int qmax; /* Max power of v or V */ - int ret; /* Returned value */ - -/* Initialise */ - ret = 0; - -/* Other initialisation to avoid compiler warnings. */ - tabaxis = NULL; - -/* Check the inherited status. */ - if( !astOK ) return ret; - -/* If the FitsChan contains a value for the NAXIS keyword, note it. - Otherwise store -1. */ - if( !astGetFitsI( this, "NAXIS", &naxis ) ) naxis = -1; - -/* Find the last WCS related card. */ - FindWcs( this, 1, 1, 0, method, class, status ); - -/* Loop round all co-ordinate versions */ - sup = GetMaxS( &(store->crval), status ); - for( s = ' '; s <= sup && astOK; s++ ){ - -/* For alternate axes, skip this axis description if there is no CRPIX1 or - CRVAL1 value. This avoids partial axis descriptions being written out. */ - if( s != ' ' ) { - if( GetItem( &(store->crpix), 0, 0, s, NULL, method, class, status ) == - AST__BAD || - GetItem( &(store->crval), 0, 0, s, NULL, method, class, status ) == - AST__BAD ) { - ok = 0; - goto next; - } - } - -/* Assume the Frame can be created succesfully. */ - ok = 1; - -/* Save the number of wcs axes. If a value for WCSAXES has been set, or - if the number of axes is not the same as specified in the NAXIS keyword, - store a WCSAXES keyword. */ - val = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - nwcs = (int) ( val + 0.5 ); - } else { - nwcs = GetMaxJM( &(store->crpix), s, status ) + 1; - if( nwcs != 0 && nwcs != naxis ) val = (double) nwcs; - } - if( val != AST__BAD ) { - SetValue( this, FormatKey( "WCSAXES", -1, -1, s, status ), - &nwcs, AST__INT, "Number of WCS axes", status ); - } - -/* Get and save WCSNAME. This is NOT required, so do not return if it is - not available. If the WCS is 1-d, only store WCSNAME if its value is - different to the CTYPE1 value. */ - cval = GetItemC( &(store->wcsname), 0, 0, s, NULL, method, class, status ); - if( cval && nwcs == 1 ) { - comm = GetItemC( &(store->ctype), 0, 0, s, NULL, method, class, status ); - if( comm && Similar( comm, cval, status ) ) cval = NULL; - } - if( cval ) SetValue( this, FormatKey( "WCSNAME", -1, -1, s, status ), &cval, - AST__STRING, "Reference name for the coord. frame", status ); - -/* The prefix for numerical projection parameters is usually "PV". */ - strcpy( parprefix, "PV" ); - -/* Keywords common to all axis types... */ - -/* Get and save CRPIX for all pixel axes. These are required, so pass on - if they are not available. */ - for( i = 0; i < nwcs; i++ ) { - val = GetItem( &(store->crpix), 0, i, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - goto next; - } - sprintf( combuf, "Reference pixel on axis %d", i + 1 ); - SetValue( this, FormatKey( "CRPIX", i + 1, -1, s, status ), &val, AST__FLOAT, - combuf, status ); - } - -/* Get and save CRVAL for all WCS axes. These are required, so - pass on if they are not available. */ - for( i = 0; i < nwcs; i++ ) { - val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - goto next; - } - sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); - SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, AST__FLOAT, - combuf, status ); - } - -/* Allocate memory to indicate if each WCS axis is described by a -TAB - algorithm or not. Initialiss it to zero. */ - tabaxis = astCalloc( nwcs, sizeof( int ) ); - -/* Get and save CTYPE for all WCS axes. These are required, so - pass on if they are not available. */ - for( i = 0; i < nwcs; i++ ) { - cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - if( !cval ) { - ok = 0; - goto next; - } - comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); - if( !comm ) { - sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); - comm = combuf; - } - -/* Extract the projection type as specified by the last 4 characters - in the CTYPE keyword value. This will be AST__WCSBAD for non-celestial - axes. Note, CTYPE can be more than 8 characters long. */ - nc = strlen( cval ); - prj = ( nc > 4 ) ? astWcsPrjType( cval + nc - 4 ) : AST__WCSBAD; - -/* If the projection type is "TPN" (an AST-specific code) convert it to - standard FITS-WCS code "TAN" and change the prefix for projection - parameters from "PV" to "QV". AST will do the inverse conversions when - reading such a header. Non-AST software will simply ignore the QV - terms and interpret the header as a simple TAN projection. */ - if( prj == AST__TPN ) { - strcpy( parprefix, "QV" ); - strcpy( type, cval ); - (void) strcpy( type + nc - 4, "-TAN" ); - cval = type; - } - -/* Note if the axis is described by the -TAB algorithm. */ - tabaxis[ i ] = ( prj == AST__WCSBAD && strlen( cval ) >= 8 && - !strncmp( cval + 4, "-TAB", 4 ) ); - -/* Store the (potentially modified) CTYPE value. */ - SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, AST__STRING, - comm, status ); - } - -/* Get and save CNAME for all WCS axes. These are NOT required, so - do not pass on if they are not available. Do not include a CNAME - keyword if its value equals the commen or value of the corresponding - CTYPE keyword. */ - for( i = 0; i < nwcs; i++ ) { - cval = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status ); - if( cval ) { - comm = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - if( !comm || strcmp( comm, cval ) ) { - comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); - if( !comm || strcmp( comm, cval ) ) { - sprintf( combuf, "Description of axis %d", i + 1 ); - SetValue( this, FormatKey( "CNAME", i + 1, -1, s, status ), &cval, - AST__STRING, combuf, status ); - } - } - } - } - -/* Now choose whether to produce CDi_j or CDELT/PCi_j keywords. */ - if( astGetCDMatrix( this ) ) { - -/* CD matrix. Multiply the row of the PC matrix by the CDELT value. */ - for( i = 0; i < nwcs; i++ ) { - cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - if( cdl == AST__BAD ) cdl = 1.0; - for( j = 0; j < nwcs; j++ ){ - val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; - val *= cdl; - if( val != 0.0 ) { - SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val, - AST__FLOAT, "Transformation matrix element", status ); - } - } - } - -/* If producing PC/CDELT keywords... */ - } else { - -/* CDELT keywords. */ - for( i = 0; i < nwcs; i++ ) { - val = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); - if( val == AST__BAD ) { - ok = 0; - goto next; - } - sprintf( combuf, "Pixel size on axis %d", i + 1 ); - SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), &val, AST__FLOAT, - combuf, status ); - } - -/* PC matrix. */ - for( i = 0; i < nwcs; i++ ) { - for( j = 0; j < nwcs; j++ ){ - val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - if( val != AST__BAD ) { - if( i == j ) { - if( astEQUAL( val, 1.0 ) ) val = AST__BAD; - } else { - if( astEQUAL( val, 0.0 ) ) val = AST__BAD; - } - } - if( val != AST__BAD ) { - SetValue( this, FormatKey( "PC", i + 1, j + 1, s, status ), &val, - AST__FLOAT, "Transformation matrix element", status ); - } - } - } - } - -/* Get and save CUNIT for all WCS axes. These are NOT required, so - do not pass on if they are not available. */ - for( i = 0; i < nwcs; i++ ) { - cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); - if( cval ) { - sprintf( combuf, "Units for axis %d", i + 1 ); - SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING, - combuf, status ); - } - } - -/* Get and save AXREF for all WCS axes. These are NOT required, so do not - pass on if they are not available. Note, AXREF is a non-standard keyword - used by AST to communicate the reference position on any axes described - by the -TAB algorithm and which has no inverse transformation. For all - other cases, the reference position corresponds to the values of CRVAL. */ - for( i = 0; i < nwcs; i++ ) { - val = GetItem( &(store->axref), i, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - sprintf( combuf, "Reference WCS value on axis %d", i + 1 ); - SetValue( this, FormatKey( "AXREF", i + 1, -1, s, status ), &val, AST__FLOAT, - combuf, status ); - } - } - -/* Get and save SREFIS. This is NOT required, so do not return if it is - not available. Note, SREFIS is a non-standard keyword used by AST to - communicate the SkyRefIs attribute in the original SkyFrame. */ - cval = GetItemC( &(store->skyrefis), 0, 0, s, NULL, method, class, status ); - if( cval ) SetValue( this, FormatKey( "SREFIS", -1, -1, s, status ), &cval, - AST__STRING, "Is SkyRef used as pole or origin?", status ); - -/* Get and save SREF for all WCS axes. These are NOT required, so do not - pass on if they are not available. Note, SREF is a non-standard keyword - used by AST to communicate the SkyRef position on any axes described - by a offset SkyFrame. */ - for( i = 0; i < nwcs; i++ ) { - val = GetItem( &(store->skyref), i, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - sprintf( combuf, "Sky reference position on axis %d", i + 1 ); - SetValue( this, FormatKey( "SREF", i + 1, -1, s, status ), &val, AST__FLOAT, - combuf, status ); - } - } - -/* Get and save SREFP for all WCS axes. These are NOT required, so do not - pass on if they are not available. Note, SREFP is a non-standard keyword - used by AST to communicate the SkyRefP position on any axes described - by a offset SkyFrame. */ - for( i = 0; i < nwcs; i++ ) { - val = GetItem( &(store->skyrefp), i, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - sprintf( combuf, "Sky primary meridian position on axis %d", i + 1 ); - SetValue( this, FormatKey( "SREFP", i + 1, -1, s, status ), &val, AST__FLOAT, - combuf, status ); - } - } - -/* Date of observation (only allowed for primary axis descriptions). */ - if( s == ' ' ) { - val = GetItem( &(store->mjdobs), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - SetValue( this, FormatKey( "MJD-OBS", -1, -1, s, status ), - &val, AST__FLOAT, "Modified Julian Date of observation", status ); - -/* The format used for the DATE-OBS keyword depends on the value of the - keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. - Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ - palCaldj( 99, 1, 1, &mjd99, &jj ); - if( val < mjd99 ) { - palDjcal( 0, val, iymdf, &jj ); - sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], - iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); - } else { - palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); - palDd2tf( 3, fd, sign, ihmsf ); - sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", - iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], - ihmsf[2], ihmsf[3] ); - } - -/* Now store the formatted string in the FitsChan. */ - cval = combuf; - SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, - "Date of observation", status ); - } - val = GetItem( &(store->mjdavg), 0, 0, ' ', NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, "MJD-AVG", &val, AST__FLOAT, - "Average Modified Julian Date of observation", status ); - -/* Store the timescale in TIMESYS. */ - cval = GetItemC( &(store->timesys), 0, 0, s, NULL, method, class, status ); - if( cval ) SetValue( this, "TIMESYS", &cval, AST__STRING, - "Timescale for MJD-OBS/MJD-AVG values", status ); - } - -/* Numerical projection parameters */ - maxm = GetMaxJM( &(store->pv), s, status ); - for( i = 0; i < nwcs; i++ ){ - for( m = 0; m <= maxm; m++ ){ - val = GetItem( &(store->pv), i, m, s, NULL, method, class, status ); - if( val != AST__BAD ) { - -/* If the axis uses the "TAB" algorithm, there may be a PVi_4a parameter - in the FitsStore. This is an AST extension to the published -TAB - algorithm, and is used to hold the interpolation method. To avoid - clashing with any standard use of PV1_4a, rename it to QVi_4a. The - default is zero (linear interpolation) so do not write the QV value - if it zero. */ - if( m == 4 && tabaxis[ i ] ) { - if( val != 0.0 ) { - SetValue( this, FormatKey( "QV", i + 1, m, s, status ), - &val, AST__FLOAT, "Use nearest neighbour " - "interpolation", status ); - } - -/* Just store the parameters for other type of axes. */ - } else { - SetValue( this, FormatKey( parprefix, i + 1, m, s, status ), &val, - AST__FLOAT, "Projection parameter", status ); - } - } - } - } - -/* String projection parameters */ - maxm = GetMaxJMC( &(store->ps), s, status ); - for( i = 0; i < nwcs; i++ ){ - for( m = 0; m <= maxm; m++ ){ - cval = GetItemC( &(store->ps), i, m, s, NULL, method, class, status ); - if( cval ) { - SetValue( this, FormatKey( "PS", i + 1, m, s, status ), &cval, - AST__STRING, "Projection parameter", status ); - } - } - } - -/* Keywords specific to celestial axes... */ - -/* Get and save RADESYS. This is NOT required, so do not return if it is - not available. */ - cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); - if( cval ) SetValue( this, FormatKey( "RADESYS", -1, -1, s, status ), &cval, - AST__STRING, "Reference frame for RA/DEC values", status ); - -/* Reference equinox */ - val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "EQUINOX", -1, -1, s, status ), - &val, AST__FLOAT, - "[yr] Epoch of reference equinox", status ); - -/* Latitude of native north pole */ - val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "LATPOLE", -1, -1, s, status ), - &val, AST__FLOAT, - "[deg] Latitude of native north pole", status ); - -/* Longitude of native north pole */ - val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "LONPOLE", -1, -1, s, status ), - &val, AST__FLOAT, - "[deg] Longitude of native north pole", status ); - -/* Keywords specific to spectral axes... */ - -/* SPECSYS - the standard of rest for the spectral axis */ - cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); - if( cval ) SetValue( this, FormatKey( "SPECSYS", -1, -1, s, status ), &cval, - AST__STRING, "Standard of rest for spectral axis", status ); - -/* SSYSSRC - the standard of rest in which ZSOURCE is stored. */ - cval = GetItemC( &(store->ssyssrc), 0, 0, s, NULL, method, class, status ); - if( cval ) SetValue( this, FormatKey( "SSYSSRC", -1, -1, s, status ), &cval, - AST__STRING, "Standard of rest for source redshift", status ); - -/* ZSOURCE - topocentric optical velocity of source */ - val = GetItem( &(store->zsource), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "ZSOURCE", -1, -1, s, status ), - &val, AST__FLOAT, "[] Redshift of source", status ); - -/* VELOSYS - topocentric apparent radial velocity of the standard of rest. */ - val = GetItem( &(store->velosys), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "VELOSYS", -1, -1, s, status ), - &val, AST__FLOAT, "[m/s] Topo. apparent velocity of rest frame", status ); - -/* RESTFRQ - rest frequency */ - val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFRQ", -1, -1, s, status ), - &val, AST__FLOAT, "[Hz] Rest frequency", status ); - -/* RESTWAV - rest wavelength */ - val = GetItem( &(store->restwav), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, FormatKey( "RESTWAV", -1, -1, s, status ), - &val, AST__FLOAT, "[m] Rest wavelength", status ); - -/* The image frequency corresponding to the rest frequency (only used for - double sideband data). This is not part of the FITS-WCS standard but - is added for the benefit of JACH. */ - val = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) { - SetValue( this, "IMAGFREQ", &val, AST__FLOAT, "[Hz] Image frequency", status ); - } - -/* OBSGEO-X/Y/Z - observer's geocentric coords. Note, these always refer - to the primary axes. */ - if( s == ' ' ) { - val = GetItem( &(store->obsgeox), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, "OBSGEO-X", &val, AST__FLOAT, "[m] Observatory geocentric X", status ); - val = GetItem( &(store->obsgeoy), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, "OBSGEO-Y", &val, AST__FLOAT, "[m] Observatory geocentric Y", status ); - val = GetItem( &(store->obsgeoz), 0, 0, s, NULL, method, class, status ); - if( val != AST__BAD ) SetValue( this, "OBSGEO-Z", &val, AST__FLOAT, "[m] Observatory geocentric Z", status ); - } - - -/* Forward SIP distortion keywords. Loop over the two spatial axes. My reading - of the SIP paper is that the SIP distortion *must* be attached to the first - two pixel axes defined by the FITS header. */ - for( i = 0; i < 2; i++ ) { - -/* Get a pointer to the FitsStore item holding the values defining this - output. */ - if( i == 0 ) { - item = &(store->asip); - strcpy( parprefix, "A_" ); - order_kwd = "A_ORDER"; - } else { - item = &(store->bsip); - strcpy( parprefix, "B_" ); - order_kwd = "B_ORDER"; - } - -/* Get the largest powers used of u and v. */ - pmax = GetMaxI( item, s, status ); - qmax = GetMaxJM( item, s, status ); - -/* Loop round all combination of powers. */ - order = 0; - for( p = 0; p <= pmax; p++ ){ - for( q = 0; q <= qmax; q++ ){ - -/* Get the polynomial coefficient for this combination of powers. If it - is good, format the keyword name and store it in the header. */ - val = GetItem( item, p, q, s, NULL, method, class, status ); - if( val != AST__BAD ) { - SetValue( this, FormatKey( parprefix, p, q, s, status ), &val, - AST__FLOAT, "SIP forward distortion coeff", status ); - if( p + q > order ) order = p + q; - } - } - } - if( order > 0 ) SetValue( this, order_kwd, &order, AST__INT, - "SIP max order", status ); - } - -/* Inverse SIP distortion keywords. Loop over the two spatial axes. */ - for( i = 0; i < 2; i++ ) { - -/* Get a pointer to the FitsStore item holding the values defining this - output. */ - if( i == 0 ) { - item = &(store->apsip); - strcpy( parprefix, "AP_" ); - order_kwd = "AP_ORDER"; - } else { - item = &(store->bpsip); - strcpy( parprefix, "BP_" ); - order_kwd = "BP_ORDER"; - } - -/* Get the largest powers used of u and v. */ - pmax = GetMaxI( item, s, status ); - qmax = GetMaxJM( item, s, status ); - -/* Loop round all combination of powers. */ - order = 0; - for( p = 0; p <= pmax; p++ ){ - for( q = 0; q <= qmax; q++ ){ - -/* Get the polynomial coefficient for this combination of powers. If it - is good, format the keyword name and store it in the header. */ - val = GetItem( item, p, q, s, NULL, method, class, status ); - if( val != AST__BAD ) { - SetValue( this, FormatKey( parprefix, p, q, s, status ), &val, - AST__FLOAT, "SIP inverse distortion coeff", status ); - if( p + q > order ) order = p + q; - } - } - } - if( order > 0 ) SetValue( this, order_kwd, &order, AST__INT, - "SIP inverse max order", status ); - } - -/* See if a Frame was sucessfully written to the FitsChan. */ -next: - ok = ok && astOK; - -/* If so, indicate we have something to return. */ - if( ok ) ret = 1; - -/* If we are producing secondary axes, clear any error status so we can - continue to produce the next Frame. Retain the error if the primary axes - could not be produced. After the primary axes, do the A axes. */ - if( s != ' ' ) { - astClearStatus; - } else { - s = 'A' - 1; - } - -/* Remove the secondary "new" flags from the FitsChan. This flag is - associated with cards which have been added to the FitsChan during - this pass through the main loop in this function. If the Frame was - written out succesfully, just clear the flags. If anything went wrong - with this Frame, remove the flagged cards from the FitsChan. */ - FixNew( this, NEW2, !ok, method, class, status ); - -/* Set the current card so that it points to the last WCS-related keyword - in the FitsChan (whether previously read or not). */ - FindWcs( this, 1, 1, 0, method, class, status ); - -/* Free resources. */ - tabaxis = astFree( tabaxis ); - } - -/* Return zero or ret depending on whether an error has occurred. */ - return astOK ? ret : 0; -} - -static AstMapping *WcsIntWorld( AstFitsChan *this, FitsStore *store, char s, - int naxes, const char *method, const char *class, int *status ){ -/* -* Name: -* WcsIntWorld - -* Purpose: -* Create a Mapping from pixel coords to intermediate world coords. - -* Type: -* Private function. - -* Synopsis: -* AstMapping *WcsIntWorld( AstFitsChan *this, FitsStore *store, char s, -* int naxes, const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function interprets the contents of the supplied FitsStore -* structure, and creates a Mapping which describes the transformation -* from pixel coordinates to intermediate world coordinates, using the -* FITS World Coordinate System conventions. This is a general linear -* transformation described by the CRPIXj, PCi_j and CDELTi keywords. - -* Parameters: -* this -* The FitsChan. ASTWARN cards may be added to this FitsChan if any -* anomalies are found in the keyword values in the FitsStore. -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* naxes -* The number of intermediate world coordinate axes (WCSAXES). -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the Mapping. -*/ - -/* Local Variables: */ - AstMapping *mapd1; /* Pointer to first distortion Mapping */ - AstMapping *mapd2; /* Pointer to second distortion Mapping */ - AstMapping *mapd3; /* Pointer to third distortion Mapping */ - AstMapping *mapd4; /* Pointer to fourth distortion Mapping */ - AstMapping *map0; /* Pointer to a Mapping */ - AstMapping *map1; /* Pointer to a Mapping */ - AstMapping *ret; /* Pointer to the returned Mapping */ - -/* Initialise the pointer to the returned Mapping. */ - ret = NULL; - -/* Check the global status. */ - if ( !astOK ) return ret; - -/* First of all, check the CTYPE keywords to see if they contain any known - distortion codes (following the syntax described in FITS-WCS paper IV). - If so, Mappings are returned which represents the distortions to be - applied at each point in the chain of Mappings produced by this function. - Any distortion codes are removed from the CTYPE values in the FitsStore. */ - DistortMaps( this, store, s, naxes, &mapd1, &mapd2, &mapd3, &mapd4, method, - class, status ); - -/* If distortion is to be applied now, initialise the returned Mapping to - be the distortion. */ - if( mapd1 ) ret = mapd1; - -/* Try to create a WinMap which translates the pixel coordinates so - that they are refered to an origin at the reference pixel. This - subtracts the value of CRPIXi from axis i. */ - map1 = (AstMapping *) WcsShift( store, s, naxes, method, class, status ); - -/* Combine this with any previous Mapping. */ - if( ret ) { - map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status ); - ret = astAnnul( ret ); - map1 = astAnnul( map1 ); - ret = map0; - } else { - ret = map1; - } - -/* If distortion is to be applied now, combine the two Mappings. */ - if( mapd2 ) { - map0 = (AstMapping *) astCmpMap( ret, mapd2, 1, "", status ); - ret = astAnnul( ret ); - mapd2 = astAnnul( mapd2 ); - ret = map0; - } - -/* Now try to create a MatrixMap to implement the PC matrix. Combine it - with the above Mapping. Add a Warning if this mapping cannot be inverted. */ - map1 = (AstMapping *) WcsPCMatrix( store, s, naxes, method, class, status ); - if( !astGetTranInverse( map1 ) ) { - Warn( this, "badmat", "The pixel rotation matrix in the original FITS " - "header (specified by CD or PC keywords) could not be inverted. " - "This may be because the matrix contains rows or columns which " - "are entirely zero.", method, class, status ); - } - map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status ); - ret = astAnnul( ret ); - map1 = astAnnul( map1 ); - ret = map0; - -/* If distortion is to be applied now, combine the two Mappings. */ - if( mapd3 ) { - map0 = (AstMapping *) astCmpMap( ret, mapd3, 1, "", status ); - ret = astAnnul( ret ); - mapd3 = astAnnul( mapd3 ); - ret = map0; - } - -/* Now try to create a diagonal MatrixMap to implement the CDELT scaling. - Combine it with the above Mapping. */ - map1 = (AstMapping *) WcsCDeltMatrix( store, s, naxes, method, class, status ); - map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status ); - ret = astAnnul( ret ); - map1 = astAnnul( map1 ); - ret = map0; - -/* If distortion is to be applied now, combine the two Mappings. */ - if( mapd4 ) { - map0 = (AstMapping *) astCmpMap( ret, mapd4, 1, "", status ); - ret = astAnnul( ret ); - mapd4 = astAnnul( mapd4 ); - ret = map0; - } - -/* Return the result. */ - return ret; -} - -static AstMapping *WcsMapFrm( AstFitsChan *this, FitsStore *store, char s, - AstFrame **frm, const char *method, - const char *class, int *status ){ -/* -* Name: -* WcsMapFrm - -* Purpose: -* Create a Mapping and Frame for the WCS transformations described in a -* FITS header. - -* Type: -* Private function. - -* Synopsis: -* AstMapping *WcsMapFrm( AstFitsChan *this, FitsStore *store, char s, -* AstFrame **frm, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function interprets the contents of the supplied FitsStore -* structure, and creates a Mapping which describes the transformation -* from pixel coordinates to world coordinates, using the FITS World -* Coordinate System conventions. It also creates a Frame describing -* the world coordinate axes. - -* Parameters: -* this -* The FitsChan. -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* frm -* The address of a location at which to store a pointer to the -* Frame describing the world coordinate axes. If the Iwc attribute -* is non-zero, then this is actually a FrameSet in which Frame 1 -* is the base Frame and describes the required WCS system. Frame -* 2 is the current Frame and describes the FITS IWC system. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the Mapping. If the Iwc attribute is non-zero, this -* will be the Mapping from pixel to IWC coordinates. Otherwise it -* will be the mapping from pixel to WCS coordinates. -*/ - -/* Local Variables: */ - AstFrame *iwcfrm; /* Frame defining IWC system */ - AstFrameSet *fs; /* Pointer to returned FrameSet */ - AstMapping *map10; /* Pointer to a Mapping */ - AstMapping *map1; /* Pointer to a Mapping */ - AstMapping *map2; /* Pointer to a Mapping */ - AstMapping *map3; /* Pointer to a Mapping */ - AstMapping *map4; /* Pointer to a Mapping */ - AstMapping *map5; /* Pointer to a Mapping */ - AstMapping *map6; /* Pointer to a Mapping */ - AstMapping *map7; /* Pointer to a Mapping */ - AstMapping *map8; /* Pointer to a Mapping */ - AstMapping *map9; /* Pointer to a Mapping */ - AstMapping *ret; /* Pointer to the returned Mapping */ - AstMapping *tabmap; /* Mapping from psi to WCS (paper III - 6.1.2) */ - AstSkyFrame *reffrm; /* SkyFrame defining reflon and reflat */ - char id[2]; /* ID string for returned Frame */ - char iwc[5]; /* Domain name for IWC Frame */ - const char *cc; /* Pointer to Domain */ - double dtai; /* TAI-UTC correction in seconds */ - double dut1; /* UT1-UTC correction in days */ - double dval; /* Temporary double value */ - double reflat; /* Reference celestial latitude */ - double reflon; /* Reference celestial longitude */ - int *tabaxis; /* Flags indicating -TAB axes */ - int wcsaxes; /* Number of physical axes */ - -/* Initialise the pointer to the returned Mapping. */ - ret = NULL; - -/* Check the global status. */ - if ( !astOK ) return ret; - -/* Identify any axes that use the -TAB algoritm code described in FITS-WCS - paper III, and convert their CTYPE values to describe linear axes - (i.e. just remove "-TAB" from the CTYPE value). This also returns a - Mapping (which includes one or more LutMaps) that should be applied to - the resulting linear axis values in order to generate the final WCS - axis values. A NULL pointer is returned if no axes use -TAB. */ - tabmap = TabMapping( this, store, s, &tabaxis, method, class, status ); - -/* Obtain the number of physical axes in the header. If the WCSAXES header - was specified, use it. Otherwise assume it is the same as the number - of pixel axes. */ - dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); - if( dval != AST__BAD ) { - wcsaxes = (int) dval + 0.5; - } else { - wcsaxes = store->naxis; - } - -/* Create a simple Frame to represent IWC coords. */ - iwcfrm = astFrame( wcsaxes, "Title=FITS Intermediate World Coordinates", status ); - strcpy( iwc, "IWC" ); - iwc[ 3 ]= s; - iwc[ 4 ]= 0; - astSetDomain( iwcfrm, iwc ); - -/* Create a simple Frame which will be used as the initial representation - for the physical axes. This Frame will be changed later (or possibly - replaced by a Frame of another class) when we know what type of - physical axes we are dealing with. Set its Domain to "AST_FITSCHAN" - This value is used to identify axes which have not been changed, - and will be replaced before returning the final FrameSet. */ - *frm = astFrame( wcsaxes, "Domain=AST_FITSCHAN", status ); - -/* Store the coordinate version character as the Ident attribute for the - returned Frame. */ - id[ 0 ] = s; - id[ 1 ] = 0; - astSetIdent( *frm, id ); - -/* Create a Mapping which goes from pixel coordinates to what FITS-WCS - paper I calls "intermediate world coordinates". This stage is the same - for all axes. It uses the CRPIXj, PCi_j and CDELTi headers (and - distortion codes from the CTYPE keywords). */ - map1 = WcsIntWorld( this, store, s, wcsaxes, method, class, status ); - -/* The conversion from intermediate world coordinates to the final world - coordinates depends on the type of axis being converted (as specified - by its CTYPE keyword). Check for each type of axis for which known - conventions exist... */ - -/* Celestial coordinate axes. The following call returns a Mapping which - transforms any celestial coordinate axes from intermediate world - coordinates to the final celestial coordinates. Other axes are left - unchanged by the Mapping. It also modifies the Frame so that a - SkyFrame is used to describe the celestial axes. */ - map2 = WcsCelestial( this, store, s, frm, iwcfrm, &reflon, &reflat, - &reffrm, &tabmap, tabaxis, method, class, status ); - -/* Spectral coordinate axes. The following call returns a Mapping which - transforms any spectral coordinate axes from intermediate world - coordinates to the final spectral coordinates. Other axes are left - unchanged by the Mapping. It also modifies the Frame so that a - SpecFrame is used to describe the spectral axes. */ - map3 = WcsSpectral( this, store, s, frm, iwcfrm, reflon, reflat, reffrm, - method, class, status ); - -/* Any axes which were not recognized by the above calls are assumed to - be linear. Create a Mapping which adds on the reference value for such - axes, and modify the Frame to desribe the axes. */ - map4 = WcsOthers( this, store, s, frm, iwcfrm, method, class, status ); - -/* If the Frame still has the Domain "AST_FITSCHAN", clear it. */ - cc = astGetDomain( *frm ); - if( cc && !strcmp( cc, "AST_FITSCHAN" ) ) astClearDomain( *frm ); - -/* Concatenate the Mappings and simplify the result. */ - map5 = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); - map6 = (AstMapping *) astCmpMap( map5, map3, 1, "", status ); - map7 = (AstMapping *) astCmpMap( map6, map4, 1, "", status ); - if( tabmap ) { - map8 = (AstMapping *) astCmpMap( map7, tabmap, 1, "", status ); - } else { - map8 = astClone( map7 ); - } - - ret = astSimplify( map8 ); - -/* Ensure that the coordinate version character is stored as the Ident - attribute for the returned Frame (the above calls may have changed it). */ - astSetIdent( *frm, id ); - -/* Set the DUT1 value. Note, the JACH store DUT1 in units of days in their - FITS headers, so convert from days to seconds. May need to do somthing - about this if the forthcoming FITS-WCS paper 5 (time axes) defines DUT1 - to be in seconds. */ - dut1 = GetItem( &(store->dut1), 0, 0, s, NULL, method, class, status ); - if( dut1 != AST__BAD ) astSetDut1( *frm, dut1*SPD ); - -/* Set the DTAI value. */ - dtai = GetItem( &(store->dtai), 0, 0, s, NULL, method, class, status ); - if( dtai != AST__BAD ) astSetDtai( *frm, dtai ); - -/* The returned Frame is actually a FrameSet in which the base (first) Frame - is the required WCS Frame. The FrameSet contains one other Frame, - which is the Frame representing IWC and is always frame 2, the current - Frame. Create a FrameSet containing these two Frames. */ - if( astGetIwc( this ) ) { - fs = astFrameSet( *frm, "", status ); - astInvert( ret ); - map9 = (AstMapping *) astCmpMap( ret, map1, 1, "", status ); - astInvert( ret ); - map10 = astSimplify( map9 ); - astAddFrame( fs, AST__BASE, map10, iwcfrm ); - -/* Return this FrameSet instead of the Frame. */ - *frm = astAnnul( *frm ); - *frm = (AstFrame *) fs; - -/* Free resources */ - map9 = astAnnul( map9 ); - map10 = astAnnul( map10 ); - -/* Also modify the returned mapping so that it goes from pixel to iwc - instead of to wcs. */ - ret = astAnnul( ret ); - ret = astClone( map1 ); - } - -/* Annull temporary resources. */ - if( reffrm ) reffrm = astAnnul( reffrm ); - if( tabmap ) tabmap = astAnnul( tabmap ); - tabaxis = astFree( tabaxis ); - iwcfrm = astAnnul( iwcfrm ); - map1 = astAnnul( map1 ); - map2 = astAnnul( map2 ); - map3 = astAnnul( map3 ); - map4 = astAnnul( map4 ); - map5 = astAnnul( map5 ); - map6 = astAnnul( map6 ); - map7 = astAnnul( map7 ); - map8 = astAnnul( map8 ); - -/* Annul thre returned objects if an error has occurred. */ - if( !astOK ) { - ret = astAnnul( ret ); - *frm = astAnnul( *frm ); - } - -/* Return the result. */ - return ret; -} - -static AstMatrixMap *WcsPCMatrix( FitsStore *store, char s, int naxes, - const char *method, const char *class, int *status ){ -/* -* Name: -* WcsPCMatrix - -* Purpose: -* Create a MatrixMap representing the PC matrix. - -* Type: -* Private function. - -* Synopsis: -* AstMatrixMap *WcsPCMatrix( FitsStore *store, char s, int naxes, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A MatrixMap representing the FITS "PC" matrix is returned. - -* Parameters: -* store -* A structure containing values for FITS keywords relating to -* the World Coordinate System. -* s -* A character s identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* naxes -* The number of intermediate world coordinate axes (WCSAXES). -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the created MatrixMap or a NULL pointer if an -* error occurred. -*/ - -/* Local Variables: */ - AstMatrixMap *new; /* The created MatrixMap */ - double *el; /* Pointer to next matrix element */ - double *mat; /* Pointer to matrix array */ - int i; /* Pixel axis index */ - int j; /* Intermediate axis index. */ - -/* Initialise/ */ - new = NULL; - -/* Check the global status. */ - if ( !astOK ) return new; - -/* Allocate memory for the matrix. */ - mat = (double *) astMalloc( sizeof(double)*naxes*naxes ); - if( astOK ){ - -/* Fill the matrix with values from the FitsStore. */ - el = mat; - for( i = 0; i < naxes; i++ ){ - for( j = 0; j < naxes; j++ ){ - -/* Get the PCj_i value for this axis. Missing terms can be defaulted so - do not report an error if the required value is not present in the - FitsStore. */ - *el = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); - -/* Diagonal terms default to to 1.0, off-diagonal to zero. */ - if( *el == AST__BAD ) *el = ( i == j ) ? 1.0: 0.0; - -/* Move on to the next matrix element. */ - el++; - } - } - -/* Create the matrix. */ - new = astMatrixMap( naxes, naxes, 0, mat, "", status ); - -/* Report an error if the inverse transformation is undefined. */ - if( !astGetTranInverse( new ) && astOK ) { - astError( AST__BDFTS, "%s(%s): Unusable rotation matrix (PC or CD) found " - "in the FITS-WCS header - the matrix cannot be inverted.", status, method, class ); - } - -/* Release the memory used to hold the matrix. */ - mat = (double *) astFree( (void *) mat ); - } - -/* If an error has occurred, attempt to annul the returned MatrixMap. */ - if( !astOK ) new = astAnnul( new ); - -/* Return the MatrixMap. */ - return new; -} - -static AstMapping *WcsNative( AstFitsChan *this, FitsStore *store, char s, - AstWcsMap *wcsmap, int fits_ilon, int fits_ilat, - const char *method, const char *class, int *status ){ -/* -* Name: -* WcsNative - -* Purpose: -* Create a CmpMap which transforms Native Spherical Coords to -* Celestial Coords. - -* Type: -* Private function. - -* Synopsis: -* AstMapping *WcsNative( AstFitsChan *this, FitsStore *store, char s, -* AstWcsMap *wcsmap, int fits_ilon, int fits_ilat, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A CmpMap is created which rotates the supplied Native Spherical Coords -* into Celestial Coords in the standard system specified by the CTYPE -* keywords. Any non-celestial axes are left unchanged. -* -* At the highest level, the returned CmpMap is made up of the following - -* Mappings in series (if celestial long/lat axes are present): -* 1 - A PermMap which rearranges the axes so that the longitude axis is -* axis 0, the latitude axis is axis 1, and all other axes are -* stored at higher indices, starting at axis 2. -* 2 - A CmpMap which converts the values on axes 0 and 1 from Native -* Spherical to Celestial coordinates, leaving all other axes -* unchanged. -* 3 - A PermMap which rearranges the axes to put the longitude and -* latitude axes back in their original places. This is just the -* inverse of the PermMap used at stage 1 above. -* -* The CmpMap used at stage 2 above, is made up of two Mappings in - -* parallel: -* 4 - A CmpMap which maps axes 0 and 1 from Native Spherical to -* Celestial coordinates. -* 5 - A UnitMap which passes on the values to axes 2, 3, etc, -* without change. -* -* The CmpMap used at stage 4 above, is made up of the following Mappings - -* in series: -* 6 - A SphMap which converts the supplied spherical coordinates into -* Cartesian Coordinates. -* 7 - A MatrixMap which rotates the Cartesian coordinates from the -* Native to the Celestial system. -* 8 - A SphMap which converts the resulting Cartesian coordinates back -* to spherical coordinates. - -* Parameters: -* this -* The FitsChan in which to store any warning cards. If NULL, no -* warnings are stored. -* store -* A structure containing values for FITS keywords relating to -* the World Coordinate System. -* s -* Co-ordinate version character to use (space means primary axes). -* wcsmap -* A mapping describing the deprojection which is being used. This is -* needed in order to be able to locate the fiducial point within the -* Native Speherical Coordinate system, since it varies from projection -* to projection. -* fits_ilon -* The zero-based FITS WCS axis index corresponding to celestial -* longitude (i.e. one less than the value of "i" in the keyword -* names "CTYPEi", "CRVALi", etc). If -1 is supplied, the index of -* the longitude axis in the supplied WcsMap is used. -* fits_ilat -* The zero-based FITS WCS axis index corresponding to celestial -* latitude (i.e. one less than the value of "i" in the keyword -* names "CTYPEi", "CRVALi", etc). If -1 is supplied, the index of -* the latitude axis in the supplied WcsMap is used. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the created CmpMap or a NULL pointer if an error occurred. - -* Notes: -* - The local variable names correspond to the notation in the papers -* by Greisen & Calabretta describing the FITS WCS system. -*/ - -/* Local Variables: */ - AstCmpMap *cmpmap; /* A CmpMap */ - AstMapping *new; /* The returned CmpMap */ - AstMatrixMap *matmap2; /* Another MatrixMap */ - AstMatrixMap *matmap; /* A MatrixMap */ - AstPermMap *permmap; /* A PermMap */ - AstSphMap *sphmap; /* A SphMap */ - AstUnitMap *unitmap; /* A UnitMap */ - char buf[150]; /* Message buffer */ - double alpha0; /* Long. of fiduaicl point in standard system */ - double alphap; /* Long. of native nth pole in standard system */ - double axis[3]; /* Vector giving the axis of rotation */ - double delta0; /* Lat. of fiducial point in standard system */ - double deltap; /* Lat. of native nth pole in standard system */ - double latpole; /* Lat. of native nth pole in standard system if deltap undefined */ - double phip; /* Long. of standard nth pole in native system */ - double phi0; /* Native longitude at fiducial point */ - double theta0; /* Native latitude at fiducial point */ - int *inperm; /* Pointer to array of output axis indices */ - int *outperm; /* Pointer to array of input axis indices */ - int axlat; /* Index of latitude physical axis */ - int axlon; /* Index of longitude physical axis */ - int i; /* Loop count */ - int nax_rem; /* No. of non-astrometric axes */ - int naxis; /* No. of axes. */ - int new_axlat; /* Index of lat. physical axis after perming */ - int tpn; /* Is this a TPN projection? */ - -/* Check the global status. */ - if ( !astOK ) return NULL; - -/* Initialise the returned CmpMap pointer. */ - new = NULL; - -/* Store the number of axes in a local variable. */ - naxis = astGetNin( wcsmap ); - -/* Get the indices of the celestial axes. */ - axlon = astGetWcsAxis( wcsmap, 0 ); - axlat = astGetWcsAxis( wcsmap, 1 ); - -/* If the corresponding FITS axis indices were not supplied, use the - WcsMap axes found above. */ - if( fits_ilon == -1 ) fits_ilon = axlon; - if( fits_ilat == -1 ) fits_ilat = axlat; - -/* If there is no longitude or latitude axis, or if we have a - non-celestial projection, just return a UnitMap. */ - if( axlon == axlat || astGetWcsType( wcsmap ) == AST__WCSBAD ){ - new = (AstMapping *) astUnitMap( naxis, "", status ); - -/* If there is a lon/lat axis pair, create the inperm and outperm arrays - which will be needed later to create the PermMap which reorganises - the axes so that axis zero is the longitude axis and axis 1 is the - latitude axis. */ - } else { - -/* Get storage for the two arrays. */ - inperm = (int *) astMalloc( sizeof( int )*(size_t)naxis ); - outperm = (int *) astMalloc( sizeof( int )*(size_t)naxis ); - if( astOK ){ - -/* Initialise an array holding the indices of the input axes which are copied - to each output axis. Initially assume that there is no re-arranging of - the axes. */ - for( i = 0; i < naxis; i++ ) outperm[ i ] = i; - -/* Swap the longitude axis and axis 0. */ - i = outperm[ axlon ]; - outperm[ axlon ] = outperm[ 0 ]; - outperm[ 0 ] = i; - -/* If axis 0 was originally the latitude axis, the latitude axis will now - be where the longitude axis was originally (because of the above axis - swap). */ - if( axlat == 0 ) { - new_axlat = axlon; - } else { - new_axlat = axlat; - } - -/* Swap the latitude axis and axis 1. */ - i = outperm[ new_axlat ]; - outperm[ new_axlat ] = outperm[ 1 ]; - outperm[ 1 ] = i; - -/* Create the array holding the output axis index corresponding to - each input axis. */ - for( i = 0; i < naxis; i++ ) inperm[ outperm[ i ] ] = i; - } - -/* Store the latitude and longitude (in the standard system) of the fiducial - point, in radians. */ - delta0 = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status ); - if( delta0 == AST__BAD ) delta0 = 0.0; - delta0 *= AST__DD2R; - alpha0 = GetItem( &(store->crval), fits_ilon, 0, s, NULL, method, class, status ); - if( alpha0 == AST__BAD ) alpha0 = 0.0; - alpha0 *= AST__DD2R; - -/* Limit the latitude to the range +/- PI/2, issuing a warning if the - supplied CRVAL value is outside this range. The "alphap" variable is used - as workspace here. */ - alphap = palDrange( delta0 ); - delta0 = alphap; - if ( delta0 > AST__DPIBY2 ){ - delta0 = AST__DPIBY2; - } else if ( delta0 < -AST__DPIBY2 ){ - delta0 = -AST__DPIBY2; - } - if( alphap != delta0 ) { - sprintf( buf, "The original FITS header specified a fiducial " - "point with latitude %.*g. A value of %.*g is being used " - "instead. ", AST__DBL_DIG, alphap*AST__DR2D, AST__DBL_DIG, - delta0*AST__DR2D ); - Warn( this, "badlat", buf, method, class, status ); - } - -/* Set a flag indicating if we have a TPN projection. The handling or - projection parameters is different for TPN projections. */ - tpn = ( astGetWcsType( wcsmap ) == AST__TPN ); - -/* Store the radian values of the FITS keywords LONPOLE and LATPOLE. Defaults - will be used if either of these items was not supplied. These keyword - values may be stored in projection parameters PVi_3a and PVi_4a for - longitude axis "i" - in which case the "PV" values take precedence over - the "LONPOLE" and "LATPOLE" values. Do not do this for TPN projections - since they use these projection parameters to specify correcton terms. */ - if( astTestPV( wcsmap, axlon, 3 ) && !tpn ) { - phip = astGetPV( wcsmap, axlon, 3 ); - } else { - phip = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); - if( phip != AST__BAD && !tpn ) astSetPV( wcsmap, axlon, 3, phip ); - } - if( phip != AST__BAD ) phip *= AST__DD2R; - if( astTestPV( wcsmap, axlon, 4 ) && !tpn ) { - latpole = astGetPV( wcsmap, axlon, 4 ); - } else { - latpole = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ); - if( latpole != AST__BAD && !tpn ) astSetPV( wcsmap, axlon, 4, latpole ); - } - if( latpole != AST__BAD ) latpole *= AST__DD2R; - -/* Find the standard Celestial Coordinates of the north pole of the Native - Spherical Coordinate system. Report an error if the position was not - defined. */ - if( !WcsNatPole( this, wcsmap, alpha0, delta0, latpole, &phip, &alphap, - &deltap, status ) && astOK ){ - astError( AST__BDFTS, "%s(%s): Conversion from FITS WCS native " - "coordinates to celestial coordinates is ill-conditioned.", status, - method, class ); - } - -/* Create the SphMap which converts spherical coordinates to Cartesian - coordinates (stage 6 in the prologue). This asumes that axis 0 is the - longitude axis, and axis 1 is the latitude axis. This will be ensured - by a PermMap created later. Indicate that the SphMap will only be used - to transform points on a unit sphere. This enables a forward SphMap - to be combined with an inverse SphMap into a UnitMap, and thus aids - simplification. */ - sphmap = astSphMap( "UnitRadius=1", status ); - astInvert( sphmap ); - -/* Set the PolarLong attribute of the SphMap so that a longitude of phi0 (the - native longitude of the fiducial point) is returned by the inverse - transformation (cartesian->spherical) at either pole. */ - GetFiducialNSC( wcsmap, &phi0, &theta0, status ); - astSetPolarLong( sphmap, phi0 ); - -/* Create a unit MatrixMap to be the basis of the MatrixMap which rotates - Native Spherical Coords to Celestial Coords (stage 7 in the prologue). */ - matmap = astMatrixMap( 3, 3, 2, NULL, "", status ); - -/* Modify the above MatrixMap so that it rotates the Cartesian position vectors - by -phip (i.e. LONPOLE) about the Z axis. This puts the north pole of the - standard system at zero longitude in the rotated system. Then annul the - original MatrixMap and use the new one instead. */ - axis[ 0 ] = 0; - axis[ 1 ] = 0; - axis[ 2 ] = 1; - matmap2 = astMtrRot( matmap, -phip, axis ); - matmap = astAnnul( matmap ); - matmap = matmap2; - -/* Now modify the above MatrixMap so that it rotates the Cartesian position - vectors by -(PI/2-deltap) about the Y axis. This puts the north pole of - the standard system as 90 degrees latitude in the rotated system. Then annul - the original MatrixMap and use the new one instead. */ - axis[ 0 ] = 0; - axis[ 1 ] = 1; - axis[ 2 ] = 0; - matmap2 = astMtrRot( matmap, deltap - AST__DPIBY2, axis ); - matmap = astAnnul( matmap ); - matmap = matmap2; - -/* Finally modify the above MatrixMap so that it rotates the Cartesian position - vectors (PI+alphap) about the Z axis. This puts the primary meridian of the - standard system at zero longitude in the rotated system. This results in the - rotated system being coincident with the standard system. */ - axis[ 0 ] = 0; - axis[ 1 ] = 0; - axis[ 2 ] = 1; - matmap2 = astMtrRot( matmap, AST__DPI + alphap, axis ); - matmap = astAnnul( matmap ); - matmap = matmap2; - -/* Combine the SphMap (stage 6) and MatrixMap (stage 7) in series. */ - cmpmap = astCmpMap( sphmap, matmap, 1, "", status ); - sphmap = astAnnul( sphmap ); - matmap = astAnnul( matmap ); - -/* Create a new SphMap which converts Cartesian coordinates to spherical - coordinates (stage 8 in the prologue). Indicate that the SphMap will - only be used to transform points on a unit sphere. */ - sphmap = astSphMap( "UnitRadius=1", status ); - -/* Set the PolarLong attribute of the SphMap so that a longitude of alpha0 - (the celestial longitude of the fiducial point) is returned by the - forward transformation (cartesian->spherical) at either pole. */ - astSetPolarLong( sphmap, alpha0 ); - -/* Add it to the compound mapping. The CmpMap then corresponds to stage 4 - in the prologue. Annul the constituent mappings. */ - new = (AstMapping *) astCmpMap( cmpmap, sphmap, 1, "", status ); - cmpmap = astAnnul( cmpmap ); - sphmap = astAnnul( sphmap ); - -/* If there are any remaining axes (i.e. axes which do not describe a - Celestial coordinate system), create a UnitMap which passes on their - values unchanged (stage 5 in the prologue), and add it the CmpMap, - putting it in parallel with the earlier mappings. The resulting CmpMap - then corresponds to stage 2 in the prologue. Note, the axis numbering - used by this UnitMap needs to take account of the fact that it is only - applied to the non-celestial axes. The axes are re-ordered by the - PermMap described at stage 1 in the prologue. */ - nax_rem = naxis - 2; - if( nax_rem > 0 ){ - unitmap = astUnitMap( nax_rem, "", status ); - cmpmap = astCmpMap( new, unitmap, 0, "", status ); - new = astAnnul( new ); - unitmap = astAnnul( unitmap ); - new = (AstMapping *) cmpmap; - } - -/* Now we need to ensure that axes 0 and 1 correspond to longitude and - latitude. If this is already the case, then the CmpMap can be returned - as it is. Otherwise, a PermMap needs to be created to rearrange the - axes. */ - if( axlon != 0 || axlat != 1 ){ - -/* Create the PermMap using the inperm and outperm arrays created earlier. - This is the mapping described as stage 1 in the prologue. */ - permmap = astPermMap( naxis, inperm, naxis, outperm, NULL, "", status ); - -/* Compound this PermMap and the CmpMap corresponding to stage 2 (created - earlier) in series. */ - cmpmap = astCmpMap( permmap, new, 1, "", status ); - new = astAnnul( new ); - new = (AstMapping *) cmpmap; - -/* Now invert the PermMap, so that it re-arranges the axes back into - their original order. This is the mapping described as stage 3 in - the prologue. */ - astInvert( permmap ); - -/* And finally.... add this inverted PermMap onto the end of the CmpMap. */ - cmpmap = astCmpMap( new, permmap, 1, "", status ); - permmap = astAnnul( permmap ); - new = astAnnul( new ); - new = (AstMapping *) cmpmap; - } - -/* Free the temporary arrays. */ - inperm = (int *) astFree( (void *) inperm ); - outperm = (int *) astFree( (void *) outperm ); - } - -/* If an error has occurred, attempt to annul the new CmpMap. */ - if( !astOK ) new = astAnnul( new ); - -/* Return the CmpMap. */ - return new; -} - -static int WcsNatPole( AstFitsChan *this, AstWcsMap *wcsmap, double alpha0, - double delta0, double latpole, double *phip, - double *alphap, double *deltap, int *status ){ - -/* -* Name: -* WcsNatPole - -* Purpose: -* Find the celestial coordinates of the north pole of the Native Spherical -* Coordinate system. - -* Type: -* Private function. - -* Synopsis: - -* int WcsNatPole( AstFitsChan *this, AstWcsMap *wcsmap, double alpha0, -* double delta0, double latpole, double *phip, -* double *alphap, double *deltap, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* The supplied WcsMap converts projected positions given in -* "Projection Plane Coords" to positions in the "Native Spherical -* Coordinate" system. This function finds the pole of this spherical -* coordinate system in terms of the standard celestial coordinate -* system to which the CRVALi, LONPOLE and LATPOLE keywords refer (this -* system should be identified by characters 5-8 of the CTYPEi -* keywords). It also supplies a default value for LONPOLE if no value -* has been supplied explicitly in the FITS header. -* -* This function implements equations 8, 9 and 10 from the FITS-WCS paper -* II by Calabretta & Greisen (plus the associated treatment of special -* cases). The paper provides more detailed documentation for the -* mathematics implemented by this function. - -* Parameters: -* this -* The FitsChan in which to store any warning cards. If NULL, no -* warnings are stored. -* wcsmap -* A mapping describing the deprojection being used (i.e. the -* mapping from Projection Plane Coords to Native Spherical Coords). -* alpha0 -* The longitude of the fiducial point in the standard celestial -* coordinate frame (in radians). Note, this fiducial point does -* not necessarily correspond to the point given by keywords CRPIXj. -* delta0 -* The celestial latitude (radians) of the fiducial point. -* latpole -* The value of FITS keyword LATPOLE, converted to radians, or the -* symbolic constant AST__BAD if the keyword was not supplied. -* phip -* Pointer to a location at which is stored the longitude of the north -* pole of the standard Celestial coordinate system, as measured in -* the Native Spherical Coordinate system, in radians. This should be -* supplied holding the radian equivalent of the value of the FITS -* keyword LONPOLE, or the symbolic constant AST__BAD if the keyword was -* not supplied (in which case a default value will be returned at the -* given location). -* alphap -* Pointer to a location at which to store the calculated longitude -* of the Native North Pole, in radians. -* deltap -* Pointer to a location at which to store the calculated latitude -* of the Native North Pole, in radians. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A status: non-zero for success, zero if the position of the native -* north pole is undefined. - -* Notes: -* - Certain combinations of keyword values result in the latitude of -* the Native North Pole being undefined. In these cases, a value of -* 0 is returned for the function value, but no error is reported. -* - All angular values used by this function are in radians. -* - A value of 0 is returned if an error has already occurred. -*/ - -/* Local Variables: */ - char buf[150]; /* Buffer for warning message */ - double cos_theta0; /* Cosine of theta0 */ - double cos_phip; /* Cosine of (phip - phi0) */ - double cos_delta0; /* Cosine of delta0 */ - double cos_deltap; /* Cosine of deltap */ - double deltap_1; /* First possible value for deltap */ - double deltap_2; /* Second possible value for deltap */ - double sin_theta0; /* Sine of theta0 */ - double sin_phip; /* Sine of (phip - phi0) */ - double sin_delta0; /* Sine of delta0 */ - double sin_deltap; /* Sine of deltap */ - double t0, t1, t2, t3, t4; /* Intermediate values */ - double phi0, theta0; /* Native coords of fiducial point */ - -/* Check the global status. */ - if ( !astOK ) return 0; - -/* Get the longitude and latitude of the fiducial point in the native - spherical coordinate frame (in radians). */ - GetFiducialNSC( wcsmap, &phi0, &theta0, status ); - -/* If no value was supplied for the FITS keyword LONPOLE, set up a default - value such that the celestial latitude increases in the same direction - as the native latitude at the fiducial; point. */ - if( *phip == AST__BAD ){ - if( delta0 >= theta0 ){ - *phip = 0.0; - } else { - *phip = AST__DPI; - } - -/* Issue a warning that a default lonpole value has been adopted. */ - sprintf( buf, "The original FITS header did not specify the " - "longitude of the native north pole. A default value " - "of %.8g degrees was assumed.", (*phip)*AST__DR2D ); - Warn( this, "nolonpole", buf, "astRead", "FitsChan", status ); - } - -/* If the fiducial point is coincident with the Native North Pole, then the - Native North Pole must have the same coordinates as the fiducial - point. Tests for equality include some tolerance to allow for rounding - errors. */ - sin_theta0 = sin( theta0 ); - if( astEQUAL( sin_theta0, 1.0 ) ){ - *alphap = alpha0; - *deltap = delta0; - -/* If the fiducial point is concident with the Native South Pole, then the - Native North Pole must have the coordinates of the point diametrically - opposite the fiducial point. */ - } else if( astEQUAL( sin_theta0, -1.0 ) ){ - *alphap = alpha0 + AST__DPI; - *deltap = -delta0; - -/* For all other cases, go through the procedure described in the WCS paper - by Greisen & Calabretta, to find the position of the Native North Pole. - First store some useful values. */ - } else { - cos_theta0 = cos( theta0 ); - cos_delta0 = cos( delta0 ); - cos_phip = cos( *phip - phi0 ); - sin_delta0 = sin( delta0 ); - sin_phip = sin( *phip - phi0 ); - -/* Next, find the two possible values for the latitude of the Native - North Pole (deltap). If any stage of this transformation is - indeterminate, return zero (except for the single special case noted - in item 6 para. 2 of the WCS paper, for which LATPOLE specifies the - values to be used). */ - t0 = cos_theta0*cos_phip; - if( fabs( t0 ) < TOL2 && fabs( sin_theta0 ) < TOL2 ){ - if( latpole != AST__BAD ) { - *deltap = latpole; - } else { - return 0; - } - } else { - t1 = atan2( sin_theta0, t0 ); - t2 = cos_theta0*cos_phip; - t2 *= t2; - t2 += sin_theta0*sin_theta0; - if( t2 <= DBL_MIN ){ - return 0; - } else { - t3 = sin_delta0/sqrt( t2 ); - if( fabs( t3 ) > 1.0 + TOL1 ){ - return 0; - } else { - if( t3 < -1.0 ){ - t4 = AST__DPI; - } else if( t3 > 1.0 ){ - t4 = 0.0; - } else { - t4 = acos( t3 ); - } - deltap_1 = palDrange( t1 + t4 ); - deltap_2 = palDrange( t1 - t4 ); - -/* Select which of these two values of deltap to use. Values outside the - range +/- PI/2 cannot be used. If both values are within this range - use the value which is closest to the supplied value of latpole (or - use the northern most value if the LATPOLE keyword was not supplied. */ - if( fabs( deltap_1 ) > AST__DPIBY2 + TOL2 ){ - *deltap = deltap_2; - } else if( fabs( deltap_2 ) > AST__DPIBY2 + TOL2 ){ - *deltap = deltap_1; - } else { - if( latpole != AST__BAD ){ - if( fabs( deltap_1 - latpole ) < - fabs( deltap_2 - latpole ) ){ - *deltap = deltap_1; - } else { - *deltap = deltap_2; - } - } else { - if( deltap_1 > deltap_2 ){ - *deltap = deltap_1; - } else { - *deltap = deltap_2; - } - -/* Issue a warning that a default latpole value has been adopted. */ - sprintf( buf, "The original FITS header did not specify " - "the latitude of the native north pole. A " - "default value of %.8g degrees was assumed.", - (*deltap)*AST__DR2D ); - Warn( this, "nolatpole", buf, "astRead", "FitsChan", status ); - } - } - if( fabs( *deltap ) > AST__DPIBY2 + TOL2 ) { - return 0; - } else if( *deltap < -AST__DPIBY2 ){ - *deltap = -AST__DPIBY2; - } else if( *deltap > AST__DPIBY2 ){ - *deltap = AST__DPIBY2; - } - } - } - } - -/* If a valid value for the latitude (deltap) has been found, find the - longitude of the Native North Pole. */ - if( *deltap != AST__BAD ) { - if( fabs( cos_delta0) > TOL2 ){ - cos_deltap = cos( *deltap ); - sin_deltap = sin( *deltap ); - if( fabs( cos_deltap ) > TOL2 ){ - t1 = sin_phip*cos_theta0/cos_delta0; - t2 = ( sin_theta0 - sin_deltap*sin_delta0 ) - /( cos_delta0*cos_deltap ); - if( ( fabs( t1 ) > TOL2 ) || ( fabs( t2 ) > TOL2 ) ){ - *alphap = alpha0 - atan2( t1, t2 ); - } else { - *alphap = alpha0; - } - } else if( sin_deltap > 0.0 ){ - *alphap = alpha0 + (*phip - phi0) - AST__DPI; - } else { - *alphap = alpha0 - (*phip - phi0); - } - } else { - *alphap = alpha0; - } - } else { - *alphap = AST__BAD; - } - } - -/* Return a success status if valid latitude and longitude values were - found. */ - return (*deltap) != AST__BAD && (*alphap) != AST__BAD ; -} - -static AstMapping *WcsOthers( AstFitsChan *this, FitsStore *store, char s, - AstFrame **frm, AstFrame *iwcfrm, const char *method, - const char *class, int *status ){ - -/* -* Name: -* WcsOthers - -* Purpose: -* Create a Mapping from intermediate world coords to any axes -* which are not covered by specialised conventions. - -* Type: -* Private function. - -* Synopsis: - -* AstMapping *WcsOthers( AstFitsChan *this, FitsStore *store, char s, -* AstFrame **frm, AstFrame *iwcfrm, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function interprets the contents of the supplied FitsStore -* structure, looking for world coordinate axes for which no -* description has yet been added to the supplied Frame . It is -* assumed that any such axes are simple linear axes. It returns a -* Mapping which simply adds on the CRVAL values to such axes. -* It also modifies the supplied Frame to describe the axes. - -* Parameters: -* this -* The FitsChan. -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* frm -* The address of a location at which to store a pointer to the -* Frame describing the world coordinate axes. -* iwcfrm -* A pointer to the Frame describing the intermediate world coordinate -* axes. The properties of this Frame may be changed on exit. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the Mapping. -*/ - -/* Local Variables: */ - AstFrame *pfrm; /* Pointer to primary Frame */ - AstFrame *pfrm2; /* Pointer to primary Frame */ - AstMapping *map1; /* Pointer to a Mapping */ - AstMapping *map2; /* Pointer to a Mapping */ - AstMapping *ret; /* The returned Mapping */ - char **comms; /* Pointer to array of CTYPE commments */ - char buf[ 100 ]; /* Buffer for textual attribute value */ - char buf2[ 100 ]; /* Buffer for textual attribute value */ - char buf3[ 20 ]; /* Buffer for default CTYPE value */ - char *newdom; /* Pointer to new Domain value */ - const char *ckeyval; /* Pointer to character keyword value */ - int i; /* Axis index */ - int j; /* Axis index */ - int len; /* Used length of string */ - int naxes; /* no. of axes in Frame */ - int nother; /* The number of "other" axes */ - int paxis; /* Primary axis index */ - int usecom; /* Use CTYPE comments as axis Labels? */ - -/* Initialise the pointer to the returned Mapping. */ - ret = NULL; - -/* Check the global status. */ - if ( !astOK ) return ret; - -/* Get the number of physical axes. */ - naxes = astGetNaxes( *frm ); - -/* Assume we will use CTYPE comments as the axis labels. */ - usecom = 1; - -/* Initialise the count of "other" axes. */ - nother = 0; - -/* Get the comments associated with the CTYPE keywords for all "other" - axes. */ - comms = astMalloc( naxes*sizeof( char * ) ); - if( comms ) { - -/* Loop round all axes in the Frame, and initialise the pointer to its - comment. */ - for( i = 0; i < naxes; i++ ){ - comms[ i ] = NULL; - -/* Get the Domain for the primary frame containing the axis. This will be - "AST_FITSCHAN" if the axis has not yet been recognised (this Domain is - set up by WcsMapFrm). Only consider the axis further if the Domain has - not been changed. */ - astPrimaryFrame( *frm, i, &pfrm, &paxis ); - if( !strcmp( astGetDomain( pfrm ), "AST_FITSCHAN" ) ) { - -/* Increment the count of "other" axes. */ - nother++; - -/* Get the comment associated with the CTYPE header. */ - ckeyval = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); - -/* If this axis has no CTYPE comment, we will use CTYPE values as axis - labels (if given, the CNAME keyword take precedence). */ - if( !ckeyval || astChrLen( ckeyval ) == 0 ) { - usecom = 0; - -/* If the CTYPE comment for this axis is the same as any other comment, we - will use CTYPE values as axis labels. */ - } else { - for( j = 0; j < nother - 1; j++ ) { - if( comms[ j ] && !strcmp( ckeyval, comms[ j ] ) ) { - usecom = 0; - break; - } - } - } - -/* If we are still using comments as axis labels, store a copy of it in the - workspace. */ - if( usecom ) comms[ i ] = astStore( NULL, ckeyval, - strlen( ckeyval ) + 1 ); - } - pfrm = astAnnul( pfrm ); - } - -/* Free the workspace holding comments. */ - for( i = 0; i < naxes; i++ ) comms[ i ] = astFree( comms[ i ] ); - comms = astFree( comms ); - } - -/* If there are no "other" axes, just return a UnitMap. */ - if( nother == 0 ) { - ret = (AstMapping *) astUnitMap( naxes, "", status ); - -/* Otherwise... */ - } else { - -/* If we have only a single other axis, use CTYPE value instead of - comment. */ - if( nother == 1 ) usecom = 0; - -/* Not yet started a new Domain value to replace "AST_FITSCHAN". */ - newdom = NULL; - pfrm2 = NULL; - -/* Check each axis of the Frame looking for axes which have not yet been - recognised. */ - for( i = 0; i < naxes; i++ ) { - -/* Get the Domain for the primary frame containing the axis. This will be - "AST_FITSCHAN" if the axis has not yet been recognised (this Domain is - set up by WcsMapFrm). Only consider the axis further if the Domain has - not been changed. */ - astPrimaryFrame( *frm, i, &pfrm, &paxis ); - if( !strcmp( astGetDomain( pfrm ), "AST_FITSCHAN" ) ) { - -/* Save a pointer to the primary Frame which we will use to set the - Domain of the primary Frame. */ - if( !pfrm2 ) pfrm2 = astClone( pfrm ); - -/* Get the CTYPE value. Use a default of "AXISn". */ - ckeyval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - if( !ckeyval ) { - sprintf( buf3, "AXIS%d", i + 1 ); - ckeyval = buf3; - } - -/* If the CTYPE value ends with "-LOG", assume it is a logarithmically spaced - axis. Get the Mapping from IWC to WCS. Reduce the used length of the - CTYPE string to exlude any trailing "-LOG" string. */ - len = strlen( ckeyval ); - if( len > 3 && !strcmp( ckeyval + len - 4, "-LOG" ) ){ - map1 = LogWcs( store, i, s, method, class, status ); - sprintf( buf2, "%.*s", len - 4, ckeyval ); - -/* Otherwise, assume the axis is linearly spaced. */ - } else { - map1 = LinearWcs( store, i, s, method, class, status ); - sprintf( buf2, "%.*s", len, ckeyval ); - } - -/* Append the CTYPE value to the final Domain value for the primary Frame. */ - if( ckeyval && astChrLen( ckeyval ) > 0 ) { - if( newdom ) { - sprintf( buf, "%s-%s", newdom, buf2 ); - } else { - sprintf( buf, "%s", buf2 ); - newdom = buf; - } - } - -/* Now modify the axis in the Frame to have appropriate values for the - Unit, Label and Symbol attributes. Also set the Unit attribute for - the corresponding axis in the IWC Frame. */ - if( ckeyval ) astSetSymbol( *frm, i, buf2 ); - ckeyval = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status ); - if( !ckeyval && usecom ) ckeyval = GetItemC( &(store->ctype_com), - i, 0, s, NULL, method, class, status ); - if( !ckeyval ) ckeyval = buf2; - if( ckeyval ) astSetLabel( *frm, i, ckeyval ); - ckeyval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); - if( ckeyval ) { - astSetUnit( *frm, i, ckeyval ); - astSetUnit( iwcfrm, i, ckeyval ); - } - -/* If this axis has been described by an earlier function (because it - uses specialised conventions such as those described in FITS-WCS papers - II or III), then create a UnitMap for this axis. */ - } else { - map1 = (AstMapping *) astUnitMap( 1, "", status ); - } - -/* Annul the pointer to the primary Frame containing the current axis. */ - pfrm = astAnnul( pfrm ); - -/* Add the Mapping for this axis in parallel with the current "running sum" - Mapping (if any). */ - if( ret ) { - map2 = (AstMapping *) astCmpMap( ret, map1, 0, "", status ); - ret = astAnnul( ret ); - map1 = astAnnul( map1 ); - ret = map2; - } else { - ret = map1; - } - } - -/* Set the Domain name for the primary Frame. It is currently set to - AST_FITSCHAN. We replace it with a value formed by concatenating the - CTYPE values of its axes. */ - if( pfrm2 ) { - if( newdom && astChrLen( newdom ) > 0 ) { - astSetDomain( pfrm2, newdom ); - } else { - astClearDomain( pfrm2 ); - } - pfrm2 = astAnnul( pfrm2 ); - } - -/* If the header contained a WCSNAME keyword, use it as the Domain name for - the Frame. Also use it to create a title. */ - ckeyval = GetItemC( &(store->wcsname), 0, 0, s, NULL, method, class, status ); - if( ckeyval ){ - astSetDomain( *frm, ckeyval ); - sprintf( buf, "%s coordinates", ckeyval ); - astSetTitle( *frm, buf ); - } - } - -/* Return the result. */ - return ret; -} - -static AstWinMap *WcsShift( FitsStore *store, char s, int naxes, - const char *method, const char *class, int *status ){ -/* -* Name: -* WcsShift - -* Purpose: -* Create a WinMap which shifts pixels coordinates so that their origin -* is at the reference pixel. - -* Type: -* Private function. - -* Synopsis: -* AstWinMap *WcsShift( FitsStore *store, char s, int naxes, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* A WinMap is created which implements a shift of origin by subtracting -* the reference pixel coordinates (CRPIXi) from the input pixel -* coordinates. - -* Parameters: -* store -* A structure containing values for FITS keywords relating to -* the World Coordinate System. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* naxes -* The number of intermediate world coordinate axes (WCSAXES). -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the created WinMap or a NULL pointer if an -* error occurred. - -* Notes: -* - If an error occurs, a NULL pointer is returned. -*/ - -/* Local Variables: */ - AstWinMap *new; /* The created WinMap */ - int j; /* Pixel axis index */ - double crpix; /* CRPIX keyword value */ - double *c1_in; /* Input corner 1 */ - double *c2_in; /* Input corner 2 */ - double *c1_out; /* Output corner 1 */ - double *c2_out; /* Output corner 2 */ - -/* Check the global status. */ - if ( !astOK ) return NULL; - -/* Initialise the returned WinMap pointer. */ - new = NULL; - -/* Allocate memory to hold the two corners, in both input and output - coordinates. */ - c1_in = (double *) astMalloc( sizeof( double )*(size_t) naxes ); - c1_out = (double *) astMalloc( sizeof( double )*(size_t) naxes ); - c2_in = (double *) astMalloc( sizeof( double )*(size_t) naxes ); - c2_out = (double *) astMalloc( sizeof( double )*(size_t) naxes ); - -/* Check these pointers can be used. */ - if( astOK ){ - -/* Set up two arbitrary corners in the input coordinate system, and the - corresponding values with the CRPIX values subtracted off. */ - for( j = 0; j < naxes; j++ ){ - -/* Get the CRPIX value for this axis. */ - crpix = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); - if( crpix == AST__BAD ) crpix = 0.0; - -/* Store the corner co-ordinates. */ - c1_in[ j ] = 0.0; - c2_in[ j ] = 1.0; - c1_out[ j ] = -crpix; - c2_out[ j ] = 1.0 - crpix; - } - -/* Create the WinMap. */ - new = astWinMap( naxes, c1_in, c2_in, c1_out, c2_out, "", status ); - -/* If an error has occurred, attempt to annul the new WinMap. */ - if( !astOK ) new = astAnnul( new ); - } - -/* Free the memory holding the corners. */ - c1_in = (double *) astFree( (void *) c1_in ); - c1_out = (double *) astFree( (void *) c1_out ); - c2_in = (double *) astFree( (void *) c2_in ); - c2_out = (double *) astFree( (void *) c2_out ); - -/* Return the WinMap. */ - return new; -} - -static AstSkyFrame *WcsSkyFrame( AstFitsChan *this, FitsStore *store, char s, - int prj, char *sys, int axlon, int axlat, - const char *method, const char *class, int *status ){ - -/* -* Name: -* WcsSkyFrame - -* Purpose: -* Create a SkyFrame to describe a WCS celestial coordinate system. - -* Type: -* Private function. - -* Synopsis: -* AstSkyFrame *WcsSkyFrame( AstFitsChan this, FitsStore *store, char s, int prj, -* char *sys, int axlon, int axlat, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A SkyFrame is returned describing the celestial coordinate system -* described by a FITS header. The axes are *not* permuted in the -* returned Frame (that is, axis 0 is longitude and axis 1 is latitude -* in the returned SkyFrame, no matter what values are supplied for -* "axlat" and "axlon"). - -* Parameters: -* this -* The FitsChan from which the keywords were read. Warning messages -* may be added to this FitsChan. -* store -* A structure containing values for FITS keywords relating to -* the World Coordinate System. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* prj -* An integer code for the WCS projection being used. -* sys -* A pointer to a string identifying the celestial co-ordinate system -* implied by the CTYPE values in the FitsStore. This will be "EQU" (for -* equatorial), or a one or two character code extracted from the -* CTYPE values. -* axlon -* Zero based index of the longitude axis in the FITS header. -* axlat -* Zero based index of the latitude axis in the FITS header. -* method -* The calling method. Used only in error messages. -* class -* The object class. Used only in error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the SkyFrame. - -* Notes: -* - A NULL pointer is returned if an error has already occurred, or -* if this function should fail for any reason. -*/ - -/* Local Variables: */ - AstSkyFrame *ret; /* Returned Frame */ - char *ckeyval; /* Pointer to string item value */ - char *lattype; /* Pointer to latitude CTYPE value */ - char *lontype; /* Pointer to longitude CTYPE value */ - char bj; /* Besselian/Julian selector */ - char buf[300]; /* Text buffer */ - char sym[10]; /* Axis symbol */ - double dval; /* Floating point attribute value */ - double eqmjd; /* MJD equivalent of equinox */ - double equinox; /* EQUINOX value */ - double geolat; /* Observer's geodetic latitude */ - double geolon; /* Observer's geodetic longitude */ - double h; /* Observer's geodetic height */ - double mjdobs; /* MJD-OBS value */ - double obsgeo[ 3 ]; /* Observer's Cartesian position */ - int radesys; /* RADESYS value */ - int report; /* Report unknown lon/lat system? */ - -/* Initialise. */ - ret = NULL; - -/* Check the global error status. */ - if ( !astOK ) return ret; - -/* Get the RADESYS keyword from the header, and identify the value. - Store a integer value identifying the system. Report an error if an - unrecognised system is supplied. Store NORADEC if the keyword was - not supplied. */ - ckeyval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); - radesys = NORADEC; - if( ckeyval ){ - if( !strncmp( ckeyval, "FK4 ", 4 ) || - !strcmp( ckeyval, "FK4" ) ){ - radesys = FK4; - } else if( !strncmp( ckeyval, "FK4-NO-E", 8 ) ){ - radesys = FK4NOE; - } else if( !strncmp( ckeyval, "FK5 ", 4 ) || - !strcmp( ckeyval, "FK5" ) ){ - radesys = FK5; - } else if( !strncmp( ckeyval, "ICRS ", 5 ) || - !strcmp( ckeyval, "ICRS" ) ){ - radesys = ICRS; - } else if( !strncmp( ckeyval, "GAPPT ", 6 ) || - !strcmp( ckeyval, "GAPPT" ) ){ - radesys = GAPPT; - } else if( astOK ){ - astError( AST__BDFTS, "%s(%s): FITS keyword '%s' has the " - "unrecognised value '%s'.", status, method, class, - FormatKey( "RADESYS", -1, -1, s, status ), ckeyval ); - } - } else { - radesys = NORADEC; - } - -/* Get the value of the EQUINOX keyword. */ - equinox = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); - -/* For FK4 and FK4-NO-E any supplied equinox value is Besselian. For all - other systems, the equinox value is Julian. */ - bj = 0; - if( equinox != AST__BAD ){ - if( radesys == FK4 || radesys == FK4NOE ){ - bj = 'B'; - } else if( radesys != NORADEC ) { - bj = 'J'; - -/* If no RADESYS was suppied, but an equinox was, use the IAU 1984 rule - to determine the default RADESYS and equinox type. */ - } else { - if( equinox < 1984.0 ){ - radesys = FK4; - bj = 'B'; - } else { - radesys = FK5; - bj = 'J'; - } - -/* If an equatorial system is being used, give a warning that a default RADESYS - value is being used. */ - if( !strcmp( sys, "EQU" ) ){ - sprintf( buf, "The original FITS header did not specify the " - "RA/DEC reference frame. A default value of %s was " - "assumed.", ( radesys == FK4 ) ? "FK4" : "FK5" ); - Warn( this, "noradesys", buf, method, class, status ); - } - } - -/* If no equinox was supplied, use a default equinox value depending - on the frame of reference. For FK4-based systems, use B1950. */ - } else { - if( radesys == FK4 || radesys == FK4NOE ){ - equinox = 1950.0; - bj = 'B'; - -/* For FK5-based systems, use J2000. */ - } else if( radesys == FK5 ){ - equinox = 2000.0; - bj = 'J'; - -/* If no RADESYS or EQUINOX was supplied, assume either FK4 B1950 or ICRS - - as decided by attribute DefB1950 (GAPPT and ICRS do not use EQUINOX). */ - } else if( radesys == NORADEC ) { - if( astGetDefB1950( this ) ) { - equinox = 1950.0; - bj = 'B'; - radesys = FK4; - } else { - radesys = ICRS; - } - if( !strcmp( sys, "EQU" ) ){ - sprintf( buf, "The original FITS header did not specify the " - "RA/DEC reference frame. A default value of %s was " - "assumed.", ( radesys == FK4 ) ? "FK4" : "ICRS" ); - Warn( this, "noradesys", buf, method, class, status ); - } - } - -/* If we have an equatorial or ecliptic system, issue a warning that a default - equinox has been adopted. */ - if( ( !strcmp( sys, "EQU" ) && radesys != ICRS && radesys != GAPPT ) || - !strcmp( sys, "ECL" ) ){ - sprintf( buf, "The original FITS header did not specify the " - "reference equinox. A default value of %c%.8g was " - "assumed.", bj, equinox ); - Warn( this, "noequinox", buf, method, class, status ); - } - } - -/* Convert the equinox to a Modified Julian Date. */ - if( equinox != AST__BAD ) { - if( bj == 'B' ) { - eqmjd = palEpb2d( equinox ); - } else { - eqmjd = palEpj2d( equinox ); - } - } else { - eqmjd = AST__BAD; - } - -/* Get a value for the Epoch attribute. If no value is available, use - EQUINOX and issue a warning. */ - mjdobs = ChooseEpoch( this, store, s, method, class, status ); - if( mjdobs == AST__BAD ) { - mjdobs = eqmjd; - if( mjdobs != AST__BAD ) { - sprintf( buf, "The original FITS header did not specify the " - "date of observation. A default value of %c%.8g was " - "assumed.", bj, equinox ); - Warn( this, "nomjd-obs", buf, method, class, status ); - } - } - -/* Create a SkyFrame for the specified system. */ - if( !strcmp( sys, "E" ) ){ - ret = astSkyFrame( "System=Ecliptic", status ); - } else if( !strcmp( sys, "H" ) ){ - ret = astSkyFrame( "System=Helioecliptic", status ); - } else if( !(strcmp( sys, "G" ) ) ){ - ret = astSkyFrame( "System=Galactic", status ); - } else if( !(strcmp( sys, "S" ) ) ){ - ret = astSkyFrame( "System=Supergalactic", status ); - } else if( !(strcmp( sys, "AZL" ) ) ){ - ret = astSkyFrame( "System=AzEl", status ); - } else if( !(strcmp( sys, "EQU" ) ) ){ - -/* For equatorial systems, the specific system is given by the RADESYS - value. */ - if( radesys == FK4 ){ - ret = astSkyFrame( "System=FK4", status ); - } else if( radesys == FK4NOE ){ - ret = astSkyFrame( "System=FK4-NO-E", status ); - } else if( radesys == FK5 ){ - ret = astSkyFrame( "System=FK5", status ); - } else if( radesys == ICRS ){ - ret = astSkyFrame( "System=ICRS", status ); - } else if( radesys == GAPPT ){ - ret = astSkyFrame( "System=GAPPT", status ); - } else if( astOK ){ - astError( AST__INTER, "%s(%s): Internal AST programming " - "error - FITS equatorial coordinate system type %d " - "not yet supported in WcsSkyFrame.", status, method, class, radesys ); - } - -/* If an unknown celestial co-ordinate system was specified by the CTYPE - keywords, add warning messages to the FitsChan and treat the axes as - a general spherical coordinate system. */ - } else if( astOK ){ - report = 1; - ret = astSkyFrame( "System=UNKNOWN", status ); - strcpy( sym, sys ); - if( strlen( sys ) == 1 ) { - strcpy( sym + 1, "LON" ); - astSetSymbol( ret, 0, sym ); - strcpy( sym + 1, "LAT" ); - astSetSymbol( ret, 1, sym ); - } else { - strcpy( sym + 2, "LN" ); - astSetSymbol( ret, 0, sym ); - strcpy( sym + 2, "LT" ); - astSetSymbol( ret, 1, sym ); - -/* The code "OF" is used by AST to describe offset sky coordinates. Set - the Domain to SKY_OFFSETS in these cases, so that we can identify - these Frames later. */ - if( !strcmp( sys, "OF" ) ) { - astSetDomain( ret, "SKY_OFFSETS" ); - report = 0; - } - } - - if( report ) { - lontype = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); - lattype = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); - if( lontype && lattype ){ - sprintf( buf, "This FITS header contains references to an unknown " - "spherical co-ordinate system specified in the values " - "%s and %s. It may not be possible to convert to " - "other standard co-ordinate systems.", lontype, lattype ); - Warn( this, "badcel", buf, method, class, status ); - } - } - } - -/* If a skyFrame was created... */ - if( ret ){ - -/* Store the projection description. */ - if( prj != AST__WCSBAD ) astSetProjection( ret, astWcsPrjDesc( prj ) ); - -/* Store the epoch of the observation in the SkyFrame. */ - if( mjdobs != AST__BAD ) astSetEpoch( ret, mjdobs ); - -/* For equatorial and ecliptic systems, store the epoch of the reference - equinox in the SkyFrame. */ - if( ( !strcmp( sys, "EQU" ) || !strcmp( sys, "ECL" ) ) && - equinox != AST__BAD ) astSetEquinox( ret, eqmjd ); - -/* If either of the CNAME keywords is set, use it as the axis label. */ - ckeyval = GetItemC( &(store->cname), axlon, 0, s, NULL, method, class, status ); - if( ckeyval ) astSetLabel( ret, 0, ckeyval ); - ckeyval = GetItemC( &(store->cname), axlat, 0, s, NULL, method, class, status ); - if( ckeyval ) astSetLabel( ret, 1, ckeyval ); - -/* Observer's position (from primary axis descriptions). Get the OBSGEO-X/Y/Z - keywords, convert to geodetic longitude and latitude and store as the - SpecFrame's ObsLat, ObsLon and ObsAlt attributes. */ - obsgeo[ 0 ] = GetItem( &(store->obsgeox), 0, 0, ' ', NULL, method, class, status ); - obsgeo[ 1 ] = GetItem( &(store->obsgeoy), 0, 0, ' ', NULL, method, class, status ); - obsgeo[ 2 ] = GetItem( &(store->obsgeoz), 0, 0, ' ', NULL, method, class, status ); - if( obsgeo[ 0 ] != AST__BAD && - obsgeo[ 1 ] != AST__BAD && - obsgeo[ 2 ] != AST__BAD ) { - eraGc2gd( 1, obsgeo, &geolon, &geolat, &h ); - astSetObsLat( ret, geolat ); - astSetObsLon( ret, geolon ); - astSetObsAlt( ret, h ); - } - -/* Store values for the reference point in the SkyFrame. */ - dval = GetItem( &(store->skyref), axlon, 0, s, NULL, method, class, status ); - if( dval != AST__BAD ) astSetSkyRef( ret, 0, dval ); - dval = GetItem( &(store->skyref), axlat, 0, s, NULL, method, class, status ); - if( dval != AST__BAD ) astSetSkyRef( ret, 1, dval ); - - dval = GetItem( &(store->skyrefp), axlon, 0, s, NULL, method, class, status ); - if( dval != AST__BAD ) astSetSkyRefP( ret, 0, dval ); - dval = GetItem( &(store->skyrefp), axlat, 0, s, NULL, method, class, status ); - if( dval != AST__BAD ) astSetSkyRefP( ret, 1, dval ); - -/* We cannot store the SkyRefIs value yet since this needs to be done - after the SkyFrame has been added into the FrameSet, so that the Frame - will be remapped to represent the intended offsets. SO instance, mark - the Frame by setting the domain to "SKY_POLE" or "SKY_ORIGIN". This - odd Domain value will be cleared later in TidyOffsets. */ - ckeyval = GetItemC( &(store->skyrefis), 0, 0, s, NULL, method, class, status ); - if( ckeyval ) { - if( !Ustrcmp( "POLE", ckeyval, status ) ) { - astSetDomain( ret, "SKY_POLE" ); - } else if( !Ustrcmp( "ORIGIN", ckeyval, status ) ) { - astSetDomain( ret, "SKY_ORIGIN" ); - } - } - } - -/* If an error has occurred, annul the Frame. */ - if( !astOK ) ret = astAnnul( ret ); - -/* Return the Frame. */ - return ret; -} - -static AstMapping *WcsSpectral( AstFitsChan *this, FitsStore *store, char s, - AstFrame **frm, AstFrame *iwcfrm, double reflon, double reflat, - AstSkyFrame *reffrm, const char *method, - const char *class, int *status ){ - -/* -* Name: -* WcsSpectral - -* Purpose: -* Create a Mapping from intermediate world coords to spectral coords -* as described in a FITS header. - -* Type: -* Private function. - -* Synopsis: - -* AstMapping *WcsSpectral( AstFitsChan *this, FitsStore *store, char s, -* AstFrame **frm, AstFrame *iwcfrm, double reflon, -* double reflat, AstSkyFrame *reffrm, -* const char *method, const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function interprets the contents of the supplied FitsStore -* structure, looking for world coordinate axes which describe positions -* in a spectrum. If such an axis is found, a Mapping is returned which -* transforms the corresponding intermediate world coordinates to -* spectral world coordinates (this mapping leaves any other axes -* unchanged). It also, modifies the supplied Frame to describe the -* axis (again, other axes are left unchanged). If no spectral axis -* is found, a UnitMap is returned, and the supplied Frame is left -* unchanged. - -* Parameters: -* this -* The FitsChan. -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* frm -* The address of a location at which to store a pointer to the -* Frame describing the world coordinate axes. -* iwcfrm -* A pointer to the Frame describing the intermediate world coordinate -* axes. The properties of this Frame may be changed on exit. -* reflon -* The reference celestial longitude, in the frame given by reffrm. -* reflat -* The reference celestial latitude, in the frame given by reffrm. -* reffrm -* The SkyFrame defining reflon and reflat. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the Mapping. -*/ - -/* Local Variables: */ - AstFrame *ofrm; /* Pointer to a Frame */ - AstMapping *map1; /* Pointer to Mapping */ - AstMapping *map2; /* Pointer to Mapping */ - AstMapping *ret; /* Pointer to the returned Mapping */ - AstSpecFrame *specfrm; /* Pointer to a SpecFrame */ - char algcode[ 5 ]; /* Displayed spectral type string */ - char stype[ 5 ]; /* Displayed spectral type string */ - const char *cname; /* Pointer to CNAME value */ - const char *ctype; /* Pointer to CTYPE value */ - const char *cunit; /* Pointer to CUNIT value */ - const char *defunit; /* Default unit string */ - const char *specsys; /* Pointer to SPECSYS value */ - const char *ssyssrc; /* Pointer to SSYSSRC value */ - double geolat; /* Observer's geodetic latitude */ - double geolon; /* Observer's geodetic longitude */ - double h; /* Observer's geodetic height */ - double mjd; /* Modified Julian Date */ - double obscentre; /* Spectral value at observation centre */ - double obsgeo[ 3 ]; /* Observer's Cartesian position */ - double restfrq; /* RESTFRQ keyword value */ - double vsource; /* Source velocity */ - int *axes; /* Pointer to axis permutation array */ - int i; /* Axis index */ - int j; /* Loop count */ - int k; /* Loop count */ - int kk; /* Loop count */ - int naxes; /* No. of axes in Frame */ - -/* Initialise the pointer to the returned Mapping. */ - ret = NULL; - -/* Check the global status. */ - if ( !astOK ) return ret; - -/* Get the number of physical axes. */ - naxes = astGetNaxes( *frm ); - -/* An array to hold a list of axis selections. */ - axes = astMalloc( naxes*sizeof( int ) ); - -/* Loop round checking each axis. */ - defunit = NULL; - map1 = NULL; - for( i = 0; i < naxes && astOK; i++ ) { - -/* Get the CTYPE value. Pass on to the next axis if no CTYPE is available. */ - ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); - if( ctype ) { - -/* See if this CTYPE describes a spectral axis, and if so, extract the - system code, the algorithm code and get the default units. */ - defunit = IsSpectral( ctype, stype, algcode, status ); - -/* Skip to the next axis if the system type was not a spectral system - type. */ - if( defunit ) { - -/* Create a SpecFrame or DSBSpecFrame with this system (the FITS type codes - are also legal SpecFrame System values). We use astSetC rather than - astSetSystem because astSetC translates string values into the - corresponding integer system identifiers. */ - if( GetItem( &(store->imagfreq), 0, 0, s, NULL, method, - class, status ) == AST__BAD ) { - specfrm = astSpecFrame( "", status ); - } else { - specfrm = (AstSpecFrame *) astDSBSpecFrame( "", status ); - } - astSetC( specfrm, "System", stype ); - -/* Set the reference position (attributes RefRA and RefDec), if known. */ - if( reffrm ) astSetRefPos( specfrm, reffrm, reflon, reflat ); - -/* Set the SpecFrame units. Use the value of the CUNIT FITS keyword for this - axis if available, otherwise use the default units for the system, noted - above. */ - cunit = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); - if( !cunit ) cunit = defunit; - astSetUnit( specfrm, 0, cunit ); - -/* Set the axis unit in the IWC Frame. */ - astSetUnit( iwcfrm, i, cunit ); - -/* Get a value for the Epoch attribute (the date of observation). */ - mjd = ChooseEpoch( this, store, s, method, class, status ); - if( mjd != AST__BAD ) astSetEpoch( specfrm, mjd ); - -/* Set the rest frequency. Use the RESTFRQ keyword (assumed to be in Hz), - or (if RESTFRQ is not available), RESTWAV (assumes to be in m). */ - restfrq = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); - if( restfrq == AST__BAD ) { - restfrq = GetItem( &(store->restwav), 0, 0, s, NULL, method, class, status ); - if( restfrq != AST__BAD ) restfrq = AST__C/restfrq; - } - astSetRestFreq( specfrm, restfrq ); - -/* Observer's position (from primary axis descriptions). Get the OBSGEO-X/Y/Z - keywords, convert to geodetic longitude and latitude and store as the - SpecFrame's ObsLat, ObsLon and ObsAlt attributes. */ - obsgeo[ 0 ] = GetItem( &(store->obsgeox), 0, 0, ' ', NULL, method, class, status ); - obsgeo[ 1 ] = GetItem( &(store->obsgeoy), 0, 0, ' ', NULL, method, class, status ); - obsgeo[ 2 ] = GetItem( &(store->obsgeoz), 0, 0, ' ', NULL, method, class, status ); - if( obsgeo[ 0 ] != AST__BAD && - obsgeo[ 1 ] != AST__BAD && - obsgeo[ 2 ] != AST__BAD ) { - eraGc2gd( 1, obsgeo, &geolon, &geolat, &h ); - astSetObsLat( specfrm, geolat ); - astSetObsLon( specfrm, geolon ); - astSetObsAlt( specfrm, h ); - } - -/* Source velocity rest frame */ - ssyssrc = GetItemC( &(store->ssyssrc), 0, 0, s, NULL, method, class, status ); - if( ssyssrc ) astSetC( specfrm, "SourceVRF", ssyssrc ); - -/* Source velocity. Use the ZSOURCE keyword and convert from redshift to - velocity. */ - vsource = GetItem( &(store->zsource), 0, 0, s, NULL, method, class, status ); - if( vsource != AST__BAD ) { - vsource += 1.0; - vsource *= vsource; - vsource = AST__C*( vsource - 1.0 )/( vsource + 1.0 ); - astSetSourceVel( specfrm, vsource ); - } - -/* Reference frame. If the SPECSYS keyword is set, use it (the FITS codes - are also legal SpecFrame StdOfRest values). We use astSetC rather than - astSetSystem because astSetC translates string values into the - corresponding integer system identifiers. */ - specsys = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); - if( specsys ) astSetC( specfrm, "StdOfRest", specsys ); - -/* Axis label. If the CNAME keyword is set, use it as the axis label. */ - cname = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status ); - if( cname ) astSetLabel( specfrm, 0, cname ); - -/* If the header contains an AXREF value for the spectral axis, use it as the - observation centre in preferences to the CRVAL value. AXREF keywords are - created by the astWrite method for axes described by -TAB algorithm that - have no inverse transformation. */ - obscentre = GetItem( &(store->axref), i, 0, s, NULL, method, - class, status ); - if( obscentre == AST__BAD ) { - obscentre = GetItem( &(store->crval), i, 0, s, NULL, method, - class, status ); - } - -/* Now do the extra stuff needed if we are creating a dual sideband - SpecFrame. */ - if( astIsADSBSpecFrame( specfrm ) ) { - DSBSetUp( this, store, (AstDSBSpecFrame *) specfrm, s, - obscentre, method, class, status ); - } - -/* Now branch for each type of algorithm code. Each case returns a 1D - Mapping which converts IWC value into the specified Spectral system. */ - -/* Linear */ - if( strlen( algcode ) == 0 ) { - map1 = LinearWcs( store, i, s, method, class, status ); - -/* Log-Linear */ - } else if( !strcmp( "-LOG", algcode ) ) { - map1 = LogWcs( store, i, s, method, class, status ); - -/* Non-Linear */ - } else if( algcode[ 0 ] == '-' && algcode[ 2 ] == '2' ) { - map1 = NonLinSpecWcs( this, algcode, store, i, s, specfrm, method, class, status ); - -/* Grism */ - } else if( !strcmp( "-GRI", algcode ) || - !strcmp( "-GRA", algcode ) ) { - map1 = GrismSpecWcs( algcode, store, i, s, specfrm, method, class, status ); - } else { - map1 = NULL; - } - if( map1 == NULL && astOK ) { - specfrm = astAnnul( specfrm ); - astError( AST__BDFTS, "%s(%s): Cannot implement spectral " - "algorithm code '%s' specified in FITS keyword '%s'.", status, - method, class, ctype + 4, FormatKey( "CTYPE", i + 1, -1, s, status ) ); - astError( AST__BDFTS, "%s(%s): Unknown algorithm code or " - "unusable parameter values.", status, method, class ); - break; - } - -/* Create a Frame by picking all the other (non-spectral) axes from the - supplied Frame. */ - j = 0; - for( k = 0; k < naxes; k++ ) { - if( k != i ) axes[ j++ ] = k; - } - -/* If there were no other axes, replace the supplied Frame with the - specframe. */ - if( j == 0 ) { - (void) astAnnul( *frm ); - *frm = (AstFrame *) specfrm; - -/* Otherwise pick the other axes from the supplied Frame */ - } else { - ofrm = astPickAxes( *frm, j, axes, NULL ); - -/* Replace the supplied Frame with a CmpFrame made up of this Frame and - the SpecFrame. */ - (void) astAnnul( *frm ); - *frm = (AstFrame *) astCmpFrame( ofrm, specfrm, "", status ); - ofrm = astAnnul( ofrm ); - specfrm = astAnnul( specfrm ); - } - -/* Permute the axis order to put the spectral axis back in its original - position. */ - j = 0; - for( kk = 0; kk < naxes; kk++ ) { - if( kk == i ) { - axes[ kk ] = naxes - 1; - } else { - axes[ kk ] = j++; - } - } - astPermAxes( *frm, axes ); - } - } - -/* If this axis is not a spectral axis, create a UnitMap (the Frame is left - unchanged). */ - if( !map1 && astOK ) map1 = (AstMapping *) astUnitMap( 1, "", status ); - -/* Add the Mapping for this axis in parallel with the Mappings for - previous axes. */ - if( ret ) { - map2 = (AstMapping *) astCmpMap( ret, map1, 0, "", status ); - ret = astAnnul( ret ); - map1 = astAnnul( map1 ); - ret = map2; - } else { - ret = map1; - map1 = NULL; - } - } - -/* Free the axes array. */ - axes= astFree( axes ); - -/* Return the result. */ - return ret; -} - -static void WcsToStore( AstFitsChan *this, AstFitsChan *trans, - FitsStore *store, const char *method, - const char *class, int *status ){ - -/* -* Name: -* WcsToStore - -* Purpose: -* Extract WCS information from the supplied FitsChan using a FITSWCS -* encoding, and store it in the supplied FitsStore. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* void WcsToStore( AstFitsChan *this, AstFitsChan *trans, -* FitsStore *store, const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* A FitsStore is a structure containing a generalised represention of -* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and -* from a set of FITS header cards (using a specified encoding), or -* an AST FrameSet. In other words, a FitsStore is an encoding- -* independant intermediary staging post between a FITS header and -* an AST FrameSet. -* -* This function extracts FITSWCS keywords from the supplied FitsChan(s), -* and stores the corresponding WCS information in the supplied FitsStore. -* Keywords will be searched for first in "trans", and then, if they -* are not found in "trans", they will be searched for in "this". - -* Parameters: -* this -* Pointer to the FitsChan containing the cards read from the -* original FITS header. This may include non-standard keywords. -* trans -* Pointer to a FitsChan containing cards representing standard -* translations of any non-standard keywords in "this". A NULL -* pointer indicates that "this" contains no non-standard keywords. -* store -* Pointer to the FitsStore structure. -* method -* Pointer to a string holding the name of the calling method. -* This is only for use in constructing error messages. -* class -* Pointer to a string holding the name of the supplied object class. -* This is only for use in constructing error messages. -* status -* Pointer to the inherited status variable. -*/ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Read all usable cards out of the main FitsChan, into the FitsStore. */ - WcsFcRead( this, trans, store, method, class, status ); - -/* If a FitsChan containing standard translations was supplied, read all - cards out of it, into the FitsStore, potentially over-writing the - non-standard values stored in the previous call to WcsFcRead. */ - if( trans ) WcsFcRead( trans, NULL, store, method, class, status ); -} - -static int WorldAxes( AstFitsChan *this, AstMapping *cmap, double *dim, int *perm, - int *status ){ - -/* -* Name: -* WorldAxes - -* Purpose: -* Associate final world axes with pixel axes. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" - -* int WorldAxes( AstFitsChan *this, AstMapping *cmap, double *dim, int *perm, -* int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function finds the association between the axes of the final -* world coordinate system, and those of the pixel coordinate -* system. This may not simply be a 1-to-1 association because the -* Mapping may include a PermMap. Each output axis is associated with -* the input axis which is most nearly aligned with it. - -* Parameters: -* this -* Pointer to the FitsChan. -* cmap -* Pointer to the Mapping from pixel coordinates to final world -* coordinates. -* dim -* Pointer to an array with one element for each input of "map", -* supplied holding the no. of pixels in the data cube along the axis, or -* AST__BAD If unknown. -* perm -* Pointer to an array with one element for each output of "map". -* On exit, each element of this array holds the zero-based index of the -* "corresponding" (i.e. most nearly parallel) pixel axis. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* Non-zero for success - zero for failure. -*/ - -/* Local Variables: */ - AstMapping *smap; - AstMapping *map; - AstPointSet *pset1; - AstPointSet *pset2; - double **ptr2; - double **ptr1; - double *dw; - double *g0; - double *nwt; - double *ntn; - double *tn; - double *wt; - double *w0; - double dg; - double s; - double sj; - double tnmin; - double wtmax; - int *outs; - int i2; - int i; - int imin; - int j2; - int j; - int jmin; - int nin; - int nout; - int nouts; - int nused; - int ret; - int retain; - int used; - -/* Initialise returned value */ - ret = 0; - -/* Other initialisation to avoid compiler warnings. */ - retain = 0; - -/* Check the status */ - if( !astOK ) return ret; - -/* Simplfy the Mapping. */ - map = astSimplify( cmap ); - -/* Get the number of inputs and outputs for the Mapping. */ - nin = astGetNin( map ); - nout = astGetNout( map ); - -/* Initialise "perm". */ - for( i = 0; i < nout; i++ ) perm[ i ] = i; - -/* First deal with Mappings that are defined in both directions. */ - if( astGetTranForward( map ) && astGetTranInverse( map ) ) { - -/* Use FindBasisVectors to find an input position which coresponds to a - good output position. Store it in a dynamic array pointed to by "g0". */ - pset1 = astPointSet( nin+1, nin, "", status ); - pset2 = astPointSet( nin+1, nout, "", status ); - if( FindBasisVectors( map, nin, nout, dim, pset1, pset2, status ) ) { - g0 = astMalloc( sizeof(double)*nin ); - ptr1 = astGetPoints( pset1 ); - if( astOK ) { - for( j = 0; j < nin; j++ ) g0[ j ] = ptr1[ j ][ 0 ]; - } - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - -/* If no basis vectors found, return. */ - } else { - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - return ret; - } - -/* Create Pointset to hold two input (pixel) points. */ - pset1 = astPointSet( 2, nin, "", status ); - ptr1 = astGetPoints( pset1 ); - -/* Create a Pointset to hold the same number of output (world) points. */ - pset2 = astPointSet( 2, nout, "", status ); - ptr2 = astGetPoints( pset2 ); - -/* Allocate memory to use as work space */ - w0 = astMalloc( sizeof(double)*nout ); - dw = astMalloc( sizeof(double)*nout ); - tn = astMalloc( sizeof(double)*nout*nin ); - wt = astMalloc( sizeof(double)*nout*nin ); - -/* Check that the pointers can be used. */ - if( astOK ) { - -/* Transform the grid position found above, plus a position 1 pixel away - along all pixel axes, into world coords. Also set up "dw" to hold - "a small increment" along each world axis. */ - for( j = 0; j < nin; j++ ) { - ptr1[ j ] [ 0 ] = g0[ j ]; - ptr1[ j ] [ 1 ] = g0[ j ] + 1.0; - } - (void) astTransform( map, pset1, 1, pset2 ); - for( i = 0; i < nout; i++ ) { - w0[ i ] = ptr2[ i ] [ 0 ]; - if( w0[ i ] != AST__BAD && ptr2[ i ] [ 1 ] != AST__BAD ) { - dw[ i ] = fabs( 0.1*( ptr2[ i ] [ 1 ] - w0[ i ] ) ); - if( dw[ i ] <= fabs( 0.001*w0[ i ] ) ) { - if( w0[ i ] != 0.0 ) { - dw[ i ] = fabs( 0.001*w0[ i ] ); - } else { - dw[ i ] = 1.0; - } - } - } else { - dw[ i ] = AST__BAD; - } - } - -/* Any PermMap in the mapping may result in the the "inverse transformation" - not being a true inverse of the forward transformation (for instance, - constant values fed in for degenerate axis would have this effect). To - ensure that "g0" and "w0" are corresponding positions, transform the - "w0" position back into grid coords and use the resulting grid position - as "g0". */ - (void) astTransform( map, pset2, 0, pset1 ); - for( j = 0; j < nin; j++ ) { - g0[ j ] = ptr1[ j ] [ 0 ]; - } - -/* In the next loop we find the tan of the angle between each WCS axis and - each of the pixel axes. Loop round each WCS axis. */ - for( i = 0; i < nout; i++ ) { - -/* Initialise the tan values for this WCS axis to AST__BAD. */ - ntn = tn + i*nin; - nwt = wt + i*nin; - for( j = 0; j < nin; j++ ) ntn[ j ] = AST__BAD; - -/* As a side issue, initialise the pixel axis assigned to each WCS axis - to -1, to indicate that no grid axis has yet been associated with this - WCS axis. */ - perm[ i ] = -1; - -/* Skip over this axis if the increment is bad. */ - if( dw[ i ] != AST__BAD ) { - -/* Store a WCS position which is offset from the "w0" position by a small - amount along the current WCS axis. The first position in "ptr2" is - currently "w0". */ - ptr2[ i ][ 0 ] += dw[ i ]; - -/* Transform this position into grid coords. */ - (void) astTransform( map, pset2, 0, pset1 ); - -/* Re-instate the original "w0" values within "ptr2", ready for the next - WCS axis. */ - ptr2[ i ][ 0 ] = w0[ i ]; - -/* Consider each pixel axis in turn as a candidate for being assigned to - the current WCS axis. */ - for( j = 0; j < nin; j++ ) { - -/* Find the tan of the angle between the current ("i"th) WCS axis and the - current ("j"th) pixel axis. This gets stored in tn[j+nin*i]. A - corresponding weight for each angle is stored in nwt[j+nin*i]. This - is the length of the projection of the vector onto the "j"th pixel - axis. */ - s = 0.0; - sj = 0.0; - for( j2 = 0; j2 < nin; j2++ ) { - if( ptr1[ j2 ][ 0 ] != AST__BAD ) { - dg = ptr1[ j2 ][ 0 ] - g0[ j2 ]; - if( j2 != j ) { - s += dg*dg; - } else { - sj = fabs( dg ); - } - } else { - s = AST__BAD; - break; - } - } - if( s != AST__BAD && sj != 0.0 ) { - ntn[ j ] = sqrt( s )/sj; - nwt[ j ] = sj; - } - } - } - } - -/* Loop until every grid axes has been assigned to a WCS axis. */ - while( 1 ) { - -/* Pass through the array of tan values, finding the smallest. Note the - pixel and WCS axis for which the smallest tan value occurs. If the tan - values are equal, favour the one with highest weight. */ - ntn = tn; - nwt = wt; - tnmin = AST__BAD; - wtmax = AST__BAD; - imin = 0; - jmin = 0; - for( i = 0; i < nout; i++ ) { - for( j = 0; j < nin; j++ ) { - if( *ntn != AST__BAD ) { - if( tnmin == AST__BAD || *ntn < tnmin ) { - tnmin = *ntn; - wtmax = *nwt; - imin = i; - jmin = j; - } else if( astEQUAL( *ntn, tnmin ) && *nwt > wtmax ) { - wtmax = *nwt; - imin = i; - jmin = j; - } - } - ntn++; - nwt++; - } - } - -/* Check we found a usable minimum tan value */ - if( tnmin != AST__BAD ) { - -/* Assign the pixel axis to the WCS axis. */ - perm[ imin ] = jmin; - -/* Set bad all the tan values for this pixel and WCS axis pair. This ensures - that the pixel axis will not be assigned to another WCS axis, and that - the WCS will not have another pixel axis assigned to it. */ - ntn = tn; - for( i = 0; i < nout; i++ ) { - for( j = 0; j < nin; j++ ) { - if( i == imin || j == jmin ) *ntn = AST__BAD; - ntn++; - } - } - -/* Leave the loop if no more good tan values were found. */ - } else { - break; - } - } - -/* The above process may have left some WCS axes with out any assigned - pixel axis. We assign the remaining pixel arbitrarily to such axes, - starting with the first remaining pixel axis. Find the lowest unused - pixel axis. */ - for( j = 0; j < nin; j++ ) { - used = 0; - for( i = 0; i < nout; i++ ) { - if( perm[ i ] == j ) { - used = 1; - break; - } - } - if( !used ) break; - } - -/* Now check each WCS axis looking for outputs which were not assigned a - pixel axis in the above process. */ - for( i = 0; i < nout; i++ ) { - if( perm[ i ] == -1 ) { - -/* Use the next unused axis value. */ - perm[ i ] = j++; - -/* Find the next unused axis value. */ - for( ; j < nin; j++ ) { - used = 0; - for( i2 = 0; i2 < nout; i2++ ) { - if( perm[ i2 ] == j ) { - used = 1; - break; - } - } - if( !used ) break; - } - } - } - -/* Indicate success. */ - if( astOK ) ret = 1; - } - -/* Free resources. */ - pset1 = astAnnul( pset1 ); - pset2 = astAnnul( pset2 ); - g0 = astFree( g0 ); - w0 = astFree( w0 ); - tn = astFree( tn ); - wt = astFree( wt ); - dw = astFree( dw ); - -/* Now, if we can use the TAB algorithm, deal with Mappings that are defined only in the forward direction. */ - } else if( astGetTranForward( map ) && astGetTabOK( this ) > 0 ) { - -/* Assume success. */ - ret = 1; - -/* Initialise to indicate no outputs have yet been assigned. */ - for( i = 0; i < nout; i++ ) perm[ i ] = -1; - -/* Find the output associated with each input. */ - for( j = 0; j < nin; j++ ) { - -/* Attempt to split off the current input. */ - outs = astMapSplit( map, 1, &j, &smap ); - -/* If successfull, store the index of the corresponding input for each - output. */ - if( outs && smap ) { - nouts = astGetNout( smap ); - for( i = 0; i < nouts; i++ ) { - if( perm[ outs[ i ] ] == -1 ) { - perm[ outs[ i ] ] = j; - } else { - ret = 0; - } - } - } - -/* Free resources. */ - outs = astFree( outs ); - if( smap ) smap = astAnnul( smap ); - } - -/* Check all outputs were assigned . */ - for( i = 0; i < nout && ret; i++ ) { - if( perm[ i ] == -1 ) ret = 0; - } - -/* If succesful, attempt to remove any duplicates from the "perm" array - (i.e. inputs that supply more than one output). First get a list of - the inputs that are currently unused (i.e. do not appear in "perm"). */ - if( ret ) { - -/* Check each input. */ - for( j = 0; j < nin; j++ ) { - -/* See how many outputs are fed by this input. */ - nused = 0; - for( i = 0; i < nout; i++ ) { - if( perm[ i ] == j ) nused++; - } - -/* If it used more than once, we need to remove all but one of the - occurrences. */ - if( nused > 1 ) { - -/* Choose the occurrence to retain. If the output with the same index as - the input is one of them, use it. Otherwise, use the first occurrence. */ - if( perm[ j ] == j ) { - retain = j; - } else { - for( i = 0; i < nout; i++ ) { - if( perm[ i ] == j ) { - retain = i; - break; - } - } - } - -/* Loop round all occurrences of this input again. */ - for( i = 0; i < nout && ret; i++ ) { - if( perm[ i ] == j ) { - -/* Replace all occurrences, except for the one being retained. */ - if( i != retain ) { - -/* Replace it with the next unused input. */ - for( j2 = 0; j2 < nin; j2++ ) { - used = 0; - for( i2 = 0; i2 < nout; i2++ ) { - if( perm[ i2 ] == j2 ) { - used = 1; - break; - } - } - if( ! used ) { - perm[ i ] = j2; - break; - } - } - -/* If there were no unused inputs, we cannot do it. */ - if( used ) ret = 0; - } - } - } - } - } - } - } - -/* Free resources. */ - map = astAnnul( map ); - -/* Return the result. */ - return ret; -} - -static int Write( AstChannel *this_channel, AstObject *object, int *status ) { -/* -* Name: -* Write - -* Purpose: -* Write an Object to a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* int Write( AstChannel *this, AstObject *object, int *status ) - -* Class Membership: -* FitsChan member function (over-rides the astWrite method -* inherited from the Channel class). - -* Description: -* This function writes an Object to a FitsChan. - -* Parameters: -* this -* Pointer to the FitsChan. -* object -* Pointer to the Object which is to be written. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* The number of Objects written to the FitsChan by this invocation of -* astWrite. - -* Notes: -* - A value of zero will be returned if this function is invoked -* with the AST error status set, or if it should fail for any -* reason. -* - The Base Frame in the FrameSet is used as the pixel Frame, and -* the Current Frame is used to create the primary axis descriptions. -* Attempts are made to create secondary axis descriptions for any -* other Frames in the FrameSet (up to a total of 26). -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - FitsStore *store; /* Intermediate storage for WCS information */ - char banner[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; /* Buffer for begin/end banner */ - const char *class; /* Pointer to string holding object class */ - const char *method; /* Pointer to string holding calling method */ - double *dim; /* Pointer to array of axis dimensions */ - int card0; /* Index of original current card */ - int comm; /* Value of Comm attribute */ - int encoding; /* FITS encoding scheme to use */ - int i; /* Axis index */ - int naxis; /* No. of pixel axes */ - int ret; /* Number of objects read */ - -/* Initialise. */ - ret = 0; - -/* Check the global error status. */ - if ( !astOK ) return ret; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_channel); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* Store the calling method, and object class. */ - method = "astWrite"; - class = astGetClass( this ); - -/* The original current card is re-instated at the end if no object - is written. Save its index. */ - card0 = astGetCard( this ); - -/* Indicate that all cards added to the FitsCHan by this call should be - marked as "new". */ - mark_new = 1; - -/* Get the encoding scheme used by the FitsChan. */ - encoding = astGetEncoding( this ); - -/* First deal with cases where we are writing to a FitsChan in which AST - objects are encoded using native AST-specific keywords... */ - if( encoding == NATIVE_ENCODING ){ - -/* Increment the nesting level which keeps track of recursive - invocations of this function. */ - write_nest++; - -/* Initialise the current indentation level for top-level objects. */ - if ( !write_nest ) current_indent = 0; - -/* Obtain the value of the Comm attribute. */ - comm = astGetComment( this ); - -/* If this is the top-level invocation (i.e. we are about to write out - a new top-level Object), then prefix it with a blank FITS line and - an appropriate banner of FITS comments, unless comments have been - suppressed. */ - if ( !write_nest && comm ) { - astSetFitsCom( this, " ", "", 0 ); - MakeBanner( -"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", - "", "", banner, status ); - astSetFitsCom( this, "COMMENT", banner, 0 ); - if( astIsAFrameSet( object ) ) { - MakeBanner( "WCS information in AST format", "", "", banner, status ); - astSetFitsCom( this, "COMMENT", banner, 0 ); - MakeBanner( "See http://www.starlink.ac.uk/ast/", "", "", banner, status ); - astSetFitsCom( this, "COMMENT", banner, 0 ); - } - MakeBanner( HEADER_TEXT, astGetClass( object ), " object", banner, status ); - astSetFitsCom( this, "COMMENT", banner, 0 ); - MakeBanner( -"................................................................", - "", "", banner, status ); - astSetFitsCom( this, "COMMENT", banner, 0 ); - } - -/* Invoke the parent astWrite method to write out the Object data. */ - (*parent_write)( this_channel, object, status ); - -/* Append a banner of FITS comments to the object data, as above, if - necessary. */ - if ( !write_nest && comm ) { - MakeBanner( -"................................................................", - "", "", banner, status ); - astSetFitsCom( this, "COMMENT", banner, 0 ); - MakeBanner( FOOTER_TEXT, astGetClass( object ), " object", banner, status ); - astSetFitsCom( this, "COMMENT", banner, 0 ); - MakeBanner( -"----------------------------------------------------------------", - "", "", banner, status ); - astSetFitsCom( this, "COMMENT", banner, 0 ); - } - -/* Return the nesting level to its previous value. */ - write_nest--; - -/* Indicate that an object has been written. */ - ret = 1; - -/* Now deal with cases where we are writing to a FitsChan in which AST - objects are encoded using any of the supported foreign encodings... */ - } else { - -/* Only proceed if the supplied object is a FrameSet. */ - if( astIsAFrameSet( object ) ){ - -/* Note the number of pixel (i.e. Base Frame) axes, and allocate memory to - hold the image dimensions. */ - naxis = astGetNin( (AstFrameSet *) object ); - dim = (double *) astMalloc( sizeof(double)*naxis ); - if( dim ){ - -/* Note the image dimensions, if known. If not, store AST__BAD values. */ - for( i = 0; i < naxis; i++ ){ - if( !astGetFitsF( this, FormatKey( "NAXIS", i + 1, -1, ' ', status ), - dim + i ) ) dim[ i ] = AST__BAD; - } - -/* Extract the required information from the FrameSet into a standard - intermediary structure called a FitsStore. The indices of any - celestial axes are returned. */ - store = FsetToStore( this, (AstFrameSet *) object, naxis, dim, - encoding, method, class, status ); - -/* If the FrameSet cannot be described in terms of any of the supported - FITS encodings, a null pointer will have been returned. */ - if( store ){ - -/* Now put header cards describing the contents of the FitsStore into the - supplied FitsChan, using the requested encoding. Zero or one is - returned depending on whether the information could be encoded. */ - ret = FitsFromStore( this, store, encoding, dim, - (AstFrameSet *) object, method, class, status ); - -/* Release the resources used by the FitsStore. */ - store = FreeStore( store, status ); - -/* If the Object was written to the FitsChan, set the current card to - end-of-file. */ - if( ret ) astSetCard( this, INT_MAX ); - } - -/* Free workspace holding image dimensions */ - dim = (double *) astFree( (void *) dim ); - } - } - } - -/* If an error has occurred, return zero and remove any new cards added - to the FitsCHan by this call. */ - if( !astOK ) ret = 0; - -/* Clear the new flag associated with cards which have been added to the - FitsChan as a result of this function. If the object was not added - succesfully to the FitsChan, remove any cards which were added before - the error was discovered. */ - FixNew( this, NEW1, !ret, method, class, status ); - FixNew( this, NEW2, !ret, method, class, status ); - -/* Indicate that all cards added to the FitsChan from now on should not be - marked as "new". */ - mark_new = 0; - -/* If no object was written, re-instate the original current card. */ - if( !ret ) astSetCard( this, card0 ); - -/* Return the answer. */ - return ret; -} - -static void WriteBegin( AstChannel *this_channel, const char *class, - const char *comment, int *status ) { -/* -* Name: -* WriteBegin - -* Purpose: -* Write a "Begin" data item to a data sink. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WriteBegin( AstChannel *this, const char *class, -* const char *comment ) - -* Class Membership: -* FitsChan member function (over-rides the protected astWriteBegin -* method inherited from the Channel class). - -* Description: -* This function writes a "Begin" data item to the data sink -* associated with a FitsChan, so as to begin the output of a new -* Object definition. - -* Parameters: -* this -* Pointer to the FitsChan. -* class -* Pointer to a constant null-terminated string containing the -* name of the class to which the Object belongs. -* comment -* Pointer to a constant null-terminated string containing a -* textual comment to be associated with the "Begin" -* item. Normally, this will describe the purpose of the Object. - -* Notes: -* - The comment supplied may not actually be used, depending on -* the nature of the FitsChan supplied. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure. */ - char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; - /* Character buffer */ - char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_channel); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Increment the indentation level for comments. */ - current_indent += INDENT_INC; - -/* If we are not beginning a top-level Object definition, and helpful - information has not been suppressed, generate an indented comment - to mark the "Begin" item and write it to the FitsChan as a comment - card with a blank keyword. */ - if ( write_nest && ( astGetFull( this ) >= 0 ) ) { - MakeIndentedComment( current_indent, '+', "Beginning of ", class, buff, status ); - astSetFitsCom( this, " ", buff, 0 ); - } - -/* Create a unique FITS keyword for this "Begin" item, basing it on - "BEGAST". */ - CreateKeyword( this, "BEGAST", keyword, status ); - -/* Generate a pre-quoted version of the class name. */ - PreQuote( class, buff, status ); - -/* Write the "Begin" item to the FitsChan as a keyword and string - value. */ - astSetFitsS( this, keyword, buff, - astGetComment( this ) ? comment : NULL, 0 ); - -/* Clear the count of items written. */ - items_written = 0; -} - -static void WriteDouble( AstChannel *this_channel, const char *name, - int set, int helpful, - double value, const char *comment, int *status ) { -/* -* Name: -* WriteDouble - -* Purpose: -* Write a double value to a data sink. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WriteDouble( AstChannel *this, const char *name, -* int set, int helpful, -* double value, const char *comment ) - -* Class Membership: -* FitsChan member function (over-rides the protected -* astWriteDouble method inherited from the Channel class). - -* Description: -* This function writes a named double value, representing the -* value of a class instance variable, to the data sink associated -* with a FitsChan. It is intended for use by class "Dump" -* functions when writing out class information which will -* subsequently be re-read. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a constant null-terminated string containing the -* name to be used to identify the value in the external -* representation. This will form the key for identifying it -* again when it is re-read. The name supplied should be unique -* within its class. -* -* Mixed case may be used and will be preserved in the external -* representation (where possible) for cosmetic effect. However, -* case is not significant when re-reading values. -* -* It is recommended that a maximum of 6 alphanumeric characters -* (starting with an alphabetic character) be used. This permits -* maximum flexibility in adapting to standard external data -* representations (e.g. FITS). -* set -* If this is zero, it indicates that the value being written is -* a default value (or can be re-generated from other values) so -* need not necessarily be written out. Such values will -* typically be included in the external representation with -* (e.g.) a comment character so that they are available to -* human readers but will be ignored when re-read. They may also -* be completely omitted in some circumstances. -* -* If "set" is non-zero, the value will always be explicitly -* included in the external representation so that it can be -* re-read. -* helpful -* This flag provides a hint about whether a value whose "set" -* flag is zero (above) should actually appear at all in the -* external representaton. -* -* If the external representation allows values to be "commented -* out" then, by default, values will be included in this form -* only if their "helpful" flag is non-zero. Otherwise, they -* will be omitted entirely. When possible, omitting the more -* obscure values associated with a class is recommended in -* order to improve readability. -* -* This default behaviour may be further modified if the -* FitsChan's Full attribute is set - either to permit all -* values to be shown, or to suppress non-essential information -* entirely. -* value -* The value to be written. -* comment -* Pointer to a constant null-terminated string containing a -* textual comment to be associated with the value. -* -* Note that this comment may not actually be used, depending on -* the nature of the FitsChan supplied and the setting of its -* Comm attribute. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure. */ - char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_channel); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Use the "set" and "helpful" flags, along with the FitsChan's - attributes to decide whether this value should actually be - written. */ - if ( Use( this, set, helpful, status ) ) { - -/* Create a unique FITS keyword from the name supplied. */ - CreateKeyword( this, name, keyword, status ); - -/* Write the value to the FitsChan as a keyword and value */ - astSetFitsF( this, keyword, value, - astGetComment( this ) ? comment : NULL, 0 ); - -/* If the value is not "set", replace the card just written by a COMMENT - card containing the text of the card as the comment. */ - if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status ); - -/* Increment the count of items written. */ - items_written++; - } -} - -static void WriteEnd( AstChannel *this_channel, const char *class, int *status ) { -/* -* Name: -* WriteEnd - -* Purpose: -* Write an "End" data item to a data sink. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WriteEnd( AstChannel *this, const char *class ) - -* Class Membership: -* FitsChan member function (over-rides the protected astWriteEnd -* method inherited from the Channel class). - -* Description: -* This function writes an "End" data item to the data sink -* associated with a FitsChan. This item delimits the end of an -* Object definition. - -* Parameters: -* this -* Pointer to the FitsChan. -* class -* Pointer to a constant null-terminated string containing the -* class name of the Object whose definition is being terminated -* by the "End" item. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure. */ - char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; - /* Character buffer */ - char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_channel); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Create a unique FITS keyword for this "End" item, basing it on - "ENDAST". */ - CreateKeyword( this, "ENDAST", keyword, status ); - -/* Generate a pre-quoted version of the class name. */ - PreQuote( class, buff, status ); - -/* Write the "End" item to the FitsChan as a keyword and string - value. */ - astSetFitsS( this, keyword, buff, - astGetComment( this ) ? "End of object definition" : NULL, - 0 ); - -/* If we are not ending a top-level Object definition, and helpful - information has not been suppressed, generate an indented comment - to mark the "End" item and write it to the FitsChan as a comment - card with a blank keyword. */ - if ( write_nest && ( astGetFull( this ) >= 0 ) ) { - MakeIndentedComment( current_indent, '-', "End of ", class, buff, status ); - astSetFitsCom( this, " ", buff, 0 ); - } - -/* Decrement the indentation level for comments. */ - current_indent -= INDENT_INC; -} - -static void WriteFits( AstFitsChan *this, int *status ){ - -/* -*++ -* Name: -c astWriteFits -f AST_WRITEFITS - -* Purpose: -* Write out all cards in a FitsChan to the sink function. - -* Type: -* Public virtual function. - -* Synopsis: -c #include "fitschan.h" -c void astWriteFits( AstFitsChan *this ) -f CALL AST_WRITEFITS( THIS, STATUS ) - -* Class Membership: -* FitsChan method. - -* Description: -c This function -f This routine -* writes out all cards currently in the FitsChan. If the SinkFile -* attribute is set, they will be written out to the specified sink file. -* Otherwise, they will be written out using the sink function specified -* when the FitsChan was created. All cards are then deleted from the -* FitsChan. - -* Parameters: -c this -f THIS = INTEGER (Given) -* Pointer to the FitsChan. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Notes: -* - If the SinkFile is unset, and no sink function is available, this -* method simply empties the FitsChan, and is then equivalent to -c astEmptyFits. -f AST_EMPTYFITS. -* - This method attempt to execute even if an error has occurred -* previously. -*-- -*/ - -/* Ensure a FitsChan was supplied. */ - if( this ) { - -/* Ensure the source function has been called */ - ReadFromSource( this, status ); - -/* We can usefully use the local destructor function to do the work, - since it only frees resources used within teh FitsChan, rather than - freeing the FitsChan itself. */ - Delete( (AstObject *) this, status ); - } -} - -static void WriteInt( AstChannel *this_channel, const char *name, - int set, int helpful, - int value, const char *comment, int *status ) { -/* -* Name: -* WriteInt - -* Purpose: -* Write an int value to a data sink. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WriteInt( AstChannel *this, const char *name, -* int set, int helpful, -* int value, const char *comment ) - -* Class Membership: -* FitsChan member function (over-rides the protected -* astWriteInt method inherited from the Channel class). - -* Description: -* This function writes a named int value, representing the -* value of a class instance variable, to the data sink associated -* with a FitsChan. It is intended for use by class "Dump" -* functions when writing out class information which will -* subsequently be re-read. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a constant null-terminated string containing the -* name to be used to identify the value in the external -* representation. This will form the key for identifying it -* again when it is re-read. The name supplied should be unique -* within its class. -* -* Mixed case may be used and will be preserved in the external -* representation (where possible) for cosmetic effect. However, -* case is not significant when re-reading values. -* -* It is recommended that a maximum of 6 alphanumeric characters -* (starting with an alphabetic character) be used. This permits -* maximum flexibility in adapting to standard external data -* representations (e.g. FITS). -* set -* If this is zero, it indicates that the value being written is -* a default value (or can be re-generated from other values) so -* need not necessarily be written out. Such values will -* typically be included in the external representation with -* (e.g.) a comment character so that they are available to -* human readers but will be ignored when re-read. They may also -* be completely omitted in some circumstances. -* -* If "set" is non-zero, the value will always be explicitly -* included in the external representation so that it can be -* re-read. -* helpful -* This flag provides a hint about whether a value whose "set" -* flag is zero (above) should actually appear at all in the -* external representaton. -* -* If the external representation allows values to be "commented -* out" then, by default, values will be included in this form -* only if their "helpful" flag is non-zero. Otherwise, they -* will be omitted entirely. When possible, omitting the more -* obscure values associated with a class is recommended in -* order to improve readability. -* -* This default behaviour may be further modified if the -* FitsChan's Full attribute is set - either to permit all -* values to be shown, or to suppress non-essential information -* entirely. -* value -* The value to be written. -* comment -* Pointer to a constant null-terminated string containing a -* textual comment to be associated with the value. -* -* Note that this comment may not actually be used, depending on -* the nature of the FitsChan supplied and the setting of its -* Comm attribute. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure. */ - char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_channel); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Use the "set" and "helpful" flags, along with the FitsChan's - attributes to decide whether this value should actually be - written. */ - if ( Use( this, set, helpful, status ) ) { - -/* Create a unique FITS keyword from the name supplied. */ - CreateKeyword( this, name, keyword, status ); - -/* Write the value to the FitsChan as a keyword and value */ - astSetFitsI( this, keyword, value, - astGetComment( this ) ? comment : NULL, 0 ); - -/* If the value is not "set", replace the card just written by a COMMENT - card containing the text of the card as the comment. */ - if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status ); - -/* Increment the count of items written. */ - items_written++; - } -} - -static void WriteIsA( AstChannel *this_channel, const char *class, - const char *comment, int *status ) { -/* -* Name: -* WriteIsA - -* Purpose: -* Write an "IsA" data item to a data sink. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WriteIsA( AstChannel *this, const char *class, -* const char *comment ) - -* Class Membership: -* FitsChan member function (over-rides the protected astWriteIsA -* method inherited from the Channel class). - -* Description: -* This function writes an "IsA" data item to the data sink -* associated with a FitsChan. This item delimits the end of the -* data associated with the instance variables of a class, as part -* of an overall Object definition. - -* Parameters: -* this -* Pointer to the FitsChan. -* class -* Pointer to a constant null-terminated string containing the -* name of the class whose data are terminated by the "IsA" -* item. -* comment -* Pointer to a constant null-terminated string containing a -* textual comment to be associated with the "IsA" -* item. Normally, this will describe the purpose of the class -* whose data are being terminated. - -* Notes: -* - The comment supplied may not actually be used, depending on -* the nature of the FitsChan supplied. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure. */ - char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; - /* Character buffer */ - char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_channel); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Output an "IsA" item only if there has been at least one item - written since the last "Begin" or "IsA" item, or if the Full - attribute for the Channel is greater than zero (requesting maximum - information). */ - if ( items_written || astGetFull( this ) > 0 ) { - -/* Create a unique FITS keyword for this "IsA" item, basing it on - "ISA". */ - CreateKeyword( this, "ISA", keyword, status ); - -/* Generate a pre-quoted version of the class name. */ - PreQuote( class, buff, status ); - -/* Write the "IsA" item to the FitsChan as a keyword and string - value. */ - astSetFitsS( this, keyword, buff, - astGetComment( this ) ? comment : NULL, 0 ); - -/* If helpful information has not been suppressed, generate an - indented comment to mark the "IsA" item and write it to the - FitsChan as a comment card with a blank keyword. */ - if ( astGetFull( this ) >= 0 ) { - MakeIndentedComment( current_indent, '.', "Class boundary", "", - buff, status ); - astSetFitsCom( this, " ", buff, 0 ); - } - } - -/* Clear the count of items written. */ - items_written = 0; -} - -static void WriteObject( AstChannel *this_channel, const char *name, - int set, int helpful, - AstObject *value, const char *comment, int *status ) { -/* -* Name: -* WriteObject - -* Purpose: -* Write an Object value to a data sink. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WriteObject( AstChannel *this, const char *name, -* int set, int helpful, -* AstObject *value, const char *comment ) - -* Class Membership: -* FitsChan member function (over-rides the protected -* astWriteObject method inherited from the Channel class). - -* Description: -* This function writes a named Object value, representing the -* value of a class instance variable, to the data sink associated -* with a FitsChan. It is intended for use by class "Dump" -* functions when writing out class information which will -* subsequently be re-read. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a constant null-terminated string containing the -* name to be used to identify the value in the external -* representation. This will form the key for identifying it -* again when it is re-read. The name supplied should be unique -* within its class. -* -* Mixed case may be used and will be preserved in the external -* representation (where possible) for cosmetic effect. However, -* case is not significant when re-reading values. -* -* It is recommended that a maximum of 6 alphanumeric characters -* (starting with an alphabetic character) be used. This permits -* maximum flexibility in adapting to standard external data -* representations (e.g. FITS). -* set -* If this is zero, it indicates that the value being written is -* a default value (or can be re-generated from other values) so -* need not necessarily be written out. Such values will -* typically be included in the external representation with -* (e.g.) a comment character so that they are available to -* human readers but will be ignored when re-read. They may also -* be completely omitted in some circumstances. -* -* If "set" is non-zero, the value will always be explicitly -* included in the external representation so that it can be -* re-read. -* helpful -* This flag provides a hint about whether a value whose "set" -* flag is zero (above) should actually appear at all in the -* external representaton. -* -* If the external representation allows values to be "commented -* out" then, by default, values will be included in this form -* only if their "helpful" flag is non-zero. Otherwise, they -* will be omitted entirely. When possible, omitting the more -* obscure values associated with a class is recommended in -* order to improve readability. -* -* This default behaviour may be further modified if the -* FitsChan's Full attribute is set - either to permit all -* values to be shown, or to suppress non-essential information -* entirely. -* value -* A pointer to the Object to be written. -* comment -* Pointer to a constant null-terminated string containing a -* textual comment to be associated with the value. -* -* Note that this comment may not actually be used, depending on -* the nature of the FitsChan supplied and the setting of its -* Comm attribute. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure. */ - char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_channel); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Use the "set" and "helpful" flags, along with the FitsChan's - attributes to decide whether this value should actually be - written. */ - if ( Use( this, set, helpful, status ) ) { - -/* Create a unique FITS keyword from the name supplied. */ - CreateKeyword( this, name, keyword, status ); - -/* Write the value to the FitsChan as a keyword and a blank string value, - not pre-quoted (this "null" value indicates that an Object description - follows). */ - astSetFitsS( this, keyword, "", - astGetComment( this ) ? comment : NULL, 0 ); - -/* If the value is "set", write out the Object description. */ - if ( set ) { - astWrite( this, value ); - -/* If the value is not set, replace the card just written to the FitsChan - by COMENT card containing the keyword and blank string value (do not - write out the Object description). */ - } else { - MakeIntoComment( this, "astWrite", astGetClass( this ), status ); - } - -/* Increment the count of items written. */ - items_written++; - } -} - -static void WriteToSink( AstFitsChan *this, int *status ){ -/* -* Name: -* WriteToSink - -* Purpose: -* Write the contents of the FitsChan out to the sink file or function. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WriteToSink( AstFitsChan *this, int *status ) - -* Class Membership: -* FitsChan member function. - -* Description: -* If the SinkFile attribute is set, each card in the FitsChan is -* written out to the sink file. Otherwise, the cards are passed in -* turn to the sink function specified when the FitsChan was created. -* If no sink function was provided, the cards are not written out. -* Cards marked as having been read into an AST object are not written -* out. - -* Parameters: -* this -* Pointer to the FitsChan. -* status -* Pointer to the inherited status variable. - -* Notes: -* - The current card is left unchanged. -*/ - -/* Local Constants: */ -#define ERRBUF_LEN 80 - -/* Local Variables: */ - FILE *fd; /* File descriptor for sink file */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - char *errstat; /* Pointer for system error message */ - char card[ AST__FITSCHAN_FITSCARDLEN + 1]; /* Buffer for header card */ - char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ - const char *sink_file; /* Path to output sink file */ - int icard; /* Current card index on entry */ - int old_ignore_used; /* Original value of external variable ignore_used */ - -/* Check the global status. */ - if( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this); - -/* If the SinkFile attribute is set, open the file. */ - fd = NULL; - if( astTestSinkFile( this ) ) { - sink_file = astGetSinkFile( this ); - fd = fopen( sink_file, "w" ); - if( !fd ) { - if ( errno ) { -#if HAVE_STRERROR_R - strerror_r( errno, errbuf, ERRBUF_LEN ); - errstat = errbuf; -#else - errstat = strerror( errno ); -#endif - astError( AST__WRERR, "astDelete(%s): Failed to open output " - "SinkFile '%s' - %s.", status, astGetClass( this ), - sink_file, errstat ); - } else { - astError( AST__WRERR, "astDelete(%s): Failed to open output " - "SinkFile '%s'.", status, astGetClass( this ), - sink_file ); - } - } - } - -/* Only proceed if a file was opened, or sink function and wrapper were supplied. */ - if( fd || ( this->sink && this->sink_wrap ) ){ - -/* Store the current card index. */ - icard = astGetCard( this ); - -/* Indicate that cards which have been read into an AST object should skipped - over by the functions which navigate the linked list of cards. */ - old_ignore_used = ignore_used; - ignore_used = 1; - -/* Ensure that the first card in the FitsChan will be the next one to be - read. */ - astSetCard( this, 1 ); - -/* Loop round obtaining and writing out each card, until all cards have been - processed. */ - while( !astFitsEof( this ) && astOK ){ - -/* Get the current card, and write it out through the sink function. - The call to astFindFits increments the current card. */ - if( astFindFits( this, "%f", card, 1 ) ) { - -/* If s sink file was opened, write the card out to it. */ - if( fd ) { - fprintf( fd, "%s\n", card ); - -/* Otherwise, use the isnk function. The sink function is an externally - supplied function which may not be thread-safe, so lock a mutex first. - Also store the channel data pointer in a global variable so that it can - be accessed in the sink function using macro astChannelData. */ - } else { - astStoreChannelData( this ); - LOCK_MUTEX3; - ( *this->sink_wrap )( *this->sink, card, status ); - UNLOCK_MUTEX3; - } - } - } - -/* Re-instate the original flag indicating if cards marked as having been - read should be skipped over. */ - ignore_used = old_ignore_used; - -/* Set the current card index back to what it was on entry. */ - astSetCard( this, icard ); - } - -/* Close the sink file. */ - if( fd ) fclose( fd ); -} - -static void WriteString( AstChannel *this_channel, const char *name, - int set, int helpful, - const char *value, const char *comment, int *status ) { -/* -* Name: -* WriteString - -* Purpose: -* Write a string value to a data sink. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* void WriteString( AstChannel *this, const char *name, -* int set, int helpful, -* const char *value, const char *comment ) - -* Class Membership: -* FitsChan member function (over-rides the protected -* astWriteString method inherited from the Channel class). - -* Description: -* This function writes a named string value, representing the -* value of a class instance variable, to the data sink associated -* with a FitsChan. It is intended for use by class "Dump" -* functions when writing out class information which will -* subsequently be re-read. - -* Parameters: -* this -* Pointer to the FitsChan. -* name -* Pointer to a constant null-terminated string containing the -* name to be used to identify the value in the external -* representation. This will form the key for identifying it -* again when it is re-read. The name supplied should be unique -* within its class. -* -* Mixed case may be used and will be preserved in the external -* representation (where possible) for cosmetic effect. However, -* case is not significant when re-reading values. -* -* It is recommended that a maximum of 6 alphanumeric characters -* (starting with an alphabetic character) be used. This permits -* maximum flexibility in adapting to standard external data -* representations (e.g. FITS). -* set -* If this is zero, it indicates that the value being written is -* a default value (or can be re-generated from other values) so -* need not necessarily be written out. Such values will -* typically be included in the external representation with -* (e.g.) a comment character so that they are available to -* human readers but will be ignored when re-read. They may also -* be completely omitted in some circumstances. -* -* If "set" is non-zero, the value will always be explicitly -* included in the external representation so that it can be -* re-read. -* helpful -* This flag provides a hint about whether a value whose "set" -* flag is zero (above) should actually appear at all in the -* external representaton. -* -* If the external representation allows values to be "commented -* out" then, by default, values will be included in this form -* only if their "helpful" flag is non-zero. Otherwise, they -* will be omitted entirely. When possible, omitting the more -* obscure values associated with a class is recommended in -* order to improve readability. -* -* This default behaviour may be further modified if the -* FitsChan's Full attribute is set - either to permit all -* values to be shown, or to suppress non-essential information -* entirely. -* value -* Pointer to a constant null-terminated string containing the -* value to be written. -* comment -* Pointer to a constant null-terminated string containing a -* textual comment to be associated with the value. -* -* Note that this comment may not actually be used, depending on -* the nature of the FitsChan supplied and the setting of its -* Comm attribute. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - AstFitsChan *this; /* Pointer to the FitsChan structure. */ - char *c; /* Pointer to next buffer character */ - char buff1[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ]; /* Buffer for a single substring */ - char buff2[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ]; /* Buffer for pre-quoted string */ - char cc; /* Next character */ - char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ - const char *start; /* Pointer to start of substring */ - int first; /* Is this the first sub-string? */ - int nc; /* No. of available columns remaining */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_channel); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_channel; - -/* Use the "set" and "helpful" flags, along with the FitsChan's - attributes to decide whether this value should actually be - written. */ - if ( Use( this, set, helpful, status ) ) { - -/* Create a unique FITS keyword from the name supplied. */ - CreateKeyword( this, name, keyword, status ); - -/* Store a pointer to the start of the next sub-string (i.e. the - beggining of the string), and then loop round until the end of the - string is reached. */ - start = value; - first = 1; - while( *start && astOK ){ - -/* Store the number of characters available in the 80 column header card - for the next substring, leaving room for the "= " string at the start, - and the delimiting quotes. Also reserve 2 characters to allow for the - possibility of double quotes being needed to protect trailing white space - (see function PreQuote). */ - nc = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 6; - -/* If this is the first sub-string reserve room for any comment. */ - if( first ){ - if( comment && comment[0] ) nc -= ChrLen( comment, status ) + 3; - -/* If the first card will be turned into a comment card, we need to leave room - for the keyword name and equals sign, etc, within the 80 columns. */ - if( !set ) nc -= FITSNAMLEN + 5; - } - -/* We need to check the sub-string for single quotes since these will - take up 2 characters each instead of 1 when encoded since single quotes - within a string are doubled. Search through from the starting - character, copying the sub-string into a buffer, and reducing the number - of available characters remaining in the card for each character. */ - c = buff1; - while( *start && nc > 0 ){ - cc = *(start++); - *(c++) = cc; - if( cc == '\'' ) { - nc -= 2; - } else { - nc -= 1; - } - } - -/* If the last character in the substring was a single quote, there may - not have been room for the extra quote which is added when the - sub-string is encoded. In this case we need to back up a character in - order to remove the single quote frin this substring and move it into - the next sub-string. */ - if( nc < 0 ){ - start--; - c--; - } - -/* If the supplied value has not been exhausted, append an ampersand to - the string. In this case we need to move the last character in the - substring into the next substring to make room for the ampersand. */ - if( *start ) { - start--; - c--; - *(c++) = '&'; - } - -/* Terminate the buffer. */ - *c = 0; - -/* The FITS standard considers trailing white space is be insignificant, - and so we need to guard against external applications throwing away - significant trailing white space. This is done by encosing the string, - including trailing white space, in double quotes. */ - PreQuote( buff1, buff2, status ); - -/* On the first pass through this loop, write the value to the FitsChan as - a keyword and value */ - if( first ){ - astSetFitsS( this, keyword, buff2, - astGetComment( this ) ? comment : NULL, 0 ); - -/* If the value is not "set", replace the card just written by a COMMENT - card containing the text of the card as the comment. */ - if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status ); - -/* On subsequent passes through the loop, store the string using a CONTINUE - keyword, with type AST__CONTINUE (this type is like AST__STRING but is - formatted without an equals sign). */ - } else { - astSetFitsCN( this, "CONTINUE", buff2, NULL, 0 ); - } - first = 0; - } - -/* Increment the count of items written. */ - items_written++; - } -} - -static AstMapping *ZPXMapping( AstFitsChan *this, FitsStore *store, char s, - int naxes, int zpxaxes[2], const char *method, - const char *class, int *status ){ -/* -* Name: -* ZPXMapping - -* Purpose: -* Create a Mapping descriping "-ZPX" (IRAF) distortion. - -* Type: -* Private function. - -* Synopsis: -* AstMapping *ZPXMapping( AstFitsChan *this, FitsStore *store, char s, -* int naxes, int zpxaxes[2], const char *method, -* const char *class, int *status ) - -* Class Membership: -* FitsChan - -* Description: -* This function uses the values in the supplied FitsStore to create a -* Mapping which implements the "-ZPX" distortion code, produced by -* the IRAF project. See: -* -* http://iraf.noao.edu/projects/ccdmosaic/zpx.html -* -* Note, the Mapping created by this function implements the "lngcor" -* and "latcor" corrections described in the WAT... keywords. The -* basic ZPN projection code is handled in the normal way, as any -* other projection is handled. - -* Parameters: -* store -* A structure containing information about the requested axis -* descriptions derived from a FITS header. -* s -* A character identifying the co-ordinate version to use. A space -* means use primary axis descriptions. Otherwise, it must be an -* upper-case alphabetical characters ('A' to 'Z'). -* naxes -* The number of intermediate world coordinate axes (WCSAXES). -* zpxaxes -* The zero-based indices of the two IWC axes that use the ZPX projection. -* method -* A pointer to a string holding the name of the calling method. -* This is used only in the construction of error messages. -* class -* A pointer to a string holding the class of the object being -* read. This is used only in the construction of error messages. -* status -* Pointer to the inherited status variable. - -* Returned Value: -* A pointer to the Mapping. -*/ - -/* Local Variables: */ - AstMapping *ret; - char *watstr; - double *cvals[ 2 ]; - int *mvals[ 2 ]; - int ncoeff[ 2 ]; - int i; - int icoeff; - int ok; - -/* Initialise the pointer to the returned Mapping. */ - ret = NULL; - -/* Check the global status. */ - if ( !astOK ) return ret; - -/* Check both axes */ - for( i = 0; i < 2; i++ ){ - mvals[ i ] = NULL; - cvals[ i ] = NULL; - ncoeff[ i ] = 0; - -/* Concatenate all the IRAF "WAT" keywords together for this axis. These - keywords are marked as having been used, so that they are not written - out when the FitsChan is deleted. */ - watstr = ConcatWAT( this, zpxaxes[ i ], method, class, status ); - -/* Extract the polynomial coefficients from the concatenated WAT string. - These are returned in the form of a list of PVi_m values for a TPN - projection. */ - ncoeff[ i ] = WATCoeffs( watstr, i, cvals + i, mvals + i, &ok, status ); - -/* If the current axis of the ZPX projection uses features not supported - by AST, do not do any more axes. */ - if( !ok ) break; - -/* Free the WAT string. */ - watstr = astFree( watstr ); - } - -/* If we can handle the ZPX projection, store the polynomial coefficients in - a new inverted TPN WcsMap. This WcsMap is used as a correction to the ZPN - WcsMap to be created later, therefore set its FITSProj value to zero so - that it is not used as the FITS projection when written out via - astWrite. Also set TPNTan to zero to indicate that the TAN part of the - TPN projection should not be used (i.e. just use the polynomial part). */ - if( ok && astOK ) { - - if( ncoeff[ 0 ] || ncoeff[ 1 ] ) { - ret = (AstMapping *) astWcsMap( naxes, AST__TPN, zpxaxes[ 0 ] + 1, - zpxaxes[ 1 ] + 1, "Invert=1", - status ); - astSetFITSProj( ret, 0 ); - astSetTPNTan( ret, 0 ); - for( i = 0; i < 2; i++ ){ - for( icoeff = 0; icoeff < ncoeff[ i ]; icoeff++ ) { - astSetPV( ret, zpxaxes[ i ], (mvals[ i ])[ icoeff ], - (cvals[ i ])[ icoeff ] ); - } - } - - } else { - ret = (AstMapping *) astUnitMap( naxes, " ", status ); - } - -/* If the TNX cannot be represented in FITS-WCS (within our restrictions), add - warning keywords to the FitsChan. */ - } else { - Warn( this, "zpx", "This FITS header includes, or was " - "derived from, a ZPX projection which requires " - "unsupported IRAF-specific corrections. The WCS " - "information may therefore be incorrect.", method, class, - status ); - } - -/* Return the result. */ - return ret; -} - -/* Functions which access class attributes. */ -/* ---------------------------------------- */ - -/* Implement member functions to access the attributes associated with - this class using the macros defined for this purpose in the - "object.h" file. For a description of each attribute, see the class - interface (in the associated .h file). */ - -/* -*att++ -* Name: -* FitsTol - -* Purpose: -* Maximum non-linearity allowed when exporting to FITS-WCS. - -* Type: -* Public attribute. - -* Synopsis: -* Floating point. - -* Description: -* This attribute is used when attempting to write a FrameSet to a -* FitsChan using a foreign encoding. It specifies the maximum -* departure from linearity allowed on any axis within the mapping -* from pixel coordinates to Intermediate World Coordinates. It is -* expressed in units of pixels. If an axis of the Mapping is found -* to deviate from linearity by more than this amount, the write -* operation fails. If the linearity test succeeds, a linear -* approximation to the mapping is used to determine the FITS keyword -* values to be placed in the FitsChan. -* -* The default value is one tenth of a pixel. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,FitsTol,fitstol,-1.0) -astMAKE_GET(FitsChan,FitsTol,double,1,(this->fitstol==-1.0?0.1:this->fitstol)) -astMAKE_SET(FitsChan,FitsTol,double,fitstol,(astMAX(value,0.0))) -astMAKE_TEST(FitsChan,FitsTol,(this->fitstol!=-1.0)) - -/* -*att++ -* Name: -* Card - -* Purpose: -* Index of current FITS card in a FitsChan. - -* Type: -* Public attribute. - -* Synopsis: -* Integer. - -* Description: -* This attribute gives the index of the "current" FITS header card -* within a FitsChan, the first card having an index of 1. The -c choice of current card affects the behaviour of functions that -f choice of current card affects the behaviour of routines that -c access the contents of the FitsChan, such as astDelFits, -c astFindFits and astPutFits. -f access the contents of the FitsChan, such as AST_DELFITS, -f AST_FINDFITS and AST_PUTFITS. -* -* A value assigned to Card will position the FitsChan at any -* desired point, so that a particular card within it can be -* accessed. Alternatively, the value of Card may be enquired in -* order to determine the current position of a FitsChan. -* -* The default value of Card is 1. This means that clearing -c this attribute (using astClear) effectively "rewinds" the -f this attribute (using AST_CLEAR) effectively "rewinds" the -* FitsChan, so that the first card is accessed next. If Card is -* set to a value which exceeds the total number of cards in the -* FitsChan (as given by its Ncard attribute), it is regarded as -* pointing at the "end-of-file". In this case, the value returned -* in response to an enquiry is always one more than the number of -* cards in the FitsChan. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ - -/* Encoding. */ -/* ========= */ - -/* -*att++ -* Name: -* Encoding - -* Purpose: -* System for encoding Objects as FITS headers. - -* Type: -* Public attribute. - -* Synopsis: -* String. - -* Description: -* This attribute specifies the encoding system to use when AST -* Objects are stored as FITS header cards in a FitsChan. It -c affects the behaviour of the astWrite and astRead functions when -f affects the behaviour of the AST_WRITE and AST_READ routines when -* they are used to transfer any AST Object to or from an external -* representation consisting of FITS header cards (i.e. whenever a -* write or read operation is performed using a FitsChan as the I/O -* Channel). -* -* There are several ways (conventions) by which coordinate system -* information may be represented in the form of FITS headers and -* the Encoding attribute is used to specify which of these should -* be used. The encoding options available are outlined in the -* "Encodings Available" section below, and in more detail in the -* sections which follow. -* -* Encoding systems differ in the range of possible Objects -* (e.g. classes) they can represent, in the restrictions they -* place on these Objects (e.g. compatibility with some -* externally-defined coordinate system model) and in the number of -* Objects that can be stored together in any particular set of -* FITS header cards (e.g. multiple Objects, or only a single -* Object). The choice of encoding also affects the range of -* external applications which can potentially read and interpret -* the FITS header cards produced. -* -* The encoding options available are not necessarily mutually -* exclusive, and it may sometimes be possible to store multiple -* Objects (or the same Object several times) using different -* encodings within the same set of FITS header cards. This -* possibility increases the likelihood of other applications being -* able to read and interpret the information. -* -* By default, a FitsChan will attempt to determine which encoding -* system is already in use, and will set the default Encoding -* value accordingly (so that subsequent I/O operations adopt the -* same conventions). It does this by looking for certain critical -* FITS keywords which only occur in particular encodings. For -* details of how this works, see the "Choice of Default Encoding" -* section below. If you wish to ensure that a particular encoding -* system is used, independently of any FITS cards already present, -* you should set an explicit Encoding value yourself. - -* Encodings Available: -* The Encoding attribute can take any of the following (case -* insensitive) string values to select the corresponding encoding - -* system: -* -* - "DSS": Encodes coordinate system information in FITS header -* cards using the convention developed at the Space Telescope -* Science Institute (STScI) for the Digitised Sky Survey (DSS) -* astrometric plate calibrations. The main advantages of this -* encoding are that FITS images which use it are widely available -* and it is understood by a number of important and -* well-established astronomy applications. For further details, -* see the section "The DSS Encoding" below. -* -* - "FITS-WCS": Encodes coordinate system information in FITS -* header cards using the conventions described in the FITS -* world coordinate system (FITS-WCS) papers by E.W. Greisen, -* M. Calabretta, et al. The main advantages of this encoding are that -* it should be understood by any FITS-WCS compliant application and -* is likely to be adopted widely for FITS data in future. For further -* details, see the section "The FITS-WCS Encoding" below. -* -* - "FITS-PC": Encodes coordinate system information in FITS -* header cards using the conventions described in an earlier draft -* of the FITS world coordinate system papers by E.W. Greisen and -* M. Calabretta. This encoding uses a combination of CDELTi and -* PCiiijjj keywords to describe the scale and rotation of the pixel -* axes. This encoding is included to support existing data and -* software which uses these now superceded conventions. In general, -* the "FITS-WCS" encoding (which uses CDi_j or PCi_j keywords to -* describe the scale and rotation) should be used in preference to -* "FITS-PC". -* -* - "FITS-IRAF": Encodes coordinate system information in FITS -* header cards using the conventions described in the document -* "World Coordinate Systems Representations Within the FITS -* Format" by R.J. Hanisch and D.G. Wells, 1988. This encoding is -* currently employed by the IRAF data analysis facility, so its -* use will facilitate data exchange with IRAF. Its main advantages -* are that it is a stable convention which approximates to a -* subset of the propsed FITS-WCS encoding (above). This makes it -* suitable as an interim method for storing coordinate system -* information in FITS headers until the FITS-WCS encoding becomes -* stable. Since many datasets currently use the FITS-IRAF -* encoding, conversion of data from FITS-IRAF to the final form of -* FITS-WCS is likely to be well supported. -* -* - "FITS-AIPS": Encodes coordinate system information in FITS -* header cards using the conventions originally introduced by the -* AIPS data analysis facility. This is base on the use of CDELTi and -* CROTAi keuwords to desribe the scale and rotation of each axis. -* These conventions have been superceded but are still widely used. -* -* - "FITS-AIPS++": Encodes coordinate system information in FITS -* header cards using the conventions used by the AIPS++ project. -* This is an extension of FITS-AIPS which includes some of the -* features of FITS-IRAF and FITS-PC. -* -* - "FITS-CLASS": Encodes coordinate system information in FITS -* header cards using the conventions used by the CLASS project. -* CLASS is a software package for reducing single-dish radio and -* sub-mm spectroscopic data. See the section "CLASS FITS format" at -* http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/. -* -* - "NATIVE": Encodes AST Objects in FITS header cards using a -* convention which is private to the AST library (but adheres to -* the general FITS standard) and which uses FITS keywords that -* will not clash with other encoding systems. The main advantages -* of this are that any class of AST Object may be encoded, and any -* (reasonable) number of Objects may be stored sequentially in the -* same FITS header. This makes FITS headers an almost loss-less -* communication path for passing AST Objects between applications -* (although all such applications must, of course, make use of the -* AST library to interpret the information). For further details, -* see the section "The NATIVE Encoding" below. - -* Choice of Default Encoding: -* If the Encoding attribute of a FitsChan is not set, the default -* value it takes is determined by the presence of certain critical -* FITS keywords within the FitsChan. The sequence of decisions - -* used to arrive at the default value is as follows: -* -* - If the FitsChan contains any keywords beginning with the -* string "BEGAST", then NATIVE encoding is used, -* - Otherwise, FITS-CLASS is used if the FitsChan contains a DELTAV -* keyword and a keyword of the form VELO-xxx, where xxx indicates one -* of the rest frames used by class (e.g. "VELO-LSR"), or "VLSR". -* - Otherwise, if the FitsChan contains a CTYPE keyword which -* represents a spectral axis using the conventions of the AIPS and -* AIPS++ projects (e.g. "FELO-LSR", etc), then one of FITS-AIPS or -* FITS-AIPS++ encoding is used. FITS-AIPS++ is used if any of the -* keywords CDi_j, PROJP, LONPOLE or LATPOLE are -* found in the FitsChan. Otherwise FITS-AIPS is used. -* - Otherwise, if the FitsChan contains a keyword of the form -* "PCiiijjj", where "i" and "j" are single digits, then -* FITS-PC encoding is used, -* - Otherwise, if the FitsChan contains a keyword of the form -* "CDiiijjj", where "i" and "j" are single digits, then -* FITS-IRAF encoding is used, -* - Otherwise, if the FitsChan contains a keyword of the form -* "CDi_j", and at least one of RADECSYS, PROJPi, or CjVALi -* where "i" and "j" are single digits, then FITS-IRAF encoding is -* used. -* - Otherwise, if the FitsChan contains any keywords of the form -* PROJPi, CjVALi or RADECSYS, where "i" and "j" are single digits, -* then FITS-PC encoding is used. -* - Otherwise, if the FitsChan contains a keyword of the form -* CROTAi, where "i" is a single digit, then FITS-AIPS encoding is -* used. -* - Otherwise, if the FitsChan contains a keyword of the form -* CRVALi, where "i" is a single digit, then FITS-WCS encoding is -* used. -* - Otherwise, if the FitsChan contains the "PLTRAH" keyword, then -* DSS encoding is used, -* - Otherwise, if none of these conditions is met (as would be the -* case when using an empty FitsChan), then NATIVE encoding is -* used. -* -* Except for the NATIVE and DSS encodings, all the above checks -* also require that the header contains at least one CTYPE, CRPIX and -* CRVAL keyword (otherwise the checking process continues to the next -* case). -* -* Setting an explicit value for the Encoding attribute always -* over-rides this default behaviour. -* -* Note that when writing information to a FitsChan, the choice of -* encoding will depend greatly on the type of application you -* expect to be reading the information in future. If you do not -* know this, there may sometimes be an advantage in writing the -* information several times, using a different encoding on each -* occasion. - -* The DSS Encoding: -* The DSS encoding uses FITS header cards to store a multi-term -* polynomial which relates pixel positions on a digitised -* photographic plate to celestial coordinates (right ascension and -* declination). This encoding may only be used to store a single -* AST Object in any set of FITS header cards, and that Object must -* be a FrameSet which conforms to the STScI/DSS coordinate system -* model (this means the Mapping which relates its base and current -* Frames must include either a DssMap or a WcsMap with type -* AST__TAN or AST__TPN). -* -c When reading a DSS encoded Object (using astRead), the FitsChan -f When reading a DSS encoded Object (using AST_READ), the FitsChan -* concerned must initially be positioned at the first card (its -* Card attribute must equal 1) and the result of the read, if -* successful, will always be a pointer to a FrameSet. The base -* Frame of this FrameSet represents DSS pixel coordinates, and the -* current Frame represents DSS celestial coordinates. Such a read -* is always destructive and causes the FITS header cards required -* for the construction of the FrameSet to be removed from the -* FitsChan, which is then left positioned at the "end-of-file". A -* subsequent read using the same encoding will therefore not -* return another FrameSet, even if the FitsChan is rewound. -* -c When astWrite is used to store a FrameSet using DSS encoding, -f When AST_WRITE is used to store a FrameSet using DSS encoding, -* an attempt is first made to simplify the FrameSet to see if it -* conforms to the DSS model. Specifically, the current Frame must -* be a FK5 SkyFrame; the projection must be a tangent plane -* (gnomonic) projection with polynomial corrections conforming to -* DSS requirements, and north must be parallel to the second base -* Frame axis. -* -* If the simplification process succeeds, a description of the -* FrameSet is written to the FitsChan using appropriate DSS FITS -* header cards. The base Frame of the FrameSet is used to form the -* DSS pixel coordinate system and the current Frame gives the DSS -* celestial coordinate system. A successful write operation will -* over-write any existing DSS encoded data in the FitsChan, but -* will not affect other (non-DSS) header cards. If a destructive -* read of a DSS encoded Object has previously occurred, then an -* attempt will be made to store the FITS header cards back in -* their original locations. -* -* If an attempt to simplify a FrameSet to conform to the DSS model -* fails (or if the Object supplied is not a FrameSet), then no -c data will be written to the FitsChan and astWrite will return -f data will be written to the FitsChan and AST_WRITE will return -* zero. No error will result. - -* The FITS-WCS Encoding: -* The FITS-WCS convention uses FITS header cards to describe the -* relationship between pixels in an image (not necessarily -* 2-dimensional) and one or more related "world coordinate systems". -* The FITS-WCS encoding may only be used to store a single AST Object -* in any set of FITS header cards, and that Object must be a FrameSet -* which conforms to the FITS-WCS model (the FrameSet may, however, -* contain multiple Frames which will be result in multiple FITS -* "alternate axis descriptions"). Details of the use made by this -* Encoding of the conventions described in the FITS-WCS papers are -* given in the appendix "FITS-WCS Coverage" of this document. A few -* main points are described below. -* -* The rotation and scaling of the intermediate world coordinate system -* can be specified using either "CDi_j" keywords, or "PCi_j" together -* with "CDELTi" keywords. When writing a FrameSet to a FitsChan, the -* the value of the CDMatrix attribute of the FitsChan determines -* which system is used. -* -* In addition, this encoding supports the "TAN with polynomial correction -* terms" projection which was included in a draft of the FITS-WCS paper, -* but was not present in the final version. A "TAN with polynomial -* correction terms" projection is represented using a WcsMap with type -* AST__TPN (rather than AST__TAN which is used to represent simple -* TAN projections). When reading a FITS header, a CTYPE keyword value -* including a "-TAN" code results in an AST__TPN projection if there are -* any projection parameters (given by the PVi_m keywords) associated with -* the latitude axis, or if there are projection parameters associated -* with the longitude axis for m greater than 4. When writing a -* FrameSet to a FITS header, an AST__TPN projection gives rise to a -* CTYPE value including the normal "-TAN" code, but the projection -* parameters are stored in keywords with names "QVi_m", instead of the -* usual "PVi_m". Since these QV parameters are not part of the -* FITS-WCS standard they will be ignored by other non-AST software, -* resulting in the WCS being interpreted as a simple TAN projection -* without any corrections. This should be seen as an interim solution -* until such time as an agreed method for describing projection -* distortions within FITS-WCS has been published. -* -* AST extends the range of celestial coordinate systems which may be -* described using this encoding by allowing the inclusion of -* "AZ--" and "EL--" as the coordinate specification within CTYPE -* values. These form a longitude/latitude pair of axes which describe -* azimuth and elevation. The geographic position of the observer -* should be supplied using the OBSGEO-X/Y/Z keywords described in FITS-WCS -* paper III. Currently, a simple model is used which includes diurnal -* aberration, but ignores atmospheric refraction, polar motion, etc. -* These may be added in a later release. -* -* If an AST SkyFrame that represents offset rather than absolute -* coordinates (see attribute SkyRefIs) is written to a FitsChan using -* FITS-WCS encoding, two alternate axis descriptions will be created. -* One will describe the offset coordinates, and will use "OFLN" and -* "OFLT" as the axis codes in the CTYPE keywords. The other will -* describe absolute coordinates as specified by the System attribute -* of the SkyFrame, using the usual CTYPE codes ("RA--"/"DEC-", etc). -* In addition, the absolute coordinates description will contain -* AST-specific keywords (SREF1/2, SREFP1/2 and SREFIS) that allow the -* header to be read back into AST in order to reconstruct the original -* SkyFrame. -* -c When reading a FITS-WCS encoded Object (using astRead), the FitsChan -f When reading a FITS-WCS encoded Object (using AST_READ), the FitsChan -* concerned must initially be positioned at the first card (its -* Card attribute must equal 1) and the result of the read, if -* successful, will always be a pointer to a FrameSet. The base -* Frame of this FrameSet represents FITS-WCS pixel coordinates, -* and the current Frame represents the physical coordinate system -* described by the FITS-WCS primary axis descriptions. If -* secondary axis descriptions are also present, then the FrameSet -* may contain additional (non-current) Frames which represent -* these. Such a read is always destructive and causes the FITS -* header cards required for the construction of the FrameSet to be -* removed from the FitsChan, which is then left positioned at the -* "end-of-file". A subsequent read using the same encoding will -* therefore not return another FrameSet, even if the FitsChan is -* rewound. -* -c When astWrite is used to store a FrameSet using FITS-WCS -f When AST_WRITE is used to store a FrameSet using FITS-WCS -* encoding, an attempt is first made to simplify the FrameSet to -* see if it conforms to the FITS-WCS model. If this simplification -* process succeeds (as it often should, as the model is reasonably -* flexible), a description of the FrameSet is written to the -* FitsChan using appropriate FITS header cards. The base Frame of -* the FrameSet is used to form the FITS-WCS pixel coordinate -* system and the current Frame gives the physical coordinate -* system to be described by the FITS-WCS primary axis -* descriptions. Any additional Frames in the FrameSet may be used -* to construct secondary axis descriptions, where appropriate. -* -* A successful write operation will over-write any existing -* FITS-WCS encoded data in the FitsChan, but will not affect other -* (non-FITS-WCS) header cards. If a destructive read of a FITS-WCS -* encoded Object has previously occurred, then an attempt will be -* made to store the FITS header cards back in their original -* locations. Otherwise, the new cards will be inserted following -* any other FITS-WCS related header cards present or, failing -* that, in front of the current card (as given by the Card -* attribute). -* -* If an attempt to simplify a FrameSet to conform to the FITS-WCS -* model fails (or if the Object supplied is not a FrameSet), then -c no data will be written to the FitsChan and astWrite will -f no data will be written to the FitsChan and AST_WRITE will -* return zero. No error will result. - -* The FITS-IRAF Encoding: -* The FITS-IRAF encoding can, for most purposes, be considered as -* a subset of the FITS-WCS encoding (above), although it differs -* in the details of the FITS keywords used. It is used in exactly -* the same way and has the same restrictions, but with the - -* addition of the following: -* -* - The only celestial coordinate systems that may be represented -* are equatorial, galactic and ecliptic, -* - Sky projections can be represented only if any associated -* projection parameters are set to their default values. -* - Secondary axis descriptions are not supported, so when writing -* a FrameSet to a FitsChan, only information from the base and -* current Frames will be stored. -* -* Note that this encoding is provided mainly as an interim measure to -* provide a more stable alternative to the FITS-WCS encoding until the -* FITS standard for encoding WCS information is finalised. The name -* "FITS-IRAF" indicates the general keyword conventions used and does -* not imply that this encoding will necessarily support all features of -* the WCS scheme used by IRAF software. Nevertheless, an attempt has -* been made to support a few such features where they are known to be -* used by important sources of data. -* -* When writing a FrameSet using the FITS-IRAF encoding, axis rotations -* are specified by a matrix of FITS keywords of the form "CDi_j", where -* "i" and "j" are single digits. The alternative form "CDiiijjj", which -* is also in use, is recognised when reading an Object, but is never -* written. -* -* In addition, the experimental IRAF "ZPX" and "TNX" sky projections will -* be accepted when reading, but will never be written (the corresponding -* FITS "ZPN" or "distorted TAN" projection being used instead). However, -* there are restrictions on the use of these experimental projections. -* For "ZPX", longitude and latitude correction surfaces (appearing as -* "lngcor" or "latcor" terms in the IRAF-specific "WAT" keywords) are -* not supported. For "TNX" projections, only cubic surfaces encoded as -* simple polynomials with "half cross-terms" are supported. If an -* un-usable "TNX" or "ZPX" projection is encountered while reading -* from a FitsChan, a simpler form of TAN or ZPN projection is used -* which ignores the unsupported features and may therefore be -* inaccurate. If this happens, a warning message is added to the -* contents of the FitsChan as a set of cards using the keyword "ASTWARN". -* -* You should not normally attempt to mix the foreign FITS encodings within -* the same FitsChan, since there is a risk that keyword clashes may occur. - -* The FITS-PC Encoding: -* The FITS-PC encoding can, for most purposes, be considered as -* equivalent to the FITS-WCS encoding (above), although it differs -* in the details of the FITS keywords used. It is used in exactly -* the same way and has the same restrictions. - -* The FITS-AIPS Encoding: -* The FITS-AIPS encoding can, for most purposes, be considered as -* equivalent to the FITS-WCS encoding (above), although it differs -* in the details of the FITS keywords used. It is used in exactly -* the same way and has the same restrictions, but with the - -* addition of the following: -* -* - The only celestial coordinate systems that may be represented -* are equatorial, galactic and ecliptic, -* - Spectral axes can only be represented if they represent -* frequency, radio velocity or optical velocity, and are linearly -* sampled in frequency. In addition, the standard of rest -* must be LSRK, LSRD, barycentric or geocentric. -* - Sky projections can be represented only if any associated -* projection parameters are set to their default values. -* - The AIT, SFL and MER projections can only be written if the CRVAL -* keywords are zero for both longitude and latitude axes. -* - Secondary axis descriptions are not supported, so when writing -* a FrameSet to a FitsChan, only information from the base and -* current Frames will be stored. -* - If there are more than 2 axes in the base and current Frames, any -* rotation must be restricted to the celestial plane, and must involve -* no shear. - -* The FITS-AIPS++ Encoding: -* The FITS-AIPS++ encoding is based on the FITS-AIPS encoding, but -* includes some features of the FITS-IRAF and FITS-PC encodings. -* Specifically, any celestial projections supported by FITS-PC may be -* used, including those which require parameterisation, and the axis -* rotation and scaling may be specified using CDi_j keywords. When -* writing a FITS header, rotation will be specified using CROTA/CDELT -* keywords if possible, otherwise CDi_j keywords will be used instead. - -* The FITS-CLASS Encoding: -* The FITS-CLASS encoding uses the conventions of the CLASS project. -* These are described in the section "Developer Manual"/"CLASS FITS - -* Format" contained in the CLASS documentation at: -* -* http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/class.html. -* - -* This encoding is similar to FITS-AIPS with the following restrictions: -* -* - When a SpecFrame is created by reading a FITS-CLASS header, the -* attributes describing the observer's position (ObsLat, ObsLon and -* ObsAlt) are left unset because the CLASS encoding does not specify -* these values. Conversions to or from the topocentric standard of rest -* will therefore be inaccurate (typically by up to about 0.5 km/s) -* unless suitable values are assigned to these attributes after the -* FrameSet has been created. -* - When writing a FrameSet to a FITS-CLASS header, the current Frame -* in the FrameSet must have at least 3 WCS axes, of which one must be -* a linear spectral axis. The spectral axis in the created header will -* always describe frequency. If the spectral axis in the supplied -* FrameSet refers to some other system (e.g. radio velocity, etc), -* then it will be converted to frequency. -* - There must be a pair of celestial axes - either (RA,Dec) or -* (GLON,GLAT). RA and Dec must be either FK4/B1950 or FK5/J2000. -* - A limited range of projection codes (TAN, ARC, STG, AIT, SFL, SIN) -* can be used. For AIT and SFL, the reference point must be at the -* origin of longitude and latitude. For SIN, the associated projection -* parameters must both be zero. -* - No rotation of the celestial axes is allowed, unless the spatial -* axes are degenerate (i.e. cover only a single pixel). -* - The frequency axis in the created header will always describe -* frequency in the source rest frame. If the supplied FrameSet uses -* some other standard of rest then suitable conversion will be applied. -* - The source velocity must be defined. In other words, the SpecFrame -* attributes SourceVel and SourceVRF must have been assigned values. -* - The frequency axis in a FITS-CLASS header does not represent -* absolute frequency, but instead represents offsets from the rest -* frequency in the standard of rest of the source. -* -* When writing a FrameSet out using FITS-CLASS encoding, the current -* Frame may be temporarily modified if this will allow the header -* to be produced. If this is done, the associated pixel->WCS Mapping -* will also be modified to take account of the changes to the Frame. -* The modifications performed include re-ordering axes (WCS axes, not -* pixel axes), changing spectral coordinate system and standard of -* rest, changing the celestial coordinate system and reference equinox, -* and changing axis units. - -* The NATIVE Encoding: -* The NATIVE encoding may be used to store a description of any -* class of AST Object in the form of FITS header cards, and (for -* most practical purposes) any number of these Object descriptions -* may be stored within a single set of FITS cards. If multiple -* Object descriptions are stored, they are written and read -* sequentially. The NATIVE encoding makes use of unique FITS -* keywords which are designed not to clash with keywords that have -* already been used for other purposes (if a potential clash is -* detected, an alternative keyword is constructed to avoid the -* clash). -* -* When reading a NATIVE encoded object from a FitsChan (using -c astRead), FITS header cards are read, starting at the current -f AST_READ), FITS header cards are read, starting at the current -* card (as determined by the Card attribute), until the start of -* the next Object description is found. This description is then -* read and converted into an AST Object, for which a pointer is -* returned. Such a read is always destructive and causes all the -* FITS header cards involved in the Object description to be -* removed from the FitsChan, which is left positioned at the -* following card. -* -* The Object returned may be of any class, depending on the -* description that was read, and other AST routines may be used to -* validate it (for example, by examining its Class or ID attribute -c using astGetC). If further NATIVE encoded Object descriptions -f using AST_GETC). If further NATIVE encoded Object descriptions -c exist in the FitsChan, subsequent calls to astRead will return -f exist in the FitsChan, subsequent calls to AST_READ will return -* the Objects they describe in sequence (and destroy their -* descriptions) until no more remain between the current card and -* the "end-of-file". -* -c When astWrite is used to write an Object using NATIVE encoding, -f When AST_WRITE is used to write an Object using NATIVE encoding, -* a description of the Object is inserted immediately before the -* current card (as determined by the Card attribute). Multiple -* Object descriptions may be written in this way and are stored -* separately (and sequentially if the Card attribute is not -* modified between the writes). A write operation using the NATIVE -* encoding does not over-write previously written Object -* descriptions. Note, however, that subsequent behaviour is -* undefined if an Object description is written inside a -* previously-written description, so this should be avoided. -* -* When an Object is written to a FitsChan using NATIVE encoding, -c astWrite should (barring errors) always transfer data and -f AST_WRITE should (barring errors) always transfer data and -* return a value of 1. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,Encoding,encoding,UNKNOWN_ENCODING) -astMAKE_SET(FitsChan,Encoding,int,encoding,( - value == NATIVE_ENCODING || - value == FITSPC_ENCODING || - value == FITSWCS_ENCODING || - value == FITSIRAF_ENCODING || - value == FITSAIPS_ENCODING || - value == FITSAIPSPP_ENCODING || - value == FITSCLASS_ENCODING || - value == DSS_ENCODING ? value : - (astError( AST__BADAT, "astSetEncoding: Unknown encoding system %d " - "supplied.", status, value ), UNKNOWN_ENCODING ))) -astMAKE_TEST(FitsChan,Encoding,( this->encoding != UNKNOWN_ENCODING )) - -/* DefB1950 */ -/* ======== */ - -/* -*att++ -* Name: -* DefB1950 - -* Purpose: -* Use FK4 B1950 as defaults? - -* Type: -* Public attribute. - -* Synopsis: -* Integer (boolean). - -* Description: -* This attribute is a boolean value which specifies a default equinox -* and reference frame to use when reading a FrameSet from a FitsChan -* with a foreign (i.e. non-native) encoding. It is only used if the FITS -* header contains RA and DEC axes but contains no information about the -* reference frame or equinox. If this is the case, then values of FK4 and -* B1950 are assumed if the DefB1950 attribute has a non-zero value and -* ICRS is assumed if DefB1950 is zero. The default value for DefB1950 -* depends on the value of the Encoding attribute: for FITS-WCS encoding -* the default is zero, and for all other encodings it is one. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,DefB1950,defb1950,-1) -astMAKE_GET(FitsChan,DefB1950,int,1,(this->defb1950 == -1 ? (astGetEncoding(this)== FITSWCS_ENCODING?0:1): this->defb1950)) -astMAKE_SET(FitsChan,DefB1950,int,defb1950,( value ? 1 : 0 )) -astMAKE_TEST(FitsChan,DefB1950,( this->defb1950 != -1 )) - -/* TabOK */ -/* ===== */ - -/* -*att++ -* Name: -* TabOK - -* Purpose: -* Should the FITS-WCS -TAB algorithm be recognised? - -* Type: -* Public attribute. - -* Synopsis: -* Integer. - -* Description: -* This attribute is an integer value which indicates if the "-TAB" -* algorithm, defined in FITS-WCS paper III, should be supported by -* the FitsChan. The default value is zero. A zero or negative value -* results in no support for -TAB axes (i.e. axes that have "-TAB" -* in their CTYPE keyword value). In this case, the -c astWrite -f AST_WRITE -* method will return zero if the write operation would required the -* use of the -TAB algorithm, and the -c astRead -f AST_READ -* method will return -c a NULL pointer -f AST__NULL -* if any axis in the supplied header uses the -TAB algorithm. - -* If TabOK is set to a non-zero positive integer, these methods will -* recognise and convert axes described by the -TAB algorithm, as -* follows: -* -c The astWrite -f The AST_WRITE -* method will generate headers that use the -TAB algorithm (if -* possible) if no other known FITS-WCS algorithm can be used to -* describe the supplied FrameSet. This will result in a table of -* coordinate values and index vectors being stored in the FitsChan. -* After the write operation, the calling application should check to -* see if such a table has been stored in the FitsChan. If so, the -* table should be retrived from the FitsChan using the -c astGetTables -f AST_GETTABLES -* method, and the data (and headers) within it copied into a new -* FITS binary table extension. See -c astGetTables -f AST_GETTABLES -* for more information. The FitsChan uses a FitsTable object to store -* the table data and headers. This FitsTable will contain the required -* columns and headers as described by FITS-WCS paper III - the -* coordinates array will be in a column named "COORDS", and the index -* vector(s) will be in columns named "INDEX<i>" (where <i> is the index -* of the corresponding FITS WCS axis). Note, index vectors are only -* created if required. The EXTNAME value will be set to the value of the -* AST__TABEXTNAME constant (currently "WCS-TAB"). The EXTVER header -* will be set to the positive integer value assigned to the TabOK -* attribute. No value will be stored for the EXTLEVEL header, and should -* therefore be considered to default to 1. -* -c The astRead -f The AST_READ -* method will generate a FrameSet from headers that use the -TAB -* algorithm so long as the necessary FITS binary tables are made -* available. There are two ways to do this: firstly, if the application -* knows which FITS binary tables will be needed, then it can create a -* Fitstable describing each such table and store it in the FitsChan -* (using method -c astPutTables or astPutTable) before invoking the astRead method. -f AST_PUTTABLES or AST_PUTTABLE) before invoking the AST_READ method. -* Secondly, if the application does not know which FITS binary tables -* will be needed by -c astRead, -f AST_READ, -* then it can register a call-back function with the FitsChan using -* method -c astTableSource. -f AST_TABLESOURCE. -* This call-back function will be called from within -c astRead -f AST_READ -* if and when a -TAB header is encountered. When called, its arguments -* will give the name, version and level of the FITS extension containing -* a required table. The call-back function should read this table from -* an external FITS file, and create a corresponding FitsTable which -* it should then return to -c astRead. Note, currently astRead -f AST_READ. Note, currently AST_READ -* can only handle -TAB headers that describe 1-dimensional (i.e. -* separable) axes. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,TabOK,tabok,-INT_MAX) -astMAKE_GET(FitsChan,TabOK,int,0,(this->tabok == -INT_MAX ? 0 : this->tabok)) -astMAKE_SET(FitsChan,TabOK,int,tabok,value) -astMAKE_TEST(FitsChan,TabOK,( this->tabok != -INT_MAX )) - -/* CarLin */ -/* ====== */ - -/* -*att++ -* Name: -* CarLin - -* Purpose: -* Ignore spherical rotations on CAR projections? - -* Type: -* Public attribute. - -* Synopsis: -* Integer (boolean). - -* Description: -* This attribute is a boolean value which specifies how FITS "CAR" -* (plate carree, or "Cartesian") projections should be treated when -* reading a FrameSet from a foreign encoded FITS header. If zero (the -* default), it is assumed that the CAR projection conforms to the -* conventions described in the FITS world coordinate system (FITS-WCS) -* paper II "Representation of Celestial Coordinates in FITS" by -* M. Calabretta & E.W. Greisen. If CarLin is non-zero, then these -* conventions are ignored, and it is assumed that the mapping from pixel -* coordinates to celestial coordinates is a simple linear transformation -* (hence the attribute name "CarLin"). This is appropriate for some older -* FITS data which claims to have a "CAR" projection, but which in fact do -* not conform to the conventions of the FITS-WCS paper. -* -* The FITS-WCS paper specifies that headers which include a CAR projection -* represent a linear mapping from pixel coordinates to "native spherical -* coordinates", NOT celestial coordinates. An extra mapping is then -* required from native spherical to celestial. This mapping is a 3D -* rotation and so the overall Mapping from pixel to celestial coordinates -* is NOT linear. See the FITS-WCS papers for further details. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,CarLin,carlin,-1) -astMAKE_GET(FitsChan,CarLin,int,1,(this->carlin == -1 ? 0 : this->carlin)) -astMAKE_SET(FitsChan,CarLin,int,carlin,( value ? 1 : 0 )) -astMAKE_TEST(FitsChan,CarLin,( this->carlin != -1 )) - -/* SipReplace */ -/* ========== */ - -/* -*att++ -* Name: -* SipReplace - -* Purpose: -* Replace SIP inverse transformation? - -* Type: -* Public attribute. - -* Synopsis: -* Integer (boolean). - -* Description: -* This attribute is a boolean value which specifies how SIP keywords -* should be handled when reading a FITS-WCS encoded header using the -c astRead -f AST_READ -* function. See -* http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf -* for more information about SIP headers. If SipReplace is non-zero, -* then any SIP keywords describing the inverse transformation (i.e. from -* WCS to pixel coordinates) are ignored. Instead a new inverse -* transformation is found by performing a fit to the forward -* transformation. The SipReplace attribute can be set to zero to prevent -* this happening. If SipReplace is zero, any SIP keywords describing the -* inverse transformation are used as supplied, rather than being -* replaced using a new fit. The default value is 1. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,SipReplace,sipreplace,-1) -astMAKE_GET(FitsChan,SipReplace,int,1,(this->sipreplace == -1 ? 1 : this->sipreplace)) -astMAKE_SET(FitsChan,SipReplace,int,sipreplace,( value ? 1 : 0 )) -astMAKE_TEST(FitsChan,SipReplace,( this->sipreplace != -1 )) - -/* PolyTan */ -/* ======= */ - -/* -*att++ -* Name: -* PolyTan - -* Purpose: -* Use PVi_m keywords to define distorted TAN projection? - -* Type: -* Public attribute. - -* Synopsis: -* Integer. - -* Description: -* This attribute is a boolean value which specifies how FITS "TAN" -* projections should be treated when reading a FrameSet from a foreign -* encoded FITS header. If zero, the projection is assumed to conform -* to the published FITS-WCS standard. If positive, the convention -* for a distorted TAN projection included in an early draft version -* of FITS-WCS paper II are assumed. In this convention the -* coefficients of a polynomial distortion to be applied to -* intermediate world coordinates are specified by the PVi_m keywords. -* This convention was removed from the paper before publication and so -* does not form part of the standard. Indeed, it is incompatible with -* the published standard because it re-defines the meaning of the -* first five PVi_m keywords on the longitude axis, which are reserved -* by the published standard for other purposes. However, this -* scheme has now been added to the registry of FITS conventions -* (http://fits.gsfc.nasa.gov/registry/tpvwcs.html) and headers -* that use this convention are created by the SCAMP utility -* (http://www.astromatic.net/software/scamp) and the Dark Energy -* Camera at NOAO. -* -* The default value for the PolyTan attribute is -1. A negative -* values causes the used convention to depend on the contents -* of the FitsChan. If the FitsChan contains any PVi_m keywords for -* the latitude axis, or if it contains PVi_m keywords for the -* longitude axis with "m" greater than 4, then the distorted TAN -* convention is used. Otherwise, the standard convention is used. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,PolyTan,polytan,-INT_MAX) -astMAKE_SET(FitsChan,PolyTan,int,polytan,value) -astMAKE_TEST(FitsChan,PolyTan,( this->polytan != -INT_MAX )) -astMAKE_GET(FitsChan,PolyTan,int,-1,(this->polytan == -INT_MAX ? -1 : this->polytan)) - -/* SipOK */ -/* ===== */ - -/* -*att++ -* Name: -* SipOK - -* Purpose: -* Use Spitzer Space Telescope keywords to define distortion? - -* Type: -* Public attribute. - -* Synopsis: -* Integer (boolean). - -* Description: -* This attribute is a boolean value which specifies whether to include -* support for the "SIP" scheme, which can be used to add distortion to -* basic FITS-WCS projections. This scheme was first defined by the -* Spitzer Space Telescope and is described in the following document: -* http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf -* The default for SipOK is 1. -* -* When using -c astRead -f AST_READ -* to read a FITS-WCS encoded header, a suitable PolyMap will always be -* included in the returned FrameSet if the header contains SIP -* keywords, regardless of the value of the SipOK attribute. The PolyMap -* will be immediately before the MatrixMap that corresponds to the FITS-WCS -* PC or CD matrix. -* -* When using -c astWrite -f AST_WRITE -* to write a FrameSet to a FITS-WCS encoded header, suitable SIP -* keywords will be included in the header if the FrameSet contains a -* PolyMap immediately before the MatrixMap that corresponds to the -* FITS-WCS PC or CD matrix, but only if the SipOK attribute is non-zero. -* If the FrameSet contains a PolyMap but SipOK is zero, then an attempt -* will be made to write out the FrameSet without SIP keywords using a -* linear approximation to the pixel-to-IWC mapping. If this fails -* because the Mapping exceeds the linearity requirement specified by -* attribute FitsTol, -c astWrite -f AST_WRITE -* will return zero, indicating that the FrameSet could not be written -* out. Note, SIP headers can only be produced for axes that form part -* of a SkyFrame. -* -* Note, the SIP distortion scheme is independent of the TPV/TPN -* distortion schemes (see attribute PolyTan). A FITS-WCS header could -* in principle, contain keywords for both schemes although this is unlikely. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,SipOK,sipok,-INT_MAX) -astMAKE_SET(FitsChan,SipOK,int,sipok,value) -astMAKE_TEST(FitsChan,SipOK,( this->sipok != -INT_MAX )) -astMAKE_GET(FitsChan,SipOK,int,1,(this->sipok == -INT_MAX ? 1 : this->sipok)) - -/* Iwc */ -/* === */ - -/* -*att++ -* Name: -* Iwc - -* Purpose: -* Include a Frame representing FITS-WCS intermediate world coordinates? - -* Type: -* Public attribute. - -* Synopsis: -* Integer (boolean). - -* Description: -* This attribute is a boolean value which is used when a FrameSet is -* read from a FitsChan with a foreign FITS encoding (e.g. FITS-WCS) using -c astRead. -f AST_READ. -* If it has a non-zero value then the returned FrameSet will include -* Frames representing "intermediate world coordinates" (IWC). These -* Frames will have Domain name "IWC" for primary axis descriptions, and -* "IWCa" for secondary axis descriptions, where "a" is replaced by -* the single alternate axis description character, as used in the -* FITS-WCS header. The default value for "Iwc" is zero. -* -* FITS-WCS paper 1 defines IWC as a Cartesian coordinate system with one -* axis for each WCS axis, and is the coordinate system produced by the -* rotation matrix (represented by FITS keyword PCi_j, CDi_j, etc). -* For instance, for a 2-D FITS-WCS header describing projected -* celestial longitude and latitude, the intermediate world -* coordinates represent offsets in degrees from the reference point -* within the plane of projection. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,Iwc,iwc,-1) -astMAKE_GET(FitsChan,Iwc,int,1,(this->iwc == -1 ? 0 : this->iwc)) -astMAKE_SET(FitsChan,Iwc,int,iwc,( value ? 1 : 0 )) -astMAKE_TEST(FitsChan,Iwc,( this->iwc != -1 )) - -/* -*att++ -* Name: -* CDMatrix - -* Purpose: -* Use CDi_j keywords to represent pixel scaling, rotation, etc? - -* Type: -* Public attribute. - -* Synopsis: -* Integer (boolean). - -* Description: -* This attribute is a boolean value which specifies how the linear -* transformation from pixel coordinates to intermediate world -* coordinates should be represented within a FitsChan when using -* FITS-WCS encoding. This transformation describes the scaling, -* rotation, shear, etc., of the pixel axes. -* -* If the attribute has a non-zero value then the transformation is -* represented by a set of CDi_j keywords representing a square matrix -* (where "i" is the index of an intermediate world coordinate axis -* and "j" is the index of a pixel axis). If the attribute has a zero -* value the transformation is represented by a set of PCi_j keywords -* (which also represent a square matrix) together with a corresponding -* set of CDELTi keywords representing the axis scalings. See FITS-WCS -* paper II "Representation of Celestial Coordinates in FITS" by -* M. Calabretta & E.W. Greisen, for a complete description of these two -* schemes. -* -* The default value of the CDMatrix attribute is determined by the -* contents of the FitsChan at the time the attribute is accessed. If -* the FitsChan contains any CDi_j keywords then the default value is -* non-zero. Otherwise it is zero. Note, reading a FrameSet from a -* FitsChan will in general consume any CDi_j keywords present in the -* FitsChan. Thus the default value for CDMatrix following a read will -* usually be zero, even if the FitsChan originally contained some -* CDi_j keywords. This behaviour is similar to that of the Encoding -* attribute, the default value for which is determined by the contents -* of the FitsChan at the time the attribute is accessed. If you wish -* to retain the original value of the CDMatrix attribute (that is, -* the value before reading the FrameSet) then you should enquire the -* default value before doing the read, and then set that value -* explicitly. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,CDMatrix,cdmatrix,-1) -astMAKE_SET(FitsChan,CDMatrix,int,cdmatrix,( value ? 1 : 0 )) -astMAKE_TEST(FitsChan,CDMatrix,( this->cdmatrix != -1 )) - -/* Clean */ -/* ===== */ - -/* -*att++ -* Name: -* Clean - -* Purpose: -* Remove cards used whilst reading even if an error occurs? - -* Type: -* Public attribute. - -* Synopsis: -* Integer (boolean). - -* Description: -* This attribute indicates whether or not cards should be removed from -* the FitsChan if an error occurs within -c astRead. -f AST_READ. -* A succesful read on a FitsChan always results in the removal of -* the cards which were involved in the description of the returned -* Object. However, in the event of an error during the read (for instance -* if the cards in the FitsChan have illegal values, or if some required -* cards are missing) no cards will be removed from the FitsChan if -* the Clean attribute is zero (the default). If Clean is non-zero then -* any cards which were used in the aborted attempt to read an object -* will be removed. -* -* This provides a means of "cleaning" a FitsChan of WCS related cards -* which works even in the event of the cards not forming a legal WCS -* description. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,Clean,clean,-1) -astMAKE_SET(FitsChan,Clean,int,clean,( value ? 1 : 0 )) -astMAKE_TEST(FitsChan,Clean,( this->clean != -1 )) - -/* -*att++ -* Name: -* FitsAxisOrder - -* Purpose: -* Frame title. - -* Type: -* Public attribute. - -* Synopsis: -* String. - -* Description: -* This attribute specifies the order for the WCS axes in any new -* FITS-WCS headers created using the -c astWrite -f AST_WRITE -* method. -* -* The value of the FitsAxisOrder attribute can be either "<auto>" -* (the default value), "<copy>" or a space-separated list of axis -* symbols: -* -* "<auto>": causes the WCS axis order to be chosen automatically so that -* the i'th WCS axis in the new FITS header is the WCS axis which is -* more nearly parallel to the i'th pixel axis. -* -* "<copy>": causes the WCS axis order to be set so that the i'th WCS -* axis in the new FITS header is the i'th WCS axis in the current -* Frame of the FrameSet being written out to the header. -* -* "Sym1 Sym2...": the space-separated list is seached in turn for -* the Symbol attribute of each axis in the current Frame of the -* FrameSet. The order in which these Symbols occur within the -* space-separated list defines the order of the WCS axes in the -* new FITS header. An error is reported if Symbol for a current -* Frame axis is not present in the supplied list. However, no error -* is reported if the list contains extra words that do not correspond -* to the Symbol of any current Frame axis. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,FitsAxisOrder,fitsaxisorder,astFree( this->fitsaxisorder )) -astMAKE_GET(FitsChan,FitsAxisOrder,const char *,NULL,(this->fitsaxisorder ? this->fitsaxisorder : "<auto>" )) -astMAKE_SET(FitsChan,FitsAxisOrder,const char *,fitsaxisorder,astStore( this->fitsaxisorder, value, strlen( value ) + (size_t) 1 )) -astMAKE_TEST(FitsChan,FitsAxisOrder,( this->fitsaxisorder != NULL )) - -/* FitsDigits. */ -/* =========== */ - -/* -*att++ -* Name: -* FitsDigits - -* Purpose: -* Digits of precision for floating point FITS values. - -* Type: -* Public attribute. - -* Synopsis: -* Integer. - -* Description: -* This attribute gives the number of significant decimal digits to -* use when formatting floating point values for inclusion in the -* FITS header cards within a FitsChan. -* -* By default, a positive value is used which results in no loss of -c information, assuming that the value's precision is double. -f information, assuming that the value is double precision. -* Usually, this causes no problems. -* -* However, to adhere strictly to the recommendations of the FITS -* standard, the width of the formatted value (including sign, -* decimal point and exponent) ought not to be more than 20 -* characters. If you are concerned about this, you should set -* FitsDigits to a negative value, such as -15. In this case, the -* absolute value (+15) indicates the maximum number of significant -* digits to use, but the actual number used may be fewer than this -* to ensure that the FITS recommendations are satisfied. When -* using this approach, the resulting number of significant digits -* may depend on the value being formatted and on the presence of -* any sign, decimal point or exponent. -* -* The value of this attribute is effective when FITS header cards -* are output, either using -c astFindFits or by the action of the FitsChan's sink function -f AST_FINDFITS or by the action of the FitsChan's sink routine -* when it is finally deleted. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ -astMAKE_CLEAR(FitsChan,FitsDigits,fitsdigits,AST__DBL_DIG) -astMAKE_GET(FitsChan,FitsDigits,int,AST__DBL_DIG,this->fitsdigits) -astMAKE_SET(FitsChan,FitsDigits,int,fitsdigits,value) -astMAKE_TEST(FitsChan,FitsDigits,( this->fitsdigits != AST__DBL_DIG )) - -/* CardComm */ -/* ======== */ - -/* -*att++ -* Name: -* CardComm - -* Purpose: -* The comment for the current card in a FitsChan. - -* Type: -* Public attribute. - -* Synopsis: -* String, read-only. - -* Description: -* This attribute gives the comment for the current card of the -* FitsChan. A zero-length string is returned if the card has no comment. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ - -/* CardName */ -/* ======== */ - -/* -*att++ -* Name: -* CardName - -* Purpose: -* The keyword name of the current card in a FitsChan. - -* Type: -* Public attribute. - -* Synopsis: -* String, read-only. - -* Description: -* This attribute gives the name of the keyword for the -* current card of the FitsChan. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ - -/* CardType */ -/* ======== */ - -/* -*att++ -* Name: -* CardType - -* Purpose: -* The data type of the current card in a FitsChan. - -* Type: -* Public attribute. - -* Synopsis: -* Integer, read-only. - -* Description: -* This attribute gives the data type of the keyword value for the -* current card of the FitsChan. It will be one of the following -* integer constants: AST__NOTYPE, AST__COMMENT, AST__INT, AST__FLOAT, -* AST__STRING, AST__COMPLEXF, AST__COMPLEXI, AST__LOGICAL, -* AST__CONTINUE, AST__UNDEF. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ - -/* Ncard */ -/* ===== */ - -/* -*att++ -* Name: -* Ncard - -* Purpose: -* Number of FITS header cards in a FitsChan. - -* Type: -* Public attribute. - -* Synopsis: -* Integer, read-only. - -* Description: -* This attribute gives the total number of FITS header cards -* stored in a FitsChan. It is updated as cards are added or -* deleted. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ - -/* Nkey */ -/* ==== */ - -/* -*att++ -* Name: -* Nkey - -* Purpose: -* Number of unique FITS keywords in a FitsChan. - -* Type: -* Public attribute. - -* Synopsis: -* Integer, read-only. - -* Description: -* This attribute gives the total number of unique FITS keywords -* stored in a FitsChan. It is updated as cards are added or -* deleted. If no keyword occurrs more than once in the FitsChan, the -* Ncard and Nkey attributes will be equal. If any keyword occurrs -* more than once, the Nkey attribute value will be smaller than -* the Ncard attribute value. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ - -/* Warnings. */ -/* ======== */ - -/* -*att++ -* Name: -* Warnings - -* Purpose: -* Controls the issuing of warnings about various conditions. - -* Type: -* Public attribute. - -* Synopsis: -* String - -* Description: -* This attribute controls the issuing of warnings about selected -* conditions when an Object or keyword is read from or written to a -* FitsChan. The value supplied for the Warnings attribute should -* consist of a space separated list of condition names (see the -* AllWarnings attribute for a list of the currently defined names). -* Each name indicates a condition which should be reported. The default -* value for Warnings is the string "BadKeyName BadKeyValue Tnx Zpx -* BadCel BadMat BadPV BadCTYPE". -* -* The text of any warning will be stored within the FitsChan in the -* form of one or more new header cards with keyword ASTWARN. If -* required, applications can check the FitsChan for ASTWARN cards -c (using astFindFits) after the call to astRead or astWrite has been -f (using AST_FINDFITS) after the call to AST_READ or AST_WRITE has been -* performed, and report the text of any such cards to the user. ASTWARN -* cards will be propagated to any output header unless they are -c deleted from the FitsChan using astDelFits. -f deleted from the FitsChan using astDelFits. - -* Notes: -* This attribute only controls the warnings that are to be stored as -* a set of header cards in the FitsChan as described above. It has no -* effect on the storage of warnings in the parent Channel structure. -* All warnings are stored in the parent Channel structure, from where -* they can be retrieved using the -c astWarnings -f AST_WARNINGS -* function. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ - -/* Clear the Warnings value by freeing the allocated memory and assigning - a NULL pointer. */ -astMAKE_CLEAR(FitsChan,Warnings,warnings,astFree( this->warnings )) - -/* If the Warnings value is not set, supply a default in the form of a - pointer to the constant string "BadKeyName BadKeyValue Tnx Zpx BadCel BadMat BadCTYPE". */ -astMAKE_GET(FitsChan,Warnings,const char *,NULL,( this->warnings ? this->warnings : - "BadKeyName BadKeyValue Tnx Zpx BadPV BadCel BadMat BadCTYPE" )) - -/* Set a Warnings value by freeing any previously allocated memory, allocating - new memory, storing the string and saving the pointer to the copy. - First check that the list does not contain any unknown conditions. If - it does, an error is reported by GoodWarns and the current attribute value - is retained. */ -astMAKE_SET(FitsChan,Warnings,const char *,warnings,( GoodWarns( value, status ) ? - astStore( this->warnings, value, strlen( value ) + (size_t) 1 ) : - this->warnings)) - -/* The Warnings value is set if the pointer to it is not NULL. */ -astMAKE_TEST(FitsChan,Warnings,( this->warnings != NULL )) - -/* AllWarnings. */ -/* ============ */ - -/* -*att++ -* Name: -* AllWarnings - -* Purpose: -* A list of all currently available condition names. - -* Type: -* Public attribute. - -* Synopsis: -* String, read-only - -* Description: -* This read-only attribute is a space separated list of all the conditions -* names recognized by the Warnings attribute. The names are listed -* below. - -* Conditions: -* The following conditions are currently recognised (all are -* case-insensitive): -* -* - "BadCel": This condition arises when reading a FrameSet from a -* non-Native encoded FitsChan if an unknown celestial co-ordinate -* system is specified by the CTYPE keywords. -* -* - "BadCTYPE": This condition arises when reading a FrameSet from a -* non-Native encoded FitsChan if an illegal algorithm code is specified -* by a CTYPE keyword, and the illegal code can be converted to an -* equivalent legal code. -* -* - "BadKeyName": This condition arises if a FITS keyword name is -* encountered that contains an illegal character (i.e. one not allowed -* by the FITS standard). -* -* - "BadKeyValue": This condition arises if the value of a FITS keyword -* cannot be determined from the content of the header card. -* -* - "BadLat": This condition arises when reading a FrameSet from a -* non-Native encoded FitsChan if the latitude of the reference point -* has an absolute value greater than 90 degrees. The actual absolute -* value used is set to exactly 90 degrees in these cases. -* -* - "BadMat": This condition arises if the matrix describing the -* transformation from pixel offsets to intermediate world coordinates -* cannot be inverted. This matrix describes the scaling, rotation, shear, -* etc., applied to the pixel axes, and is specified by keywords such as -* PCi_j, CDi_j, CROTA, etc. For example, the matrix will not be invertable -* if any rows or columns consist entirely of zeros. The FITS-WCS Paper I -* "Representation of World Coordinates in FITS" by Greisen & Calabretta -* requires that this matrix be invertable. Many operations (such as -* grid plotting) will not be possible if the matrix cannot be inverted. -* -* - "BadPV": This condition arises when reading a FrameSet from a -* non-Native encoded FitsChan. It is issued if a PVi_m header is found -* that refers to a projection parameter that is not used by the -* projection type specified by CTYPE, or the PV values are otherwise -* inappropriate for the projection type. -* -* - "BadVal": This condition arises when reading a FrameSet from a -* non-Native encoded FitsChan if it is not possible to convert the -* value of a FITS keywords to the expected type. For instance, this -* can occur if the FITS header contains a string value for a keyword -* which should have a floating point value, or if the keyword has no -* value at all (i.e. is a comment card). -* -* - "Distortion": This condition arises when reading a FrameSet from a -* non-Native encoded FitsChan if any of the CTYPE keywords specify an -* unsupported distortion code using the "4-3-3" format specified in -* FITS-WCS paper IV. Such distortion codes are ignored. -* -* - "NoCTYPE": This condition arises if a default CTYPE value is used -c within astRead, due to no value being present in the supplied FitsChan. -f within AST_READ, due to no value being present in the supplied FitsChan. -* This condition is only tested for when using non-Native encodings. -* -* - "NoEquinox": This condition arises if a default equinox value is used -c within astRead, due to no value being present in the supplied FitsChan. -f within AST_READ, due to no value being present in the supplied FitsChan. -* This condition is only tested for when using non-Native encodings. -* -* - "NoRadesys": This condition arises if a default reference frame is -c used for an equatorial co-ordinate system within astRead, due to no -f used for an equatorial co-ordinate system within AST_READ, due to no -* value being present in the supplied FitsChan. This condition is only -* tested for when using non-Native encodings. -* -* - "NoLonpole": This condition arises if a default value is used for -c the LONPOLE keyword within astRead, due to no value being present -f the LONPOLE keyword within AST_READ, due to no value being present -* in the supplied FitsChan. This condition is only tested for when -* using non-Native encodings. -* -* - "NoLatpole": This condition arises if a default value is used for -c the LATPOLE keyword within astRead, due to no value being present -f the LATPOLE keyword within AST_READ, due to no value being present -* in the supplied FitsChan. This condition is only tested for when -* using non-Native encodings. -* -* - "NoMjd-obs": This condition arises if a default value is used for -c the date of observation within astRead, due to no value being present -f the date of observation within AST_READ, due to no value being present -* in the supplied FitsChan. This condition is only tested for when using -* non-Native encodings. -* -* - "Tnx": This condition arises if a FrameSet is read from a FITS -* header containing an IRAF "TNX" projection which includes terms -* not supproted by AST. Such terms are ignored and so the resulting -* FrameSet may be inaccurate. -* -* - "Zpx": This condition arises if a FrameSet is read from a FITS -* header containing an IRAF "ZPX" projection which includes "lngcor" -* or "latcor" correction terms. These terms are not supported by AST -* and are ignored. The resulting FrameSet may therefore be inaccurate. - -* Applicability: -* FitsChan -* All FitsChans have this attribute. -*att-- -*/ - -/* Copy constructor. */ -/* ----------------- */ - -static void Copy( const AstObject *objin, AstObject *objout, int *status ) { -/* -* Name: -* Copy - -* Purpose: -* Copy constructor for FitsChan objects. - -* Type: -* Private function. - -* Synopsis: -* void Copy( const AstObject *objin, AstObject *objout, int *status ) - -* Description: -* This function implements the copy constructor for FitsChan objects. - -* Parameters: -* objin -* Pointer to the FitsChan to be copied. -* objout -* Pointer to the FitsChan being constructed. -* status -* Pointer to the inherited status variable. - -* Notes: -* - The source and sink functions are not propagated (i.e. the -* pointers are set NULL in the output FitsChan). -* - This constructor makes a deep copy, including a copy of the -* keyword values. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - const char *class; /* Pointer to object class */ - AstFitsChan *in; /* Pointer to input FitsChan */ - AstFitsChan *out; /* Pointer to output FitsChan */ - int *flags; - int icard; - int old_ignore_used; /* Original value of external variable ignore_used */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(objin); - -/* Obtain pointers to the input and output FitsChans. */ - in = (AstFitsChan *) objin; - out = (AstFitsChan *) objout; - -/* Nullify all pointers in the output FitsChan so that the input - data will not be deleted in the event of an error occurring. */ - out->card = NULL; - out->head = NULL; - out->keyseq = NULL; - out->keywords = NULL; - out->source = NULL; - out->saved_source = NULL; - out->source_wrap = NULL; - out->sink = NULL; - out->sink_wrap = NULL; - out->warnings = NULL; - out->tabsource = NULL; - out->tabsource_wrap = NULL; - -/* Store the object class. */ - class = astGetClass( in ); - -/* Ensure all cards are copied, including those already read by astRead. */ - old_ignore_used = ignore_used; - ignore_used = 0; - -/* Save the current card index in the input FitsChan. */ - icard = astGetCard( in ); - -/* Rewind the input FitsChan. */ - astClearCard( in ); - -/* Copy all the FitsCard structures from input to output. */ - while( !astFitsEof( in ) && astOK ){ - -/* Get a pointer to the flags mask for this card. */ - flags = CardFlags( in, status ); - -/* Store a new card in the output, holding the same information as the - input card. */ - NewCard( out, CardName( in, status ), CardType( in, status ), CardData( in, NULL, status ), - CardComm( in, status ), (flags?(*flags):0), status ); - -/* Move on to the next input card. */ - MoveCard( in, 1, "astCopy", class, status ); - } - -/* Set the current card in both input and output to the current input - card on entry. */ - astSetCard( in, icard ); - astSetCard( out, icard ); - -/* Copy the list of keyword sequence numbers used. */ - if( in->keyseq ) out->keyseq = astCopy( in->keyseq ); - -/* Copy the Warnings attribute value */ - if( in->warnings ) out->warnings = astStore( NULL, in->warnings, - strlen( in->warnings ) + 1 ); - -/* Copy any tables currently in the FitsChan structure. */ - if( in->tables ) out->tables = astCopy( in->tables ); - -/* Reinstate the original setting of the external ignore_used variable. */ - ignore_used = old_ignore_used; - -/* If an error occurred, delete the contents of the output Object. */ - if( !astOK ) Delete( objout, status ); -} - -/* Destructor. */ -/* ----------- */ - -static void Delete( AstObject *obj, int *status ) { -/* -* Name: -* Delete - -* Purpose: -* Destructor for FitsChan objects. - -* Type: -* Private function. - -* Synopsis: -* void Delete( AstObject *obj, int *status ) - -* Description: -* This function implements the destructor for FitsChan objects. - -* Parameters: -* obj -* Pointer to the FitsChan to be deleted. -* status -* Pointer to the inherited status variable. - -* Notes: -* This function attempts to execute even if the global error status is -* set. -*/ - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to FitsChan */ - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) obj; - -/* Write out the contents of the FitsChan using the sink function - provided when it was created. */ - WriteToSink( this, status ); - -/* Remove all cards from the FitsChan. */ - EmptyFits( this, status ); -} - -/* Dump function. */ -/* -------------- */ - -static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { -/* -* Name: -* Dump - -* Purpose: -* Dump function for FitsChan objects. - -* Type: -* Private function. - -* Synopsis: -* void Dump( AstObject *this, AstChannel *channel, int *status ) - -* Description: -* This function implements the Dump function which writes out data -* for the FitsChan class to an output Channel. - -* Parameters: -* this -* Pointer to the FitsChan whose data are being written. -* channel -* Pointer to the Channel to which the data are being written. -* status -* Pointer to the inherited status variable. -*/ -#define KEY_LEN 50 /* Maximum length of a keyword */ - -/* Local Variables: */ - AstFitsChan *this; /* Pointer to the FitsChan structure */ - astDECLARE_GLOBALS /* Declare the thread specific global data */ - char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ - const char *class; /* Object class */ - const char *sval; /* Pointer to string value */ - double dval; /* Double value */ - int cardtype; /* Keyword data type */ - int flags; /* Keyword flags */ - int icard; /* Index of current card */ - int ival; /* Integer value */ - int ncard; /* No. of cards dumped so far */ - int old_ignore_used; /* Original value of external variable ignore_used */ - int set; /* Attribute value set? */ - void *data; /* Pointer to keyword data value */ - -/* Check the global error status. */ - if ( !astOK ) return; - -/* Get a pointer to the structure holding thread-specific global data. */ - astGET_GLOBALS(this_object); - -/* Obtain a pointer to the FitsChan structure. */ - this = (AstFitsChan *) this_object; - -/* Store the object class. */ - class = astGetClass( this ); - -/* Save the index of ht ecurrent card. */ - icard = astGetCard( this ); - -/* Write out values representing the instance variables for the - FitsChan class. Accompany these with appropriate comment strings, - possibly depending on the values being written.*/ - -/* Card. */ -/* ----- */ - astWriteInt( channel, "Card", 1, 1, icard, "Index of current card" ); - -/* Encoding. */ -/* --------- */ - set = TestEncoding( this, status ); - ival = set ? GetEncoding( this, status ) : astGetEncoding( this ); - if( ival > UNKNOWN_ENCODING && ival <= MAX_ENCODING ) { - astWriteString( channel, "Encod", set, 1, xencod[ival], "Encoding system" ); - } else { - astWriteString( channel, "Encod", set, 1, UNKNOWN_STRING, "Encoding system" ); - } - -/* FitsAxisOrder. */ -/* -------------- */ - set = TestFitsAxisOrder( this, status ); - sval = set ? GetFitsAxisOrder( this, status ) : astGetFitsAxisOrder( this ); - astWriteString( channel, "FAxOrd", set, 1, sval, - "Order of WCS axes in new FITS headers" ); - -/* FitsDigits. */ -/* ----------- */ - set = TestFitsDigits( this, status ); - ival = set ? GetFitsDigits( this, status ) : astGetFitsDigits( this ); - astWriteInt( channel, "FitsDg", set, 1, ival, "No. of digits for floating point values" ); - -/* DefB1950 */ -/* -------- */ - set = TestDefB1950( this, status ); - ival = set ? GetDefB1950( this, status ) : astGetDefB1950( this ); - astWriteInt( channel, "DfB1950", set, 1, ival, (ival ? "Default to FK4 B1950": "Default to ICRS") ); - -/* TabOK */ -/* ----- */ - set = TestTabOK( this, status ); - ival = set ? GetTabOK( this, status ) : astGetTabOK( this ); - astWriteInt( channel, "TabOK", set, 1, ival, ( ival > 0 ? "EXTVER value for -TAB headers": "Do not support -TAB CTYPE codes") ); - -/* CDMatrix */ -/* -------- */ - set = TestCDMatrix( this, status ); - ival = set ? GetCDMatrix( this, status ) : astGetCDMatrix( this ); - astWriteInt( channel, "CdMat", set, 1, ival, (ival ? "Use CD Matrix":"Use PC matrix") ); - -/* CarLin */ -/* ------ */ - set = TestCarLin( this, status ); - ival = set ? GetCarLin( this, status ) : astGetCarLin( this ); - astWriteInt( channel, "CarLin", set, 1, ival, (ival ? "Use simple linear CAR projections": "Use full FITS-WCS CAR projections") ); - -/* SipReplace */ -/* ------ */ - set = TestSipReplace( this, status ); - ival = set ? GetSipReplace( this, status ) : astGetSipReplace( this ); - astWriteInt( channel, "SipReplace", set, 1, ival, "Replace SIP inverse coefficients?" ); - -/* FitsTol */ -/* ------- */ - set = TestFitsTol( this, status ); - dval = set ? GetFitsTol( this, status ) : astGetFitsTol( this ); - astWriteDouble( channel, "FitsTol", set, 1, dval, "[pixel] Max allowed " - "departure from linearity"); - -/* PolyTan */ -/* ------- */ - set = TestPolyTan( this, status ); - ival = set ? GetPolyTan( this, status ) : astGetPolyTan( this ); - astWriteInt( channel, "PolyTan", set, 0, ival, (ival ? "Use distorted TAN convention": "Use standard TAN convention") ); - -/* SipOK */ -/* ----- */ - set = TestSipOK( this, status ); - ival = set ? GetSipOK( this, status ) : astGetSipOK( this ); - astWriteInt( channel, "SipOK", set, 0, ival, (ival ? "Use SIP distortion convention": "Ignore SIP keywords") ); - -/* Iwc */ -/* --- */ - set = TestIwc( this, status ); - ival = set ? GetIwc( this, status ) : astGetIwc( this ); - astWriteInt( channel, "Iwc", set, 1, ival, (ival ? "Include an IWC Frame": "Do not include an IWC Frame") ); - -/* Clean */ -/* ----- */ - set = TestClean( this, status ); - ival = set ? GetClean( this, status ) : astGetClean( this ); - astWriteInt( channel, "Clean", set, 0, ival, "Always remove used cards?" ); - -/* Warnings. */ -/* --------- */ - set = TestWarnings( this, status ); - sval = set ? GetWarnings( this, status ) : astGetWarnings( this ); - astWriteString( channel, "Warn", set, 1, sval, "Warnings to be reported" ); - -/* Now do instance variables which are not attributes. */ -/* =================================================== */ - -/* Ensure all cards are copied, including those already read by astRead. */ - old_ignore_used = ignore_used; - ignore_used = 0; - -/* Rewind the FitsChan. */ - astClearCard( this ); - -/* Dump each card. */ - ncard = 1; - while( !astFitsEof( this ) && astOK ){ - -/* Write out the keyword name. */ - if( CardName( this, status ) ){ - (void) sprintf( buff, "Nm%d", ncard ); - astWriteString( channel, buff, 1, 1, CardName( this, status ), - "FITS keyword name" ); - } - -/* Write out the keyword type. */ - cardtype = CardType( this, status ); - (void) sprintf( buff, "Ty%d", ncard ); - astWriteString( channel, buff, 1, 1, type_names[ cardtype ], - "FITS keyword data type" ); - -/* Write out the flag values if any are non-zero. */ - flags = *CardFlags( this, status ); - if( flags ){ - (void) sprintf( buff, "Fl%d", ncard ); - astWriteInt( channel, buff, 1, 1, flags, "FITS keyword flags" ); - } - -/* Write out the data value, if defined, using the appropriate data type. */ - data = CardData( this, NULL, status ); - if( data && cardtype != AST__UNDEF ){ - if( cardtype == AST__FLOAT ){ - (void) sprintf( buff, "Dt%d", ncard ); - astWriteDouble( channel, buff, 1, 1, *( (double *) data ), - "FITS keyword value" ); - } else if( cardtype == AST__STRING || cardtype == AST__CONTINUE ){ - (void) sprintf( buff, "Dt%d", ncard ); - astWriteString( channel, buff, 1, 1, (char *) data, - "FITS keyword value" ); - } else if( cardtype == AST__INT ){ - (void) sprintf( buff, "Dt%d", ncard ); - astWriteInt( channel, buff, 1, 1, *( (int *) data ), - "FITS keyword value" ); - } else if( cardtype == AST__LOGICAL ){ - (void) sprintf( buff, "Dt%d", ncard ); - astWriteInt( channel, buff, 1, 1, *( (int *) data ), - "FITS keyword value" ); - } else if( cardtype == AST__COMPLEXF ){ - (void) sprintf( buff, "Dr%d", ncard ); - astWriteDouble( channel, buff, 1, 1, *( (double *) data ), - "FITS keyword real value" ); - (void) sprintf( buff, "Di%d", ncard ); - astWriteDouble( channel, buff, 1, 1, *( ( (double *) data ) + 1 ), - "FITS keyword imaginary value" ); - } else if( cardtype == AST__COMPLEXI ){ - (void) sprintf( buff, "Dr%d", ncard ); - astWriteInt( channel, buff, 1, 1, *( (int *) data ), - "FITS keyword real value" ); - (void) sprintf( buff, "Di%d", ncard ); - astWriteInt( channel, buff, 1, 1, *( ( (int *) data ) + 1 ), - "FITS keyword imaginary value" ); - } - } - -/* Write out the keyword comment. */ - if( CardComm( this, status ) ){ - (void) sprintf( buff, "Cm%d", ncard ); - astWriteString( channel, buff, 1, 1, CardComm( this, status ), - "FITS keyword comment" ); - } - -/* Move on to the next card. */ - ncard++; - MoveCard( this, 1, "astDump", class, status ); - } - -/* Dump any FitTables. */ - if( this->tables ) { - astWriteObject( channel, "Tables", 1, 1, this->tables, - "A KeyMap holding associated binary tables" ); - } - -/* Reinstate the original setting of the external ignore_used variable. */ - ignore_used = old_ignore_used; - -/* Reinstate the original current card. */ - astSetCard( this, icard ); -#undef KEY_LEN -} - -/* Standard class functions. */ -/* ========================= */ - -/* Implement the astIsAFitsChan and astCheckFitsChan functions using the macros - defined for this purpose in the "object.h" header file. */ -astMAKE_ISA(FitsChan,Channel) -astMAKE_CHECK(FitsChan) -AstFitsChan *astFitsChan_( const char *(* source)( void ), - void (* sink)( const char * ), - const char *options, int *status, ...) { - -/* -*++ -* Name: -c astFitsChan -f AST_FITSCHAN - -* Purpose: -* Create a FitsChan. - -* Type: -* Public function. - -* Synopsis: -c #include "fitschan.h" -c AstFitsChan *astFitsChan( const char *(* source)( void ), -c void (* sink)( const char * ), -c const char *options, ... ) -f RESULT = AST_FITSCHAN( SOURCE, SINK, OPTIONS, STATUS ) - -* Class Membership: -* FitsChan constructor. - -* Description: -* This function creates a new FitsChan and optionally initialises -* its attributes. -* -* A FitsChan is a specialised form of Channel which supports I/O -* operations involving the use of FITS (Flexible Image Transport -* System) header cards. Writing an Object to a FitsChan (using -c astWrite) will, if the Object is suitable, generate a -f AST_WRITE) will, if the Object is suitable, generate a -* description of that Object composed of FITS header cards, and -* reading from a FitsChan will create a new Object from its FITS -* header card description. -* -* While a FitsChan is active, it represents a buffer which may -* contain zero or more 80-character "header cards" conforming to -* FITS conventions. Any sequence of FITS-conforming header cards -* may be stored, apart from the "END" card whose existence is -* merely implied. The cards may be accessed in any order by using -* the FitsChan's integer Card attribute, which identifies a "current" -* card, to which subsequent operations apply. Searches -c based on keyword may be performed (using astFindFits), new -c cards may be inserted (astPutFits, astPutCards, astSetFits<X>) and -c existing ones may be deleted (astDelFits) or changed (astSetFits<X>). -f based on keyword may be performed (using AST_FINDFITS), new -f cards may be inserted (AST_PUTFITS, AST_PUTCARDS, AST_SETFITS<X>) and -f existing ones may be deleted (AST_DELFITS) or changed (AST_SETFITS<X>). -* -* When you create a FitsChan, you have the option of specifying -* "source" and "sink" functions which connect it to external data -* stores by reading and writing FITS header cards. If you provide -* a source function, it is used to fill the FitsChan with header cards -* when it is accessed for the first time. If you do not provide a -* source function, the FitsChan remains empty until you explicitly enter -c data into it (e.g. using astPutFits, astPutCards, astWrite -f data into it (e.g. using AST_PUTFITS, AST_PUTCARDS, AST_WRITE -* or by using the SourceFile attribute to specifying a text file from -* which headers should be read). When the FitsChan is deleted, any -* remaining header cards in the FitsChan can be saved in either of -* two ways: 1) by specifying a value for the SinkFile attribute (the -* name of a text file to which header cards should be written), or 2) -* by providing a sink function (used to to deliver header cards to an -* external data store). If you do not provide a sink function or a -* value for SinkFile, any header cards remaining when the FitsChan -* is deleted will be lost, so you should arrange to extract them -* first if necessary -c (e.g. using astFindFits or astRead). -f (e.g. using AST_FINDFITS or AST_READ). -* -* Coordinate system information may be described using FITS header -* cards using several different conventions, termed -* "encodings". When an AST Object is written to (or read from) a -* FitsChan, the value of the FitsChan's Encoding attribute -* determines how the Object is converted to (or from) a -* description involving FITS header cards. In general, different -* encodings will result in different sets of header cards to -* describe the same Object. Examples of encodings include the DSS -* encoding (based on conventions used by the STScI Digitised Sky -* Survey data), the FITS-WCS encoding (based on a proposed FITS -* standard) and the NATIVE encoding (a near loss-less way of -* storing AST Objects in FITS headers). -* -* The available encodings differ in the range of Objects they can -* represent, in the number of Object descriptions that can coexist -* in the same FitsChan, and in their accessibility to other -* (external) astronomy applications (see the Encoding attribute -* for details). Encodings are not necessarily mutually exclusive -* and it may sometimes be possible to describe the same Object in -* several ways within a particular set of FITS header cards by -* using several different encodings. -* -c The detailed behaviour of astRead and astWrite, when used with -f The detailed behaviour of AST_READ and AST_WRITE, when used with -* a FitsChan, depends on the encoding in use. In general, however, -c all use of astRead is destructive, so that FITS header cards -f all use of AST_READ is destructive, so that FITS header cards -* are consumed in the process of reading an Object, and are -* removed from the FitsChan (this deletion can be prevented for -* specific cards by calling the -c astRetainFits function). -f AST_RETAINFITS routine). -* -* If the encoding in use allows only a single Object description -* to be stored in a FitsChan (e.g. the DSS, FITS-WCS and FITS-IRAF -c encodings), then write operations using astWrite will -f encodings), then write operations using AST_WRITE will -* over-write any existing Object description using that -* encoding. Otherwise (e.g. the NATIVE encoding), multiple Object -* descriptions are written sequentially and may later be read -* back in the same sequence. - -* Parameters: -c source -f SOURCE = FUNCTION (Given) -c Pointer to a source function which takes no arguments and -c returns a pointer to a null-terminated string. This function -c will be used by the FitsChan to obtain input FITS header -c cards. On each invocation, it should read the next input card -c from some external source (such as a FITS file), and return a -c pointer to the (null-terminated) contents of the card. It -c should return a NULL pointer when there are no more cards to -c be read. -c -c If "source" is NULL, the FitsChan will remain empty until -c cards are explicitly stored in it (e.g. using astPutCards, -c astPutFits or via the SourceFile attribute). -f A source routine, which is a function taking two arguments: a -f character argument of length 80 to contain a FITS card, and an -f integer error status argument. It should return an integer value. -f This function will be used by the FitsChan to obtain input -f FITS header cards. On each invocation, it should read the -f next input card from some external source (such as a FITS -f file), and return the contents of the card via its character -f argument. It should return a function result of one unless -f there are no more cards to be read, in which case it should -f return zero. If an error occurs, it should set its error -f status argument to an error value before returning. -f -f If the null routine AST_NULL is supplied as the SOURCE value, -f the FitsChan will remain empty until cards are explicitly -f stored in it (e.g. using AST_PUTCARDS, AST_PUTFITS or via the -f SourceFile attribute). -c sink -f SINK = SUBROUTINE (Given) -c Pointer to a sink function that takes a pointer to a -c null-terminated string as an argument and returns void. If -c no value has been set for the SinkFile attribute, this -c function will be used by the FitsChan to deliver any FITS -c header cards it contains when it is finally deleted. On -c each invocation, it should deliver the contents of the character -c string passed to it as a FITS header card to some external -c data store (such as a FITS file). -f A sink routine, which is a subroutine which takes two -f arguments: a character argument of length 80 to contain a -f FITS card, and an integer error status argument. If no -f value has been set for the SinkFile attribute, this routine -f will be used by the FitsChan to deliver any FITS header cards -f it contains when it is finally deleted. On each invocation, -f it should deliver the contents of the character string passed -f to it as a FITS header card to some external data store (such -f as a FITS file). If an error occurs, it should set its error -f status argument to an error value before returning. -* -c If "sink" is NULL, -f If the null routine AST_NULL is supplied as the SINK value, -* and no value has been set for the SinkFile attribute, the -* contents of the FitsChan will be lost when it is deleted. -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 FitsChan. 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. -f A character string containing an optional comma-separated -f list of attribute assignments to be used for initialising the -f new FitsChan. The syntax used is identical to that for the -f AST_SET routine. -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). -* -* Note, the FITSCHAN_OPTIONS environment variable may be used -* to specify default options for all newly created FitsChans. -f STATUS = INTEGER (Given and Returned) -f The global status. - -* Returned Value: -c astFitsChan() -f AST_FITSCHAN = INTEGER -* A pointer to the new FitsChan. - -* Notes: -f - The names of the routines supplied for the SOURCE and SINK -f arguments should appear in EXTERNAL statements in the Fortran -f routine which invokes AST_FITSCHAN. However, this is not generally -f necessary for the null routine AST_NULL (so long as the AST_PAR -f include file has been used). -c - No FITS "END" card will be written via the sink function. You -f - No FITS "END" card will be written via the sink routine. You -* should add this card yourself after the FitsChan has been -* deleted. -* - A null Object pointer (AST__NULL) will be returned if this -* function is invoked with the AST error status set, or if it -* should fail for any reason. -f - Note that the null routine AST_NULL (one underscore) is -f different to AST__NULL (two underscores), which is the null Object -f pointer. - -* Status Handling: -* The protected interface to this function includes an extra -* parameter at the end of the parameter list descirbed above. This -* parameter is a pointer to the integer inherited status -* variable: "int *status". -*-- -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Pointer to thread-specific global data */ - AstFitsChan *new; /* Pointer to new FitsChan */ - 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 FitsChan, allocating memory and initialising the - virtual function table as well if necessary. This interface is for - use by other C functions within AST, and uses the standard "wrapper" - functions included in this class. */ - new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init, - &class_vtab, "FitsChan", source, SourceWrap, - sink, SinkWrap ); - -/* If successful, note that the virtual function table has been - initialised. */ - if ( astOK ) { - class_init = 1; - -/* Apply any default options specified by "<class>_OPTIONS" environment - variable. */ - astEnvSet( new ); - -/* Obtain the variable argument list and pass it along with the - options string to the astVSet method to initialise the new - FitsChan'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 FitsChan. */ - return new; -} - -AstFitsChan *astFitsChanId_( const char *(* source)( void ), - void (* sink)( const char * ), - const char *options, ... ) { - -/* -* Name: -* astFitsChanId_ - -* Purpose: -* Create a FitsChan. - -* Type: -* Private function. - -* Synopsis: -* #include "fitschan.h" -* AstFitsChan *astFitsChanId_( const char *(* source)( void ), -* void (* sink)( const char * ), -* const char *options, ... ) - -* Class Membership: -* FitsChan constructor. - -* Description: -* This function implements the external (public) C interface to the -* astFitsChan constructor function. Another function (astFitsChanForId) -* should be called to create a FitsChan for use within other languages. -* Both functions return an ID value (instead of a true C pointer) to -* external users, and must be provided because astFitsChan_ 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 astFitsChan_ 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 astFitsChan_. - -* Returned Value: -* The ID value associated with the new FitsChan. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Pointer to thread-specific global data */ - AstFitsChan *new; /* Pointer to new FitsChan */ - 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 FitsChan, allocating memory and initialising the - virtual function table as well if necessary. This interface is for - use by external C functions and uses the standard "wrapper" - functions included in this class. */ - new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init, - &class_vtab, "FitsChan", source, SourceWrap, - sink, SinkWrap ); - -/* If successful, note that the virtual function table has been - initialised. */ - if ( astOK ) { - class_init = 1; - -/* Apply any default options specified by "<class>_OPTIONS" environment - variable. */ - astEnvSet( new ); - -/* Obtain the variable argument list and pass it along with the - options string to the astVSet method to initialise the new - FitsChan'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 FitsChan. */ - return astMakeId( new ); -} - -AstFitsChan *astFitsChanForId_( const char *(* source)( void ), - char *(* source_wrap)( const char *(*)( void ), int * ), - void (* sink)( const char * ), - void (* sink_wrap)( void (*)( const char * ), - const char *, int * ), - const char *options, ... ) { - -/* -*+ -* Name: -* astFitsChanFor - -* Purpose: -* Initialise a FitsChan from a foreign language interface. - -* Type: -* Public function. - -* Synopsis: -* #include "fitschan.h" -* AstFitsChan *astFitsChanFor( const char *(* source)( void ), -* char *(* source_wrap)( const char *(*) -* ( void ), int * ), -* void (* sink)( const char * ), -* void (* sink_wrap)( void (*)( const char * ), -* const char *, int * ), -* const char *options, ... ) - -* Class Membership: -* FitsChan constructor. - -* Description: -* This function creates a new FitsChan from a foreign language -* interface and optionally initialises its attributes. -* -* A FitsChan implements FITS input/output for the AST library. -* Writing an Object to a FitsChan (using astWrite) will generate a -* textual representation of that Object in terms of FITS header cards, -* and reading from a FitsChan (using astRead) will create a new Object -* from its FITS representation. -* -* Normally, when you use a FitsChan, you should provide "source" -* and "sink" functions which connect it to an external data store -* by reading and writing the resulting text. This function also -* requires you to provide "wrapper" functions which will invoke -* the source and sink functions. - -* Parameters: -* source -* Pointer to a "source" function which will be used to obtain -* FITS header cards. Generally, this will be obtained by -* casting a pointer to a source function which is compatible -* with the "source_wrap" wrapper function (below). The pointer -* should later be cast back to its original type by the -* "source_wrap" function before the function is invoked. -* -* If "source" is NULL, the FitsChan will remain empty until -* cards are added explicitly (e.g. using astPutCards or astPutFits). -* source_wrap -* Pointer to a function which can be used to invoke the -* "source" function supplied (above). This wrapper function is -* necessary in order to hide variations in the nature of the -* source function, such as may arise when it is supplied by a -* foreign (non-C) language interface. -* -* The single parameter of the "source_wrap" function is a -* pointer to the "source" function, and it should cast this -* function pointer (as necessary) and invoke the function with -* appropriate arguments to obtain the next FITS header card. -* The "source_wrap" function should then return a pointer -* to a dynamically allocated, null terminated string containing -* the text that was read. The string will be freed (using -* astFree) when no longer required and the "source_wrap" -* function need not concern itself with this. A NULL pointer -* should be returned if there is no more input to read. -* -* If "source" is NULL, the FitsChan will remain empty until -* cards are added explicitly (e.g. using astPutCards or astPutFits). -* sink -* Pointer to a "sink" function which will be used to deliver -* FITS header cards. Generally, this will be obtained by -* casting a pointer to a sink function which is compatible with -* the "sink_wrap" wrapper function (below). The pointer should -* later be cast back to its original type by the "sink_wrap" -* function before the function is invoked. -* -* If "sink" is NULL, the contents of the FitsChan will not be -* written out before being deleted. -* sink_wrap -* Pointer to a function which can be used to invoke the "sink" -* function supplied (above). This wrapper function is necessary -* in order to hide variations in the nature of the sink -* function, such as may arise when it is supplied by a foreign -* (non-C) language interface. -* -* The first parameter of the "sink_wrap" function is a pointer -* to the "sink" function, and the second parameter is a pointer -* to a const, null-terminated character string containing the -* text to be written. The "sink_wrap" function should cast the -* "sink" function pointer (as necessary) and invoke the -* function with appropriate arguments to deliver the line of -* output text. The "sink_wrap" function then returns void. -* -* If "sink_wrap" is NULL, the contents of the FitsChan will not be -* written out before being deleted. -* options -* Pointer to a null-terminated string containing an optional -* comma-separated list of attribute assignments to be used for -* initialising the new FitsChan. The syntax used is identical to -* that for the astSet function and may include "printf" format -* specifiers identified by "%" symbols in the normal way. -* ... -* If the "options" string contains "%" format specifiers, then -* an optional list of additional arguments may follow it in -* order to supply values to be substituted for these -* specifiers. The rules for supplying these are identical to -* those for the astSet function (and for the C "printf" -* function). - -* Returned Value: -* astFitsChanFor() -* A pointer to the new FitsChan. - -* Notes: -* - A null Object pointer (AST__NULL) will be returned if this -* function is invoked with the global error status set, or if it -* should fail for any reason. -* - This function is only available through the public interface -* to the FitsChan class (not the protected interface) and is -* intended solely for use in implementing foreign language -* interfaces to this class. -*- - -* Implememtation Notes: -* - This function behaves exactly like astFitsChanId_, in that it -* returns ID values and not true C pointers, but it has two -* additional arguments. These are pointers to the "wrapper -* functions" which are needed to accommodate foreign language -* interfaces. -*/ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Pointer to thread-specific global data */ - AstFitsChan *new; /* Pointer to new FitsChan */ - va_list args; /* Variable argument list */ - int *status; /* Pointer to inherited status value */ - -/* Get a pointer to the inherited status value. */ - status = astGetStatusPtr; - -/* Check the global status. */ - if ( !astOK ) return NULL; - -/* Get a pointer to the thread specific global data structure. */ - astGET_GLOBALS(NULL); - -/* Initialise the FitsChan, allocating memory and initialising the - virtual function table as well if necessary. */ - new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init, - &class_vtab, "FitsChan", source, source_wrap, - sink, sink_wrap ); - -/* If successful, note that the virtual function table has been - initialised. */ - if ( astOK ) { - class_init = 1; - -/* Apply any default options specified by "<class>_OPTIONS" environment - variable. */ - astEnvSet( new ); - -/* Obtain the variable argument list and pass it along with the - options string to the astVSet method to initialise the new - FitsChan'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 FitsChan. */ - return astMakeId( new ); -} - -AstFitsChan *astInitFitsChan_( void *mem, size_t size, int init, - AstFitsChanVtab *vtab, const char *name, - const char *(* source)( void ), - char *(* source_wrap)( const char *(*)( void ), int * ), - void (* sink)( const char * ), - void (* sink_wrap)( void (*)( const char * ), - const char *, int * ), int *status ) { - -/* -*+ -* Name: -* astInitFitsChan - -* Purpose: -* Initialise a FitsChan. - -* Type: -* Protected function. - -* Synopsis: -* #include "fitschan.h" -* AstFitsChan *astInitFitsChan_( void *mem, size_t size, int init, -* AstFitsChanVtab *vtab, const char *name, -* const char *(* source)( void ), -* char *(* source_wrap)( const char *(*)( void ), int * ), -* void (* sink)( const char * ), -* void (* sink_wrap)( void (*)( const char * ), -* const char *, int * ) ) - -* Class Membership: -* FitsChan initialiser. - -* Description: -* This function is provided for use by class implementations to -* initialise a new FitsChan object. It allocates memory (if -* necessary) to accommodate the FitsChan plus any additional data -* associated with the derived class. It then initialises a -* FitsChan 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 FitsChan at the start of the memory passed -* via the "vtab" parameter. - -* Parameters: -* mem -* A pointer to the memory in which the FitsChan is to be -* initialised. This must be of sufficient size to accommodate -* the FitsChan data (sizeof(FitsChan)) 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 FitsChan (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 FitsChan structure, so a valid value must be -* supplied even if not required for allocating memory. -* init -* A boolean flag indicating if the FitsChan'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 FitsChan. -* 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). -* source -* Pointer to a "source" function which will be used to obtain -* FITS header cards. Generally, this will be obtained by -* casting a pointer to a source function which is compatible -* with the "source_wrap" wrapper function (below). The pointer -* should later be cast back to its original type by the -* "source_wrap" function before the function is invoked. -* -* If "source" is NULL, the FitsChan will remain empty until -* cards are added explicitly (e.g. using astPutCards or astPutFits). -* source_wrap -* Pointer to a function which can be used to invoke the -* "source" function supplied (above). This wrapper function is -* necessary in order to hide variations in the nature of the -* source function, such as may arise when it is supplied by a -* foreign (non-C) language interface. -* -* The single parameter of the "source_wrap" function is a -* pointer to the "source" function, and it should cast this -* function pointer (as necessary) and invoke the function with -* appropriate arguments to obtain the next FITS header card. -* The "source_wrap" function should then return a pointer -* to a dynamically allocated, null terminated string containing -* the text that was read. The string will be freed (using -* astFree) when no longer required and the "source_wrap" -* function need not concern itself with this. A NULL pointer -* should be returned if there is no more input to read. -* -* If "source" is NULL, the FitsChan will remain empty until -* cards are added explicitly (e.g. using astPutCards or astPutFits). -* sink -* Pointer to a "sink" function which will be used to deliver -* FITS header cards. Generally, this will be obtained by -* casting a pointer to a sink function which is compatible with -* the "sink_wrap" wrapper function (below). The pointer should -* later be cast back to its original type by the "sink_wrap" -* function before the function is invoked. -* -* If "sink" is NULL, the contents of the FitsChan will not be -* written out before being deleted. -* sink_wrap -* Pointer to a function which can be used to invoke the "sink" -* function supplied (above). This wrapper function is necessary -* in order to hide variations in the nature of the sink -* function, such as may arise when it is supplied by a foreign -* (non-C) language interface. -* -* The first parameter of the "sink_wrap" function is a pointer -* to the "sink" function, and the second parameter is a pointer -* to a const, null-terminated character string containing the -* text to be written. The "sink_wrap" function should cast the -* "sink" function pointer (as necessary) and invoke the -* function with appropriate arguments to deliver the line of -* output text. The "sink_wrap" function then returns void. -* -* If "sink_wrap" is NULL, the contents of the FitsChan will not be -* written out before being deleted. - -* Returned Value: -* A pointer to the new FitsChan. - -* 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: */ - AstFitsChan *new; /* Pointer to new FitsChan */ - -/* Check the global status. */ - if ( !astOK ) return NULL; - -/* If necessary, initialise the virtual function table. */ - if ( init ) astInitFitsChanVtab( vtab, name ); - -/* Initialise a Channel structure (the parent class) as the first - component within the FitsChan structure, allocating memory if - necessary. I am not sure why FitsChan has its own source_wrap and - sink_wrap items, rather than just using those inherited from Channel. - It may be possible to do away with the fitschan wrappers and just use - the channel wrapper, but I have not yet tried this. Old mail from RFWS - suggests that it may be because the F77 FitsChan source and sink - interfaces handle fixed length strings (80 characters), whereas - Channel sournce and sink handle variable length strings. This needs - investigating. */ - new = (AstFitsChan *) astInitChannel( mem, size, 0, - (AstChannelVtab *) vtab, name, - NULL, NULL, NULL, NULL ); - if ( astOK ) { - -/* Initialise the FitsChan data. */ -/* ---------------------------- */ - new->head = NULL; - new->card = NULL; - new->keyseq = NULL; - new->keywords = NULL; - new->defb1950 = -1; - new->tabok = -INT_MAX; - new->cdmatrix = -1; - new->carlin = -1; - new->sipreplace = -1; - new->fitstol = -1.0; - new->polytan = -INT_MAX; - new->sipok = -INT_MAX; - new->iwc = -1; - new->clean = -1; - new->fitsdigits = AST__DBL_DIG; - new->fitsaxisorder = NULL; - new->encoding = UNKNOWN_ENCODING; - new->warnings = NULL; - new->tables = NULL; - -/* Save the pointers to the source and sink functions and the wrapper - functions that invoke them. */ - new->source = source; - new->saved_source = NULL; - new->source_wrap = source_wrap; - new->sink = sink; - new->sink_wrap = sink_wrap; - new->tabsource = NULL; - new->tabsource_wrap = NULL; - -/* Rewind the FitsChan so that the next read operation will return the - first card. */ - new->card = new->head; - -/* 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; -} -AstFitsChan *astLoadFitsChan_( void *mem, size_t size, - AstFitsChanVtab *vtab, const char *name, - AstChannel *channel, int *status ) { - -/* -*+ -* Name: -* astLoadFitsChan - -* Purpose: -* Load a FitsChan. - -* Type: -* Protected function. - -* Synopsis: -* #include "fitschan.h" -* AstFitsChan *astLoadFitsChan( void *mem, size_t size, -* AstFitsChanVtab *vtab, const char *name, -* AstChannel *channel ) - -* Class Membership: -* FitsChan loader. - -* Description: -* This function is provided to load a new FitsChan 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 -* FitsChan 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 FitsChan at the start of the memory -* passed via the "vtab" parameter. - -* Parameters: -* mem -* A pointer to the memory into which the FitsChan is to be -* loaded. This must be of sufficient size to accommodate the -* FitsChan data (sizeof(FitsChan)) 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 FitsChan (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 FitsChan 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(AstFitsChan) is used instead. -* vtab -* Pointer to the start of the virtual function table to be -* associated with the new FitsChan. If this is NULL, a pointer -* to the (static) virtual function table for the FitsChan 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 "FitsChan" is used instead. - -* Returned Value: -* A pointer to the new FitsChan. - -* 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. -*- -*/ -#define KEY_LEN 50 /* Maximum length of a keyword */ - -/* Local Variables: */ - astDECLARE_GLOBALS /* Pointer to thread-specific global data */ - AstFitsChan *new; /* Pointer to the new FitsChan */ - char *comment; /* Pointer to keyword comment */ - char *keynm; /* Keyword name */ - char *text; /* Textual version of integer value */ - char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ - double dval[2]; /* Double precision data values */ - int flags; /* Keyword flags */ - int free_data; /* Should data memory be freed? */ - int ival[2]; /* Integer data values */ - int ncard; /* No. of FitsCards read so far */ - int type; /* Keyword type */ - void *data; /* Pointer to keyword data value */ - -/* Initialise. */ - new = NULL; - -/* Check the global error status. */ - if ( !astOK ) return new; - -/* Get a pointer to the thread specific global data structure. */ - astGET_GLOBALS(channel); - -/* If a NULL virtual function table has been supplied, then this is - the first loader to be invoked for this FitsChan. In this case the - FitsChan belongs to this class, so supply appropriate values to be - passed to the parent class loader (and its parent, etc.). */ - if ( !vtab ) { - size = sizeof( AstFitsChan ); - vtab = &class_vtab; - name = "FitsChan"; - -/* If required, initialise the virtual function table for this class. */ - if ( !class_init ) { - astInitFitsChanVtab( 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 FitsChan. */ - new = astLoadChannel( mem, size, (AstChannelVtab *) 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, "FitsChan" ); - -/* Initialise the KeyMap holding the keywords in the FitsChan. */ - new->keywords = NULL; - -/* Initialise the list of keyword sequence numbers. */ - new->keyseq = NULL; - -/* Set the pointers to the source and sink functions, and their - wrapper functions, to NULL (we cannot restore these since they - refer to process-specific addresses). */ - new->source = NULL; - new->saved_source = NULL; - new->source_wrap = NULL; - new->sink = NULL; - new->sink_wrap = NULL; - new->tabsource = NULL; - new->tabsource_wrap = NULL; - -/* Now read each individual data item from this list and use it to - initialise the appropriate instance variable(s) for this class. */ - -/* Encoding. */ -/* --------- */ - text = astReadString( channel, "encod", UNKNOWN_STRING ); - if( text && strcmp( text, UNKNOWN_STRING ) ) { - new->encoding = FindString( MAX_ENCODING + 1, xencod, text, - "the FitsChan component 'Encod'", - "astRead", astGetClass( channel ), status ); - } else { - new->encoding = UNKNOWN_ENCODING; - } - if ( TestEncoding( new, status ) ) SetEncoding( new, new->encoding, status ); - text = astFree( text ); - -/* FitsAxisOrder. */ -/* -------------- */ - new->fitsaxisorder = astReadString( channel, "faxord", NULL ); - -/* FitsDigits. */ -/* ----------- */ - new->fitsdigits = astReadInt( channel, "fitsdg", AST__DBL_DIG ); - if ( TestFitsDigits( new, status ) ) SetFitsDigits( new, new->fitsdigits, status ); - -/* DefB1950 */ -/* -------- */ - new->defb1950 = astReadInt( channel, "dfb1950", -1 ); - if ( TestDefB1950( new, status ) ) SetDefB1950( new, new->defb1950, status ); - -/* TabOK */ -/* ----- */ - new->tabok = astReadInt( channel, "tabok", -INT_MAX ); - if ( TestTabOK( new, status ) ) SetTabOK( new, new->tabok, status ); - -/* CDMatrix */ -/* -------- */ - new->cdmatrix = astReadInt( channel, "cdmat", -1 ); - if ( TestCDMatrix( new, status ) ) SetCDMatrix( new, new->cdmatrix, status ); - -/* CarLin */ -/* ------ */ - new->carlin = astReadInt( channel, "carlin", -1 ); - if ( TestCarLin( new, status ) ) SetCarLin( new, new->carlin, status ); - -/* SipReplace */ -/* ---------- */ - new->sipreplace = astReadInt( channel, "sipreplace", -1 ); - if ( TestSipReplace( new, status ) ) SetSipReplace( new, new->sipreplace, status ); - -/* FitsTol */ -/* ------- */ - new->fitstol = astReadDouble( channel, "fitstol", -1.0 ); - if ( TestFitsTol( new, status ) ) SetFitsTol( new, new->fitstol, status ); - -/* PolyTan */ -/* ------- */ - new->polytan = astReadInt( channel, "polytan", -1 ); - if ( TestPolyTan( new, status ) ) SetPolyTan( new, new->polytan, status ); - -/* SipOK */ -/* ----- */ - new->sipok = astReadInt( channel, "sipok", -1 ); - if ( TestSipOK( new, status ) ) SetSipOK( new, new->sipok, status ); - -/* Iwc */ -/* --- */ - new->iwc = astReadInt( channel, "iwc", -1 ); - if ( TestIwc( new, status ) ) SetIwc( new, new->iwc, status ); - -/* Clean */ -/* ----- */ - new->clean = astReadInt( channel, "clean", -1 ); - if ( TestClean( new, status ) ) SetClean( new, new->clean, status ); - -/* Warnings. */ -/* --------- */ - new->warnings = astReadString( channel, "warn", NULL ); - -/* Card. */ -/* ----- */ - -/* Initialise the index of the card to be read next. */ - ncard = 1; - new->card = NULL; - new->head = NULL; - -/* Load each card. */ - type = AST__NOTYPE + 1; - while( type != AST__NOTYPE && astOK ){ - -/* Get the keyword type. */ - (void) sprintf( buff, "ty%d", ncard ); - text = astReadString( channel, buff, " " ); - if( strcmp( text, " " ) ) { - type = FindString( 9, type_names, text, - "a FitsChan keyword data type", - "astRead", astGetClass( channel ), status ); - } else { - type = AST__NOTYPE; - } - text = astFree( text ); - -/* Only proceed if the keyword type was found. */ - if( type != AST__NOTYPE ){ - -/* Get the keyword name. Use a default blank name. */ - (void) sprintf( buff, "nm%d", ncard ); - keynm = astReadString( channel, buff, " " ); - -/* Get the data value, using the appropriate data type, unless the - keyword is a comment keyword or is undefined. */ - free_data = 0; - if( type == AST__FLOAT ){ - (void) sprintf( buff, "dt%d", ncard ); - dval[ 0 ] = astReadDouble( channel, buff, AST__BAD ); - data = (void *) dval; - } else if( type == AST__STRING || type == AST__CONTINUE ){ - (void) sprintf( buff, "dt%d", ncard ); - data = (void *) astReadString( channel, buff, "" ); - free_data = 1; - } else if( type == AST__INT ){ - (void) sprintf( buff, "dt%d", ncard ); - ival[ 0 ] = astReadInt( channel, buff, 0 ); - data = (void *) ival; - } else if( type == AST__LOGICAL ){ - (void) sprintf( buff, "dt%d", ncard ); - ival[ 0 ] = astReadInt( channel, buff, 0 ); - data = (void *) ival; - } else if( type == AST__COMPLEXF ){ - (void) sprintf( buff, "dr%d", ncard ); - dval[ 0 ] = astReadDouble( channel, buff, AST__BAD ); - (void) sprintf( buff, "di%d", ncard ); - dval[ 1 ] = astReadDouble( channel, buff, AST__BAD ); - data = (void *) dval; - } else if( type == AST__COMPLEXI ){ - (void) sprintf( buff, "dr%d", ncard ); - ival[ 0 ] = astReadInt( channel, buff, 0 ); - (void) sprintf( buff, "di%d", ncard ); - ival[ 1 ] = astReadInt( channel, buff, 0 ); - data = (void *) ival; - } else { - data = NULL; - } - -/* Get the keyword flags (only written by versions of AST later than - V1.4). These are packed into an int. */ - (void) sprintf( buff, "fl%d", ncard ); - flags = astReadInt( channel, buff, 0 ); - -/* If the flags were not found, use the keyword deletion flag written by - AST V1.4 and earlier. */ - if( !flags ) { - (void) sprintf( buff, "dl%d", ncard ); - flags = astReadInt( channel, buff, 0 ); - } - -/* Get the keyword comment. */ - (void) sprintf( buff, "cm%d", ncard ); - comment = astReadString( channel, buff, NULL ); - -/* Append a new card to the output FitsChan. */ - NewCard( new, keynm, type, data, comment, flags, status ); - -/* Free the character strings, and data (if required). */ - comment = (char *) astFree( (void *) comment ); - keynm = (char *) astFree( (void *) keynm ); - if( free_data ) data = astFree( data ); - } - -/* Move on to the next card. */ - ncard++; - } - -/* Set up the current card index. */ - astSetCard( new, astReadInt( channel, "card", 0 ) ); - -/* Load any FitTables. */ - new->tables = astReadObject( channel, "tables", NULL ); - } - -/* If an error occurred, clean up by deleting the new FitsChan. */ - if ( !astOK ) new = astDelete( new ); - -/* Return the new FitsChan pointer. */ - return new; -} - -/* 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 astWriteFits_( AstFitsChan *this, int *status ){ - if( !this ) return; - (**astMEMBER(this,FitsChan,WriteFits))(this, status ); -} - -void astReadFits_( AstFitsChan *this, int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,ReadFits))(this, status ); -} - -void astEmptyFits_( AstFitsChan *this, int *status ){ - if( !this ) return; - (**astMEMBER(this,FitsChan,EmptyFits))(this, status ); -} - -void astShowFits_( AstFitsChan *this, int *status ){ - if( !this ) return; - (**astMEMBER(this,FitsChan,ShowFits))(this, status ); -} - -void astPutCards_( AstFitsChan *this, const char *cards, int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,PutCards))(this,cards, status ); -} - -void astPutFits_( AstFitsChan *this, const char *card, int overwrite, int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,PutFits))(this,card,overwrite, status ); -} - -void astDelFits_( AstFitsChan *this, int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,DelFits))(this, status ); -} - -void astPurgeWCS_( AstFitsChan *this, int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,PurgeWCS))(this, status ); -} - -AstKeyMap *astGetTables_( AstFitsChan *this, int *status ){ - if( !astOK ) return NULL; - return (**astMEMBER(this,FitsChan,GetTables))(this, status ); -} - -void astPutTables_( AstFitsChan *this, AstKeyMap *tables, int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,PutTables))(this, tables, status ); -} - -void astPutTable_( AstFitsChan *this, AstFitsTable *table, const char *extnam, - int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,PutTable))(this, table, extnam, status ); -} - -void astRemoveTables_( AstFitsChan *this, const char *key, int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,RemoveTables))(this, key, status ); -} - -void astRetainFits_( AstFitsChan *this, int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,RetainFits))(this, status ); -} - -int astFitsEof_( AstFitsChan *this, int *status ){ - if( !this ) return 1; - return (**astMEMBER(this,FitsChan,FitsEof))( this, status ); -} - -void astSetFitsCom_( AstFitsChan *this, const char *name, - const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsCom))( this, name, comment, overwrite, status ); -} - -void astSetFitsI_( AstFitsChan *this, const char *name, int value, - const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsI))( this, name, value, comment, overwrite, status ); -} - -void astSetFitsF_( AstFitsChan *this, const char *name, double value, - const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsF))( this, name, value, comment, overwrite, status ); -} - -void astSetFitsS_( AstFitsChan *this, const char *name, const char *value, - const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsS))( this, name, value, comment, overwrite, status ); -} - -void astSetFitsCN_( AstFitsChan *this, const char *name, const char *value, - const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsCN))( this, name, value, comment, overwrite, status ); -} - -void astSetFitsCF_( AstFitsChan *this, const char *name, double *value, - const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsCF))( this, name, value, comment, overwrite, status ); -} - -void astSetFitsCI_( AstFitsChan *this, const char *name, int *value, - const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsCI))( this, name, value, comment, overwrite, status ); -} - -void astSetFitsL_( AstFitsChan *this, const char *name, int value, - const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsL))( this, name, value, comment, overwrite, status ); -} - -void astSetFitsU_( AstFitsChan *this, const char *name, const char *comment, - int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsU))( this, name, comment, overwrite, status ); -} - -void astSetFitsCM_( AstFitsChan *this, const char *comment, int overwrite, int *status ) { - if ( !astOK ) return; - (**astMEMBER(this,FitsChan,SetFitsCM))( this, comment, overwrite, status ); -} - -void astClearCard_( AstFitsChan *this, int *status ){ - if( !this ) return; - (**astMEMBER(this,FitsChan,ClearCard))( this, status ); -} - -void astSetCard_( AstFitsChan *this, int card, int *status ){ - if( !this ) return; - (**astMEMBER(this,FitsChan,SetCard))( this, card, status ); -} - -int astTestCard_( AstFitsChan *this, int *status ){ - if( !this ) return 0; - return (**astMEMBER(this,FitsChan,TestCard))( this, status ); -} - -int astGetCard_( AstFitsChan *this, int *status ){ - if( !this ) return 0; - return (**astMEMBER(this,FitsChan,GetCard))( this, status ); -} - -int astGetNcard_( AstFitsChan *this, int *status ){ - if( !this ) return 0; - return (**astMEMBER(this,FitsChan,GetNcard))( this, status ); -} - -int astGetCardType_( AstFitsChan *this, int *status ){ - if( !this ) return AST__NOTYPE; - return (**astMEMBER(this,FitsChan,GetCardType))( this, status ); -} - -const char *astGetCardComm_( AstFitsChan *this, int *status ){ - if( !this ) return NULL; - return (**astMEMBER(this,FitsChan,GetCardComm))( this, status ); -} - -const char *astGetCardName_( AstFitsChan *this, int *status ){ - if( !this ) return NULL; - return (**astMEMBER(this,FitsChan,GetCardName))( this, status ); -} - -int astGetNkey_( AstFitsChan *this, int *status ){ - if( !this ) return 0; - return (**astMEMBER(this,FitsChan,GetNkey))( this, status ); -} - -int astGetClean_( AstFitsChan *this, int *status ){ - if( !this ) return 0; - return (**astMEMBER(this,FitsChan,GetClean))( this, status ); -} - -const char *astGetAllWarnings_( AstFitsChan *this, int *status ){ - if( !this ) return NULL; - return (**astMEMBER(this,FitsChan,GetAllWarnings))( this, status ); -} - -int astGetFitsCF_( AstFitsChan *this, const char *name, double *value, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,GetFitsCF))( this, name, value, status ); -} - -int astGetFitsCI_( AstFitsChan *this, const char *name, int *value, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,GetFitsCI))( this, name, value, status ); -} - -int astGetFitsF_( AstFitsChan *this, const char *name, double *value, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,GetFitsF))( this, name, value, status ); -} - -int astGetFitsI_( AstFitsChan *this, const char *name, int *value, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,GetFitsI))( this, name, value, status ); -} - -int astGetFitsL_( AstFitsChan *this, const char *name, int *value, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,GetFitsL))( this, name, value, status ); -} - -int astTestFits_( AstFitsChan *this, const char *name, int *there, int *status ){ - if( there ) *there = 0; - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,TestFits))( this, name, there, status ); -} - -int astGetFitsS_( AstFitsChan *this, const char *name, char **value, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,GetFitsS))( this, name, value, status ); -} - -int astGetFitsCN_( AstFitsChan *this, const char *name, char **value, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,GetFitsCN))( this, name, value, status ); -} - -int astFitsGetCom_( AstFitsChan *this, const char *name, char **comment, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,FitsGetCom))( this, name, comment, status ); -} - -int astKeyFields_( AstFitsChan *this, const char *filter, int maxfld, - int *ubnd, int *lbnd, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,KeyFields))( this, filter, maxfld, - ubnd, lbnd, status ); -} - -int astFindFits_( AstFitsChan *this, const char *name, char *card, int inc, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,FindFits))( this, name, card, inc, status ); -} - -int astGetEncoding_( AstFitsChan *this, int *status ){ - if( !astOK ) return UNKNOWN_ENCODING; - return (**astMEMBER(this,FitsChan,GetEncoding))( this, status ); -} - -int astGetCDMatrix_( AstFitsChan *this, int *status ){ - if( !astOK ) return 0; - return (**astMEMBER(this,FitsChan,GetCDMatrix))( this, status ); -} -void astSetTableSource_( AstFitsChan *this, - void (*tabsource)( void ), - void (*tabsource_wrap)( void (*)( void ), - AstFitsChan *, const char *, - int, int, int * ), - int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,SetTableSource))( this, tabsource, - tabsource_wrap, status ); -} -void astTableSource_( AstFitsChan *this, - void (* tabsource)( AstFitsChan *, const char *, - int, int, int * ), - int *status ){ - if( !astOK ) return; - (**astMEMBER(this,FitsChan,TableSource))( this, tabsource, status ); -} - -/* - * A diagnostic function which lists the contents of a FitsChan to - * standard output. - */ - -/* -static void ListFC( AstFitsChan *, const char * ); - -static void ListFC( AstFitsChan *this, const char *ttl ) { - FitsCard *cardo; - char card[ 81 ]; - printf("%s\n----------------------------------------\n", ttl ); - cardo = (FitsCard *) this->card; - astClearCard( this ); - while( !astFitsEof( this ) && astOK ){ - FormatCard( this, card, "List" ); - if( this->card == cardo ) { - printf( "%s <<<<< currrent card <<<<< \n", card ); - } else { - printf( "%s\n", card ); - } - MoveCard( this, 1, "List", "FitsChan" ); - } - this->card = cardo; -} -*/ - - - - - - - - - - - - - - - |